Skip to content

Commit

Permalink
fix(api): improve perf for /api/v1/app/dashboard
Browse files Browse the repository at this point in the history
  • Loading branch information
guybrush committed Oct 9, 2024
1 parent 2054c64 commit b88385e
Show file tree
Hide file tree
Showing 2 changed files with 167 additions and 64 deletions.
118 changes: 100 additions & 18 deletions handlers/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -849,6 +849,7 @@ Combined validator get, performance, attestation efficency, sync committee stati
Not public documented
*/
func ApiDashboard(w http.ResponseWriter, r *http.Request) {
start := time.Now()
w.Header().Set("Content-Type", "application/json")

j := json.NewEncoder(w)
Expand Down Expand Up @@ -886,6 +887,12 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
var syncCommitteeStats *SyncCommitteesInfo
var proposalLuckStats *types.ApiProposalLuckResponse

type timingEntry struct {
name string
duration time.Duration
}
timings := []timingEntry{}

if getValidators {
queryIndices, err := parseApiValidatorParamToIndices(parsedBody.IndicesOrPubKey, maxValidators)
if err != nil {
Expand All @@ -902,6 +909,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getGeneralValidatorInfoForAppDashboard(%v) took longer than 10 sec", queryIndices)
}
timings = append(timings, timingEntry{name: "getGeneralValidatorInfoForAppDashboard", duration: elapsed})
return err
})

Expand All @@ -913,6 +921,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getValidatorEffectiveness(%v, %v) took longer than 10 sec", epoch-1, queryIndices)
}
timings = append(timings, timingEntry{name: "getValidatorEffectiveness", duration: elapsed})
return err
})

Expand All @@ -924,6 +933,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getRocketpoolValidators(%v) took longer than 10 sec", queryIndices)
}
timings = append(timings, timingEntry{name: "getRocketpoolValidators", duration: elapsed})
return err
})

Expand All @@ -935,6 +945,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getValidatorExecutionPerformance(%v) took longer than 10 sec", queryIndices)
}
timings = append(timings, timingEntry{name: "getValidatorExecutionPerformance", duration: elapsed})
return err
})

Expand All @@ -947,6 +958,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getSyncCommitteeInfoForValidators(%v, %v) took longer than 10 sec", queryIndices, period)
}
timings = append(timings, timingEntry{name: "getSyncCommitteeInfoForValidators", duration: elapsed})
return err
})

Expand All @@ -960,6 +972,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
logger.Warnf("SyncPeriodOfEpoch(%v) + 1 took longer than 10 sec", epoch)
logger.Warnf("getSyncCommitteeInfoForValidators(%v, %v) took longer than 10 sec", queryIndices, period)
}
timings = append(timings, timingEntry{name: "getSyncCommitteeInfoForValidators+1", duration: elapsed})
return err
})

Expand All @@ -971,6 +984,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getSyncCommitteeStatistics(%v, %v) took longer than 10 sec", queryIndices, epoch)
}
timings = append(timings, timingEntry{name: "getSyncCommitteeStatistics", duration: elapsed})
return err
})

Expand All @@ -982,6 +996,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getProposalLuck(%v, %v) took longer than 10 sec", queryIndices, epoch)
}
timings = append(timings, timingEntry{name: "getProposalLuckStats", duration: elapsed})
return err
})
}
Expand All @@ -995,6 +1010,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getEpoch(%v) took longer than 10 sec", int64(epoch)-1)
}
timings = append(timings, timingEntry{name: "getEpoch-1", duration: elapsed})
return err
})

Expand All @@ -1006,6 +1022,7 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getEpoch(%v) took longer than 10 sec", int64(epoch)-10)
}
timings = append(timings, timingEntry{name: "getEpoch-10", duration: elapsed})
return err
})

Expand All @@ -1017,10 +1034,25 @@ func ApiDashboard(w http.ResponseWriter, r *http.Request) {
if elapsed > 10*time.Second {
logger.Warnf("getRocketpoolStats() took longer than 10 sec")
}
timings = append(timings, timingEntry{name: "getRocketpoolStats", duration: elapsed})
return err
})

err = g.Wait()

totalDuration := time.Since(start)
if totalDuration > 5*time.Second {
timings = append(timings, timingEntry{name: "total", duration: time.Since(start)})
sort.Slice(timings, func(i, j int) bool {
return timings[i].duration > timings[j].duration
})
str := ""
for _, t := range timings {
str += fmt.Sprintf("%40s: %v\n", t.name, t.duration)
}
logger.Warnf("timings for ApiDashboard: \n%s", str)
}

if err != nil {
utils.LogError(err, "dashboard", 0)
SendBadRequestResponse(w, r.URL.String(), err.Error())
Expand Down Expand Up @@ -1084,12 +1116,29 @@ func getSyncCommitteeStatistics(validators []uint64, epoch uint64) (*SyncCommitt
return &SyncCommitteesInfo{}, nil
}

expectedSlots, err := getExpectedSyncCommitteeSlots(validators, epoch)
if err != nil {
return nil, err
}
g := errgroup.Group{}

stats, err := getSyncCommitteeSlotsStatistics(validators, epoch)
var expectedSlots uint64
g.Go(func() error {
var err error
expectedSlots, err = getExpectedSyncCommitteeSlots(validators, epoch)
if err != nil {
return err
}
return nil
})

var stats types.SyncCommitteesStats
g.Go(func() error {
var err error
stats, err = getSyncCommitteeSlotsStatistics(validators, epoch)
if err != nil {
return err
}
return nil
})

err := g.Wait()
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -1397,8 +1446,20 @@ func getRocketpoolValidators(queryIndices []uint64) ([]interface{}, error) {
}

func getGeneralValidatorInfoForAppDashboard(queryIndices []uint64) ([]interface{}, error) {
// we use MAX(validatorindex)+1 instead of COUNT(*) for querying the rank_count for performance-reasons
rows, err := db.ReaderDb.Query(`
start := time.Now()
type timingEntry struct {
name string
duration time.Duration
}
timings := []timingEntry{}

g := new(errgroup.Group)

var data []interface{}
g.Go(func() error {
start := time.Now()
// we use MAX(validatorindex)+1 instead of COUNT(*) for querying the rank_count for performance-reasons
rows, err := db.ReaderDb.Query(`
WITH maxValidatorIndex AS (
SELECT MAX(validatorindex)+1 as total_count
FROM validator_performance
Expand Down Expand Up @@ -1427,53 +1488,61 @@ func getGeneralValidatorInfoForAppDashboard(queryIndices []uint64) ([]interface{
LEFT JOIN validator_names ON validator_names.publickey = validators.pubkey
WHERE validators.validatorindex = ANY($1)
ORDER BY validators.validatorindex`, pq.Array(queryIndices))
if err != nil {
return nil, fmt.Errorf("error querying validators: %w", err)
}
defer rows.Close()

data, err := utils.SqlRowsToJSON(rows)
if err != nil {
return nil, fmt.Errorf("error converting validators to json: %w", err)
}
if err != nil {
return fmt.Errorf("error querying validators: %w", err)
}
defer rows.Close()

g := new(errgroup.Group)
data, err = utils.SqlRowsToJSON(rows)
if err != nil {
return fmt.Errorf("error converting validators to json: %w", err)
}
timings = append(timings, timingEntry{name: "valis-query", duration: time.Since(start)})
return nil
})

var balances map[uint64][]*types.ValidatorBalance
g.Go(func() error {
start := time.Now()
var err error
balances, err = db.BigtableClient.GetValidatorBalanceHistory(queryIndices, services.LatestEpoch(), services.LatestEpoch())
if err != nil {
return fmt.Errorf("error in GetValidatorBalanceHistory: %w", err)
}
timings = append(timings, timingEntry{name: "GetValidatorBalanceHistory", duration: time.Since(start)})
return nil
})

var currentDayIncome map[uint64]int64
g.Go(func() error {
start := time.Now()
var err error
currentDayIncome, err = db.GetCurrentDayClIncome(queryIndices)
if err != nil {
return fmt.Errorf("error in GetCurrentDayClIncome: %w", err)
}
timings = append(timings, timingEntry{name: "GetCurrentDayClIncome", duration: time.Since(start)})
return nil
})

var lastAttestationSlots map[uint64]uint64
g.Go(func() error {
start := time.Now()
var err error
lastAttestationSlots, err = db.BigtableClient.GetLastAttestationSlots(queryIndices)
if err != nil {
return fmt.Errorf("error in GetLastAttestationSlots: %w", err)
}
timings = append(timings, timingEntry{name: "GetLastAttestationSlots", duration: time.Since(start)})
return nil
})

err = g.Wait()
err := g.Wait()
if err != nil {
return nil, fmt.Errorf("error in validator errgroup: %w", err)
}

aggregateStart := time.Now()
for _, entry := range data {
eMap, ok := entry.(map[string]interface{})
if !ok {
Expand Down Expand Up @@ -1505,6 +1574,19 @@ func getGeneralValidatorInfoForAppDashboard(queryIndices []uint64) ([]interface{
}
}
}
timings = append(timings, timingEntry{name: "aggregate", duration: time.Since(aggregateStart)})
if time.Since(start) > 10*time.Second {
timings = append(timings, timingEntry{name: "total", duration: time.Since(start)})

sort.Slice(timings, func(i, j int) bool {
return timings[i].duration > timings[j].duration
})
str := ""
for _, t := range timings {
str += fmt.Sprintf("%40s:\t%v\n", t.name, t.duration)
}
logger.Warnf("timings for getGeneralValidatorInfoForAppDashboard: \n%s", str)
}

return data, nil
}
Expand Down
Loading

0 comments on commit b88385e

Please sign in to comment.