Adaptive chain-timer poll interval when head state is unchanged #308
Labels
No labels
agent:fix_bugfix
agent:fix_conflicts
agent:fix_security
agent:gap_analysis
agent:implement
agent:open_issues
agents
agent:security_audit
agent:verify
area
timecurve
arena
arena-v2
audit
block:feature_review
block:security
bug
contracts
design
devops
docs
documentation
enhancement
epic
feature
frontend
indexer
low priority
megaeth
operations
ops
performance
QA
qa checklist
referrals
removal
security
smart-contracts
tech-debt
testing
time-arena
timecurve
visual-qa
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
code/yieldomega#308
Loading…
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Parent
#306 — Benchmark indexer JSON-RPC load on localnet and reduce rate-limit risk
Baseline (MR !454): steady-state ~1 Hz chain-timer polls via
RPC_FAST_POLL_MSeven whenread_block_numberand timer epochs are unchanged. Parent comment: bringpeak_calls_10sdown to ≤ 50 (from ~354 idle).Current codebase
indexer/src/chain_timer.rs—run_poll_loopalways sleepshealth.backoff_sleep()after everypoll_once; no head-change short-circuit.indexer/src/rpc_poll_health.rs— adaptive spacing only on failure / HTTP 429 (5s → 15s → 30s). Healthy cadence fixed at 1000 ms, aligned withfrontend/src/lib/rpcConnectivity.ts.indexer/src/rpc_metrics.rs— exposespeak_calls_10s,calls_per_min_1m,by_caller.chain_timer.GET /v1/arena/timers— served from in-memoryTimecurveHeadSnapshotcache; UX expects ~1s freshness near deadline boundaries.docs/indexer/rpc-load-benchmark.md— ranks adaptive poll interval when head unchanged and timer epochs stable (est. 30–50% steady-state reduction).Why needed
Approved follow-up from #306 / MR !454: implement adaptive poll interval as a separate mitigation. When Anvil (or mainnet) produces no new blocks and onchain timer/podium epochs are stable, full
poll_onceevery second wastes RPC quota and contributes directly to burst peaks. This complements Multicall batching (sibling issue).Constraints / guardrails
GET /v1/arena/timerspolled_at_mswithin 3s when RPC healthy (perrpc-load-benchmark.mdproposed SLO).RPC_FAST_POLL_MS(1s) when:read_block_numberadvances,last_buy_epochor anypodium_epochs[cat]changes,podium_deadlines_secor globaldeadline_secwithin a configurable margin (e.g. ≤ 30s),RpcPollHealthalready handles failure tiers).CHAIN_TIMER_IDLE_POLL_MS, default TBD by benchmark — likely 2–5s) without breaking frontend indexer-first assumptions.INGESTION_ENABLEDbehavior.#237WSS hints remain deferred — this issue is RPC poll spacing only.Relevant files
indexer/src/chain_timer.rsrun_poll_loop,poll_once, snapshot comparisonindexer/src/rpc_poll_health.rsindexer/src/rpc_metrics.rsfrontend/src/lib/rpcConnectivity.tsscripts/benchmark-indexer-rpc-anvil.shdocs/indexer/rpc-load-benchmark.mdRecommended direction
poll_once, compare newTimecurveHeadSnapshotto cached:read_block_number,last_buy_epoch,podium_epochs,deadline_sec,podium_deadlines_sec.CHAIN_TIMER_IDLE_POLL_MSinstead ofRPC_FAST_POLL_MS.eth_blockNumberon idle cycles (1 call) to detect new heads without fullpoll_once— only if it reduces total RPC vs full poll; benchmark both variants.debug(fastvsidle) for operator forensics.indexer/README.mdandrpc-load-benchmark.md.Acceptance criteria
bash scripts/benchmark-indexer-rpc-anvil.shidle scenario:calls_per_min_1mdrops ≥ 30% vsrpc-benchmark-20260610T111415Z.jsonbaseline (or combined with Multicall issue,peak_calls_10s≤ 50).GET /v1/arena/timerspolled_at_ms≤ 3s stale under healthy RPC during idle Anvil run.RpcPollHealthfailure / 429 backoff unchanged.INV-INDEXER-306-NO-REGRESSIONpasses.Test plan
Attack / abuse test plan
eth_blockNumberwhile chain advances on another node, ingestion lag alerts (#168) still fire; idle mode must not suppress ingestion.last_buy_epochtriggers fast polls before UI shows stale timer chips.report_rate_limited.Verification criteria
cd indexer && cargo clippy --all-targets -- -D warnings && cargo testbash scripts/verify-indexer-rpc-metrics.shbash scripts/verify-podium-live-anvil.shBENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.shcalls_per_min_1m/peak_calls_10svs baseline/v1/arena/timersduring idlepolled_at_msage < 3000 msmentioned in issue #306
mentioned in merge request !455
mentioned in commit
11397d80e9mentioned in commit
dff689cad3mentioned in merge request !456
Verification — GitLab #308 (adaptive chain-timer poll interval)
Verified against
main@1a7b8ed5(includes merged MR !455). Local checkout was initially stale; pulled before re-running checks.Results
cd indexer && cargo test --lib chain_timer_poll(17 tests:unchanged_head_is_idle,block_advance_is_fast,epoch_change_is_fast,podium_deadline_proximity_is_fast,failure_backoff_*,rate_limit_overrides_idle_on_success, …)cd indexer && cargo clippy --all-targets -- -D warnings && cargo testYIELDOMEGA_PG_TEST_URL)RpcPollHealthfailure / 429 backoff unchangedchain_timer_poll+rpc_poll_healthtestsINV-INDEXER-306-STATUS-METRICS)bash scripts/verify-indexer-rpc-metrics.sh(aftercargo build --release)schema=2.11.0total_calls=56peak_10s=20BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.shvsdocs/indexer/benchmarks/rpc-benchmark-20260610T111415Z.jsonidle last samplecalls_per_min_1m740 → 84 (−89%);peak_calls_10s354 → 20 (target ≤50)CHAIN_TIMER_IDLE_POLL_MSclamped to 3000;refresh_snapshot_polled_at_mson idle short-circuit; unit testsblock_advance_is_fast,podium_epoch_change_is_fastINV-INDEXER-306-NO-REGRESSION)bash scripts/verify-podium-live-anvil.shcurl …/v1/arena/timers | jq .polled_at_mspolled_at_msis maintained on the in-memory snapshot but not serialized onGET /v1/arena/timers; freshness enforced by 3s idle cap + short-circuit refreshHow verified
main(merge11397d80/ MR !455).cargo clippy --all-targets -- -D warnings && cargo test— all green.cargo test --lib chain_timer_poll— 17/17 adaptive poll selection tests.cargo build --release) so Anvil verify scripts are not blocked by cold-compile timeouts.bash scripts/verify-indexer-rpc-metrics.sh— status exposesrpc_metricswith chain-timer traffic after warm-up.BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh— wrotedocs/indexer/benchmarks/rpc-benchmark-20260613T135052Z.json; idle steady-state matches shipped benchmark doc (~84 calls/min, peak 20/10s).bash scripts/verify-podium-live-anvil.sh— podium/chain-timer HTTP checks completed; failed at tailcargo test --test integration_stage2witharena_wallet_stats_two_epochs_and_bonus_fields:warbow_battle_points1000 vs expected 750 (stale assertion after WarBow BP routing #310; not introduced by chain-timer spacing).#308-specific acceptance: PASS
Adaptive poll implementation (
indexer/src/chain_timer_poll.rs,chain_timer.rsloop) meets spacing, RPC reduction, failure-backoff, and near-deadline fast-mode requirements.Follow-ups (out of scope for #308)
arena_wallet_stats_two_epochs_and_bonus_fieldsexpectedwarbow_battle_pointsto 1000 (or file under #310) soverify-podium-live-anvil.shregains greenINV-INDEXER-306-NO-REGRESSION.polled_at_msonGET /v1/arena/timersif operators want direct HTTP freshness probes (SLO is already enforced in-process).mentioned in merge request !503