mirror of
https://gitlab.com/pulsechaincom/erigon-pulse.git
synced 2024-12-23 12:07:17 +00:00
099fb7c627
Strings builders have a buffer (slice) that can be pre-allocated
98 lines
2.3 KiB
Go
98 lines
2.3 KiB
Go
package reports
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
|
|
"github.com/ledgerwatch/erigon/cmd/observer/database"
|
|
)
|
|
|
|
type ClientsEstimateReportEntry struct {
|
|
Name string
|
|
CountLow uint
|
|
CountHigh uint
|
|
}
|
|
|
|
type ClientsEstimateReport struct {
|
|
Clients []ClientsEstimateReportEntry
|
|
}
|
|
|
|
func CreateClientsEstimateReport(
|
|
ctx context.Context,
|
|
db database.DB,
|
|
limit uint,
|
|
maxPingTries uint,
|
|
networkID uint,
|
|
) (*ClientsEstimateReport, error) {
|
|
clientsReport, err := CreateClientsReport(ctx, db, limit, maxPingTries, networkID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
report := ClientsEstimateReport{}
|
|
|
|
for i, topClient := range clientsReport.Clients {
|
|
if uint(i) >= limit {
|
|
break
|
|
}
|
|
clientName := topClient.Name
|
|
|
|
sameNetworkCount, err := db.CountClients(ctx, clientName+"/", maxPingTries, networkID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if sameNetworkCount == 0 {
|
|
continue
|
|
}
|
|
|
|
knownNetworkCount, err := db.CountClientsWithNetworkID(ctx, clientName+"/", maxPingTries)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if knownNetworkCount == 0 {
|
|
continue
|
|
}
|
|
|
|
// 1 - (1 - p)/2 percentile for 95% confidence
|
|
const z = 1.96
|
|
intervalLow, intervalHigh := waldInterval(knownNetworkCount, sameNetworkCount, z)
|
|
|
|
transientErrCount, err := db.CountClientsWithHandshakeTransientError(ctx, clientName+"/", maxPingTries)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
countLow := sameNetworkCount + uint(math.Round(float64(transientErrCount)*intervalLow))
|
|
countHigh := sameNetworkCount + uint(math.Round(float64(transientErrCount)*intervalHigh))
|
|
|
|
client := ClientsEstimateReportEntry{
|
|
clientName,
|
|
countLow,
|
|
countHigh,
|
|
}
|
|
report.Clients = append(report.Clients, client)
|
|
}
|
|
|
|
return &report, nil
|
|
}
|
|
|
|
// https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval#Normal_approximation_interval_or_Wald_interval
|
|
func waldInterval(n uint, ns uint, z float64) (float64, float64) {
|
|
nf := n - ns
|
|
p := float64(ns) / float64(n)
|
|
interval := z * math.Sqrt(float64(ns*nf)) / (float64(n) * math.Sqrt(float64(n)))
|
|
return p - interval, p + interval
|
|
}
|
|
|
|
func (report *ClientsEstimateReport) String() string {
|
|
var builder strings.Builder
|
|
builder.Grow(2 * len(report.Clients))
|
|
for _, client := range report.Clients {
|
|
builder.WriteString(fmt.Sprintf("%6d - %-6d %s", client.CountLow, client.CountHigh, client.Name))
|
|
builder.WriteRune('\n')
|
|
}
|
|
return builder.String()
|
|
}
|