From 0b38f8a5abdfcce189635b60f6a96b5f68de50a5 Mon Sep 17 00:00:00 2001 From: Amine Afia Date: Tue, 1 Oct 2024 16:14:45 +0200 Subject: [PATCH] Add chain tracker to track chain head (#77) ### TL;DR Implemented a ChainTracker to monitor and report the latest block number in the current chain. ### What changed? - Added a new `ChainHead` metric in `metrics.go` to track the latest block number. - Created a new `chain_tracker.go` file with a `ChainTracker` struct and associated methods. - The `ChainTracker` polls the RPC at regular intervals to fetch the latest block number. - Updated the `Orchestrator` to start the `ChainTracker` alongside other components. ### How to test? 1. Run the indexer with the new changes. 2. Monitor the `chain_tracker_chain_head` metric using Prometheus or a similar monitoring tool. 3. Verify that the metric is updating at regular intervals (default: every 5 minutes). 4. Compare the reported chain head with the actual latest block number on the blockchain to ensure accuracy. ### Why make this change? This change allows for real-time tracking of the blockchain's progress, providing valuable information about the current state of the chain. This can be useful for: 1. Monitoring the health of the blockchain network. 2. Detecting potential network issues or delays. 3. Providing context for other indexer operations and metrics. 4. Enabling more accurate synchronization and data consistency checks. --- internal/metrics/metrics.go | 8 ++++ internal/orchestrator/chain_tracker.go | 53 ++++++++++++++++++++++++++ internal/orchestrator/orchestrator.go | 8 ++++ 3 files changed, 69 insertions(+) create mode 100644 internal/orchestrator/chain_tracker.go diff --git a/internal/metrics/metrics.go b/internal/metrics/metrics.go index 4625c24..953b828 100644 --- a/internal/metrics/metrics.go +++ b/internal/metrics/metrics.go @@ -34,6 +34,14 @@ var LastFetchedBlock = promauto.NewGauge(prometheus.GaugeOpts{ Help: "The last block number fetched by the worker from the RPC", }) +// ChainTracker Metrics +var ( + ChainHead = promauto.NewGauge(prometheus.GaugeOpts{ + Name: "chain_tracker_chain_head", + Help: "The latest block number in the current chain", + }) +) + // Poller metrics var ( PolledBatchSize = promauto.NewGauge(prometheus.GaugeOpts{ diff --git a/internal/orchestrator/chain_tracker.go b/internal/orchestrator/chain_tracker.go new file mode 100644 index 0000000..8489af4 --- /dev/null +++ b/internal/orchestrator/chain_tracker.go @@ -0,0 +1,53 @@ +package orchestrator + +import ( + "context" + "time" + + "github.com/rs/zerolog/log" + "github.com/thirdweb-dev/indexer/internal/common" + "github.com/thirdweb-dev/indexer/internal/metrics" +) + +const DEFAULT_CHAIN_TRACKER_POLL_INTERVAL = 300000 // 5 minutes + +type ChainTracker struct { + rpc common.RPC + triggerIntervalMs int +} + +func NewChainTracker(rpc common.RPC) *ChainTracker { + + return &ChainTracker{ + rpc: rpc, + triggerIntervalMs: DEFAULT_CHAIN_TRACKER_POLL_INTERVAL, + } +} + +func (ct *ChainTracker) Start() { + interval := time.Duration(ct.triggerIntervalMs) * time.Millisecond + ticker := time.NewTicker(interval) + + log.Debug().Msgf("Chain tracker running") + go func() { + for range ticker.C { + latestBlockNumber, err := ct.getLatestBlockNumber() + if err != nil { + log.Error().Err(err).Msg("Error getting latest block number") + continue + } + metrics.ChainHead.Set(float64(latestBlockNumber) / 100) + } + }() + + // Keep the program running (otherwise it will exit) + select {} +} + +func (ct *ChainTracker) getLatestBlockNumber() (uint64, error) { + blockNumber, err := ct.rpc.EthClient.BlockNumber(context.Background()) + if err != nil { + return 0, err + } + return blockNumber, nil +} diff --git a/internal/orchestrator/orchestrator.go b/internal/orchestrator/orchestrator.go index 8e3d477..5168ba2 100644 --- a/internal/orchestrator/orchestrator.go +++ b/internal/orchestrator/orchestrator.go @@ -61,5 +61,13 @@ func (o *Orchestrator) Start() { }() } + // The chain tracker is always running + wg.Add(1) + go func() { + defer wg.Done() + chainTracker := NewChainTracker(o.rpc) + chainTracker.Start() + }() + wg.Wait() }