diff --git a/handlers/validators.go b/handlers/validators.go index 0598374676..0cdfe3a93b 100644 --- a/handlers/validators.go +++ b/handlers/validators.go @@ -17,11 +17,6 @@ import ( "github.com/gobitfly/eth2-beaconchain-explorer/utils" ) -type states struct { - Name string `db:"statename"` - Count uint64 `db:"statecount"` -} - // Validators returns the validators using a go template func Validators(w http.ResponseWriter, r *http.Request) { templateFiles := append(layoutTemplateFiles, "validators.html") @@ -31,15 +26,7 @@ func Validators(w http.ResponseWriter, r *http.Request) { validatorsPageData := types.ValidatorsPageData{} - var currentStateCounts []*states - - qry := "SELECT status AS statename, COUNT(*) AS statecount FROM validators GROUP BY status" - err := db.ReaderDb.Select(¤tStateCounts, qry) - if err != nil { - utils.LogError(err, "error retrieving validators data", 0) - http.Error(w, "Internal server error", http.StatusInternalServerError) - return - } + currentStateCounts := services.LatestValidatorStateCounts() for _, state := range currentStateCounts { switch state.Name { diff --git a/services/monitoring.go b/services/monitoring.go index a8c3bde977..8ed3cabcb9 100644 --- a/services/monitoring.go +++ b/services/monitoring.go @@ -249,25 +249,26 @@ func startServicesMonitoringService() { firstRun := true servicesToCheck := map[string]time.Duration{ - "eth1indexer": time.Minute * 15, - "slotVizUpdater": time.Minute * 15, - "slotUpdater": time.Minute * 15, - "latestProposedSlotUpdater": time.Minute * 15, - "epochUpdater": time.Minute * 15, - "rewardsExporter": time.Minute * 15, - "mempoolUpdater": time.Minute * 15, - "indexPageDataUpdater": time.Minute * 15, - "latestBlockUpdater": time.Minute * 15, - "headBlockRootHashUpdater": time.Minute * 15, - "notification-collector": time.Minute * 15, - "relaysUpdater": time.Minute * 15, - "ethstoreExporter": time.Minute * 60, - "statsUpdater": time.Minute * 30, - "poolsUpdater": time.Minute * 30, - "slotExporter": time.Minute * 15, - "statistics": time.Minute * 90, - "ethStoreStatistics": time.Minute * 15, - "lastExportedStatisticDay": time.Minute * 15, + "eth1indexer": time.Minute * 15, + "slotVizUpdater": time.Minute * 15, + "slotUpdater": time.Minute * 15, + "latestProposedSlotUpdater": time.Minute * 15, + "epochUpdater": time.Minute * 15, + "rewardsExporter": time.Minute * 15, + "mempoolUpdater": time.Minute * 15, + "indexPageDataUpdater": time.Minute * 15, + "latestBlockUpdater": time.Minute * 15, + "headBlockRootHashUpdater": time.Minute * 15, + "notification-collector": time.Minute * 15, + "relaysUpdater": time.Minute * 15, + "ethstoreExporter": time.Minute * 60, + "statsUpdater": time.Minute * 30, + "poolsUpdater": time.Minute * 30, + "slotExporter": time.Minute * 15, + "statistics": time.Minute * 90, + "ethStoreStatistics": time.Minute * 15, + "lastExportedStatisticDay": time.Minute * 15, + "validatorStateCountsUpdater": time.Minute * 15, //"notification-sender", //exclude for now as the sender is only running on mainnet } diff --git a/services/services.go b/services/services.go index 9dc6ca9f09..85807892e1 100644 --- a/services/services.go +++ b/services/services.go @@ -87,6 +87,9 @@ func Init() { ready.Add(1) go latestExportedStatisticDayUpdater(ready) + ready.Add(1) + go validatorStateCountsUpdater(ready) + if utils.Config.RatelimitUpdater.Enabled { go ratelimit.DBUpdater() } @@ -1766,3 +1769,45 @@ func LatestExportedStatisticDay() (uint64, error) { } return wanted, nil } + +func validatorStateCountsUpdater(wg *sync.WaitGroup) { + firstRun := true + + for { + var currentStateCounts []types.ValidatorStateCountRow + qry := "SELECT status AS statename, COUNT(*) AS statecount FROM validators GROUP BY status" + err := db.ReaderDb.Select(¤tStateCounts, qry) + + if err != nil { + logger.Errorf("error retrieving validator state counts from the database: %v", err) + + if err.Error() == "sql: database is closed" { + logger.Fatalf("error retrieving validator state counts from the database: %v", err) + } + } else { + cacheKey := fmt.Sprintf("%d:frontend:validator_state_counts", utils.Config.Chain.ClConfig.DepositChainID) + err := cache.TieredCache.Set(cacheKey, currentStateCounts, utils.Day) + if err != nil { + logger.Errorf("error caching validator state counts: %v", err) + } + if firstRun { + logger.Info("initialized validator state counts updater") + wg.Done() + firstRun = false + } + } + ReportStatus("validatorStateCountsUpdater", "Running", nil) + time.Sleep(time.Minute) + } +} + +func LatestValidatorStateCounts() []types.ValidatorStateCountRow { + wanted := []types.ValidatorStateCountRow{} + cacheKey := fmt.Sprintf("%d:frontend:validator_state_counts", utils.Config.Chain.ClConfig.DepositChainID) + if wanted, err := cache.TieredCache.GetWithLocalTimeout(cacheKey, time.Minute, wanted); err == nil { + return wanted.([]types.ValidatorStateCountRow) + } else { + logger.Errorf("error retrieving validator state count data from cache: %v", err) + } + return wanted +} diff --git a/types/frontend.go b/types/frontend.go index 4e69b75854..88df7b7023 100644 --- a/types/frontend.go +++ b/types/frontend.go @@ -615,3 +615,8 @@ type SearchValidatorsByEth1Result []struct { ValidatorIndices pq.Int64Array `db:"validatorindices" json:"validator_indices"` Count uint64 `db:"count" json:"-"` } + +type ValidatorStateCountRow struct { + Name string `db:"statename"` + Count uint64 `db:"statecount"` +}