Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions cmd/snapshot-checker/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ func main() {
ExecutionClient: executionClient,
CronIntervalSec: cfg.CronIntervalSec,
Network: cfg.Network,
BlockRange: cfg.BlockRange,
}

// Print configuration
Expand Down Expand Up @@ -118,6 +119,7 @@ func printSnapshotCheckerConfig(prefix string, sc domain.SnapshotCheckerConfig)
b.WriteString("SnapshotCheckerConfig:\n")
b.WriteString(fmt.Sprintf(" Network: %s\n", sc.Network))
b.WriteString(fmt.Sprintf(" CronIntervalSec: %d\n", sc.CronIntervalSec))
b.WriteString(fmt.Sprintf(" BlockRange: %d\n", sc.BlockRange))
b.WriteString(" ExecutionClient:\n")
b.WriteString(fmt.Sprintf(" ShortName: %s\n", c.ShortName))
b.WriteString(fmt.Sprintf(" DnpName: %s\n", c.DnpName))
Expand Down
1 change: 1 addition & 0 deletions internal/application/domain/snapshot.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type SnapshotCheckerConfig struct {
ExecutionClient ExecutionClientInfo // The execution client to manage
CronIntervalSec int // Interval between snapshot checks in seconds (default 6 hours)
Network string // Network name (e.g., hoodi)
BlockRange int // Number of blocks to consider as up-to-date range
}

// ValidExecutionClients contains all valid execution client short names for hoodi
Expand Down
27 changes: 27 additions & 0 deletions internal/application/services/snapshot_checker.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"clients-test/internal/logger"
"context"
"fmt"
"strconv"
"time"
)

Expand Down Expand Up @@ -190,6 +191,11 @@ func (s *SnapshotCheckerService) checkNeedsSnapshotDownload(ctx context.Context,
logger.InfoWithPrefix(snapshotLogPrefix, "[%s] Current snapshot block: %s", client.ShortName, currentBlockNumber)
logger.InfoWithPrefix(snapshotLogPrefix, "[%s] Latest available block: %s", client.ShortName, latestBlockNumber)

// BlockRange logic: if within range, consider up to date (before isNewerSnapshot)
if s.isWithinBlockRange(currentBlockNumber, latestBlockNumber, s.config.BlockRange, client.ShortName) {
return false, nil
}

// Check if newer snapshot is available
isNewer, err := s.blockNumber.IsNewerSnapshot(ctx, latestBlockNumber)
if err != nil {
Expand All @@ -205,6 +211,27 @@ func (s *SnapshotCheckerService) checkNeedsSnapshotDownload(ctx context.Context,
return isNewer, nil
}

// isWithinBlockRange checks if the difference between latest and current block is within the given range.
// If so, logs and returns true (should skip download). Otherwise, returns false.
func (s *SnapshotCheckerService) isWithinBlockRange(currentBlock, latestBlock string, blockRange int, clientShortName string) bool {
currentInt, err1 := parseBlockNumber(currentBlock)
latestInt, err2 := parseBlockNumber(latestBlock)
if err1 == nil && err2 == nil && blockRange > 0 {
diff := latestInt - currentInt
if diff >= 0 && diff <= int64(blockRange) {
logger.InfoWithPrefix(snapshotLogPrefix, "[%s] Block difference (%d) is within BlockRange (%d); skipping download. Current: %d, Latest: %d", clientShortName, diff, blockRange, currentInt, latestInt)
return true
}
}
logger.InfoWithPrefix(snapshotLogPrefix, "[%s] Block difference is outside BlockRange; download needed. Current: %s, Latest: %s", clientShortName, currentBlock, latestBlock)
return false
}

// parseBlockNumber safely parses a block number string to int64
func parseBlockNumber(s string) (int64, error) {
return strconv.ParseInt(s, 10, 64)
}

// StopDownload stops the current download container (for graceful shutdown)
func (s *SnapshotCheckerService) StopDownload(ctx context.Context) {
clientName := s.config.ExecutionClient.ShortName
Expand Down
16 changes: 11 additions & 5 deletions internal/config/snapshot_checker_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,34 +15,40 @@ type SnapshotCheckerAppConfig struct {
ExecutionClient string
Network string
CronIntervalSec int
RunOnce bool // If true, run once and exit (for testing)
BlockRange int // Number of blocks to check for new snapshots
RunOnce bool // If true, run once and exit
}

// DefaultCronIntervalSec is 6 hours in seconds
const DefaultCronIntervalSec = 6 * 60 * 60

// DefaultBlockRange is 7200 blocks (~1 day without missing blocks)
const DefaultBlockRange = 7200

// ParseSnapshotCheckerConfig parses CLI flags and environment variables into a SnapshotCheckerAppConfig
func ParseSnapshotCheckerConfig() SnapshotCheckerAppConfig {
// CLI flags
executionClient := flag.String("execution-client", "", "Execution client name (geth, nethermind, reth, besu, erigon). Required.")
network := flag.String("network", "hoodi", "Network name (e.g., hoodi). Default: hoodi")
cronIntervalSec := flag.Int("cron-interval", 0, "Interval between snapshot checks in seconds. Default: 21600 (6 hours)")
runOnce := flag.Bool("run-once", true, "Run once and exit (for testing). Default: false")
blockRange := flag.Int("block-range", 0, "Number of blocks to check for new snapshots. Default: 7200 (1 day)")
runOnce := flag.Bool("run-once", true, "Run once and exit. Default: false")

flag.Parse()

config := SnapshotCheckerAppConfig{
ExecutionClient: strings.TrimSpace(strings.ToLower(getConfigValue(*executionClient, "EXECUTION_CLIENT", ""))),
Network: getConfigValue(*network, "NETWORK", "hoodi"),
CronIntervalSec: getCronIntervalConfigValueInt(*cronIntervalSec, "CRON_INTERVAL_SEC", DefaultCronIntervalSec),
CronIntervalSec: getConfigValueInt(*cronIntervalSec, "CRON_INTERVAL_SEC", DefaultCronIntervalSec),
BlockRange: getConfigValueInt(*blockRange, "BLOCK_RANGE", DefaultBlockRange),
RunOnce: *runOnce || os.Getenv("RUN_ONCE") == "true",
}

return config
}

// getCronIntervalConfigValueInt returns the flag value if set, otherwise falls back to the environment variable, then default
func getCronIntervalConfigValueInt(flagValue int, envName string, defaultValue int) int {
// getConfigValueInt returns the flag value if set, otherwise falls back to the environment variable, then default
func getConfigValueInt(flagValue int, envName string, defaultValue int) int {
if flagValue != 0 {
return flagValue
}
Expand Down