Batch chain-timer eth_calls via Multicall3 to cut JSON-RPC burst #307

Closed
opened 2026-06-13 06:34:50 +00:00 by PlasticDigits · 15 comments
PlasticDigits commented 2026-06-13 06:34:50 +00:00 (Migrated from gitlab.com)

Parent

#306 — Benchmark indexer JSON-RPC load on localnet and reduce rate-limit risk

Baseline (MR !454, Anvil): chain_timer dominates steady-state RPC — each poll_once (~1 Hz) issues 2 head reads plus ~30 sequential eth_calls. Idle peak_calls_10s ≈ 354; parent comment targets ≤ 50 / 10s.

Current codebase

  • indexer/src/chain_timer.rs — poll_once fans out sequential eth_call_u256 / eth_call_address / podium_at_block_id at a single block_id. Sale head fields use tokio::try_join! (parallel round-trips, not batched). sale_state::poll_sale_state_at_block adds more eth_calls.
  • indexer/src/sale_state.rs — additional head eth_calls tagged chain_timer.
  • indexer/src/rpc_http.rs — shared transport; rpc_first_ok_instrumented records one logical call per attempt. No Multicall3 helper yet.
  • indexer/src/rpc_metrics.rs — RpcCaller::ChainTimer, peak_calls_10s rolling burst window.
  • docs/indexer/rpc-load-benchmark.md — ranks batch/multicall (est. 40–60% fewer round-trips) and coalesce duplicate eth_call at same block tag (est. 10–20%) as top chain-timer wins.
  • Benchmark harness — bash scripts/benchmark-indexer-rpc-anvil.sh samples GET /v1/status → rpc_metrics.

Why needed

Approved follow-up from #306 / MR !454: implement ranked mitigations starting with multicall batching. Without collapsing the per-poll eth_call fan-out, the indexer cannot meet the operator target of peak_calls_10s ≤ 50 on localnet idle scenarios and will remain at high rate-limit risk on shared RPC providers.

Constraints / guardrails

  • Preserve GET /v1/arena/timers and GET /v1/timecurve/sale-state response shapes and block-tag semantics (#254, #273).
  • All head reads must remain at the same read_block_number / BlockId within one poll cycle.
  • Do not regress ingestion liveness (#168) or RpcPollHealth 429 backoff behavior.
  • Multicall3 address: 0xcA11bde05977b3631167028862bE2a173976CA11 (standard on Anvil / EVM chains). Graceful fallback if aggregate unavailable (document operator requirement).
  • rpc_metrics must still count logical provider round-trips (one aggregate = one eth_call metric), not inner sub-calls.
  • Complements (does not replace) adaptive poll-interval work — see sibling issue from #306 triage.

Relevant files

Path Role
indexer/src/chain_timer.rs poll_once, selectors, podium/sale-head reads
indexer/src/sale_state.rs Sale-state eth_calls inside timer poll
indexer/src/rpc_http.rs Transport + future aggregate3 helper
indexer/src/rpc_metrics.rs Burst / per-caller metrics
scripts/benchmark-indexer-rpc-anvil.sh Before/after benchmark
scripts/verify-indexer-rpc-metrics.sh Smoke
scripts/verify-podium-live-anvil.sh Arena API regression
docs/indexer/rpc-load-benchmark.md Operator runbook + SLO table
  1. Add a small multicall.rs (or module in rpc_http.rs) wrapping Multicall3 aggregate3 with allowFailure: false, decoding fixed ABI layouts for the timer/sale-head selectors already defined in chain_timer.rs.
  2. Refactor poll_once to build one (or few) aggregate batches covering:
    • timer scalars (arenaStart, deadline, timerCapSec, lastBuyEpoch, …)
    • podiumDeadline(cat) / podiumEpoch(cat) loops
    • sale-head getters currently in try_join!
    • sale_state reads
    • optional PodiumVaults.activePoolBalance(cat) when configured (#302)
  3. Keep podium(cat) as separate aggregate(s) or a dedicated batch — tuple decode is larger; still far fewer round-trips than 4× sequential.
  4. Coalesce duplicate selector+args at the same block tag before submitting aggregate (cheap win per rpc-load-benchmark.md).
  5. Unit-test decode fixtures with known return blobs; integration test on Anvil that poll_once output matches pre-refactor snapshot for a fixed block.

Acceptance criteria

  • poll_once uses Multicall3 (or equivalent single-round-trip batching) for the majority of chain-timer eth_calls.
  • bash scripts/benchmark-indexer-rpc-anvil.sh with BENCHMARK_SCENARIO_SEC=120: idle scenario final sample shows peak_calls_10s ≤ 50 (or document remaining gap + follow-up if adaptive poll is also required).
  • by_caller.chain_timer eth_call count per minute drops ≥ 40% vs pre-change baseline artifact (docs/indexer/benchmarks/rpc-benchmark-20260610T111415Z.json).
  • GET /v1/arena/timers polled_at_ms freshness stays within 3s of wall clock when RPC healthy (idle Anvil).
  • INV-INDEXER-306-NO-REGRESSION checks pass (podium-live verify + indexer tests).
  • Update docs/indexer/rpc-load-benchmark.md baseline table and mitigation status.

Test plan

export PATH="$HOME/.foundry/bin:$PATH"
source /usr/local/cargo/env 2>/dev/null || true

cd indexer && cargo clippy --all-targets -- -D warnings && cargo test
bash scripts/verify-indexer-rpc-metrics.sh
bash scripts/verify-podium-live-anvil.sh
BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh
# Compare peak_calls_10s / by_caller.chain_timer in new docs/indexer/benchmarks/*.json

Attack / abuse test plan

  • Malformed aggregate returns — ensure decode errors surface as poll failure → RpcPollHealth backoff, not stale cache served as fresh.
  • Partial batch failure — allowFailure: false should fail entire poll; no mixed stale/new head fields in API responses.
  • Oversized batch — stay within provider eth_call gas/data limits; split batches if needed without reintroducing 30+ round-trips.
  • Metrics gaming — confirm rpc_metrics records one logical call per aggregate, not per inner call.

Verification criteria

Check Command Expected
Unit + clippy cd indexer && cargo clippy --all-targets -- -D warnings && cargo test PASS
Status metrics smoke bash scripts/verify-indexer-rpc-metrics.sh PASS, rpc_metrics populated
Arena head regression bash scripts/verify-podium-live-anvil.sh PASS
RPC burst target BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh idle peak_calls_10s ≤ 50
API freshness curl -sf http://127.0.0.1:3103/v1/status | jq .rpc_metrics after warm-up chain_timer dominant method still eth_call but rate reduced
## Parent [#306 — Benchmark indexer JSON-RPC load on localnet and reduce rate-limit risk](https://gitlab.com/PlasticDigits/yieldomega/-/issues/306) Baseline (MR !454, Anvil): `chain_timer` dominates steady-state RPC — each `poll_once` (~1 Hz) issues **2** head reads plus **~30** sequential `eth_call`s. Idle `peak_calls_10s` ≈ **354**; parent comment targets **≤ 50 / 10s**. ## Current codebase - **`indexer/src/chain_timer.rs`** — `poll_once` fans out sequential `eth_call_u256` / `eth_call_address` / `podium_at_block_id` at a single `block_id`. Sale head fields use `tokio::try_join!` (parallel round-trips, not batched). `sale_state::poll_sale_state_at_block` adds more `eth_call`s. - **`indexer/src/sale_state.rs`** — additional head `eth_call`s tagged `chain_timer`. - **`indexer/src/rpc_http.rs`** — shared transport; `rpc_first_ok_instrumented` records one logical call per attempt. No Multicall3 helper yet. - **`indexer/src/rpc_metrics.rs`** — `RpcCaller::ChainTimer`, `peak_calls_10s` rolling burst window. - **`docs/indexer/rpc-load-benchmark.md`** — ranks **batch/multicall** (est. 40–60% fewer round-trips) and **coalesce duplicate `eth_call`** at same block tag (est. 10–20%) as top chain-timer wins. - **Benchmark harness** — `bash scripts/benchmark-indexer-rpc-anvil.sh` samples `GET /v1/status` → `rpc_metrics`. ## Why needed Approved follow-up from #306 / MR !454: implement ranked mitigations starting with multicall batching. Without collapsing the per-poll `eth_call` fan-out, the indexer cannot meet the operator target of **`peak_calls_10s` ≤ 50** on localnet idle scenarios and will remain at high rate-limit risk on shared RPC providers. ## Constraints / guardrails - Preserve **`GET /v1/arena/timers`** and **`GET /v1/timecurve/sale-state`** response shapes and block-tag semantics ([#254](https://gitlab.com/PlasticDigits/yieldomega/-/issues/254), [#273](https://gitlab.com/PlasticDigits/yieldomega/-/issues/273)). - All head reads must remain at the **same `read_block_number`** / `BlockId` within one poll cycle. - Do **not** regress ingestion liveness ([#168](https://gitlab.com/PlasticDigits/yieldomega/-/issues/168)) or `RpcPollHealth` 429 backoff behavior. - Multicall3 address: `0xcA11bde05977b3631167028862bE2a173976CA11` (standard on Anvil / EVM chains). Graceful fallback if aggregate unavailable (document operator requirement). - `rpc_metrics` must still count **logical** provider round-trips (one aggregate = one `eth_call` metric), not inner sub-calls. - Complements (does not replace) adaptive poll-interval work — see sibling issue from #306 triage. ## Relevant files | Path | Role | |------|------| | `indexer/src/chain_timer.rs` | `poll_once`, selectors, podium/sale-head reads | | `indexer/src/sale_state.rs` | Sale-state `eth_call`s inside timer poll | | `indexer/src/rpc_http.rs` | Transport + future `aggregate3` helper | | `indexer/src/rpc_metrics.rs` | Burst / per-caller metrics | | `scripts/benchmark-indexer-rpc-anvil.sh` | Before/after benchmark | | `scripts/verify-indexer-rpc-metrics.sh` | Smoke | | `scripts/verify-podium-live-anvil.sh` | Arena API regression | | `docs/indexer/rpc-load-benchmark.md` | Operator runbook + SLO table | ## Recommended direction 1. Add a small **`multicall.rs`** (or module in `rpc_http.rs`) wrapping Multicall3 `aggregate3` with `allowFailure: false`, decoding fixed ABI layouts for the timer/sale-head selectors already defined in `chain_timer.rs`. 2. Refactor `poll_once` to build one (or few) aggregate batches covering: - timer scalars (`arenaStart`, `deadline`, `timerCapSec`, `lastBuyEpoch`, …) - `podiumDeadline(cat)` / `podiumEpoch(cat)` loops - sale-head getters currently in `try_join!` - `sale_state` reads - optional `PodiumVaults.activePoolBalance(cat)` when configured ([#302](https://gitlab.com/PlasticDigits/yieldomega/-/issues/302)) 3. Keep **`podium(cat)`** as separate aggregate(s) or a dedicated batch — tuple decode is larger; still far fewer round-trips than 4× sequential. 4. **Coalesce** duplicate selector+args at the same block tag before submitting aggregate (cheap win per `rpc-load-benchmark.md`). 5. Unit-test decode fixtures with known return blobs; integration test on Anvil that `poll_once` output matches pre-refactor snapshot for a fixed block. ## Acceptance criteria - [ ] `poll_once` uses Multicall3 (or equivalent single-round-trip batching) for the majority of chain-timer `eth_call`s. - [ ] `bash scripts/benchmark-indexer-rpc-anvil.sh` with `BENCHMARK_SCENARIO_SEC=120`: **idle** scenario final sample shows `peak_calls_10s` **≤ 50** (or document remaining gap + follow-up if adaptive poll is also required). - [ ] `by_caller.chain_timer` `eth_call` count per minute drops ≥ **40%** vs pre-change baseline artifact (`docs/indexer/benchmarks/rpc-benchmark-20260610T111415Z.json`). - [ ] `GET /v1/arena/timers` `polled_at_ms` freshness stays within **3s** of wall clock when RPC healthy (idle Anvil). - [ ] `INV-INDEXER-306-NO-REGRESSION` checks pass (podium-live verify + indexer tests). - [ ] Update `docs/indexer/rpc-load-benchmark.md` baseline table and mitigation status. ## Test plan ```bash export PATH="$HOME/.foundry/bin:$PATH" source /usr/local/cargo/env 2>/dev/null || true cd indexer && cargo clippy --all-targets -- -D warnings && cargo test bash scripts/verify-indexer-rpc-metrics.sh bash scripts/verify-podium-live-anvil.sh BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh # Compare peak_calls_10s / by_caller.chain_timer in new docs/indexer/benchmarks/*.json ``` ## Attack / abuse test plan - **Malformed aggregate returns** — ensure decode errors surface as poll failure → `RpcPollHealth` backoff, not stale cache served as fresh. - **Partial batch failure** — `allowFailure: false` should fail entire poll; no mixed stale/new head fields in API responses. - **Oversized batch** — stay within provider `eth_call` gas/data limits; split batches if needed without reintroducing 30+ round-trips. - **Metrics gaming** — confirm `rpc_metrics` records one logical call per aggregate, not per inner call. ## Verification criteria | Check | Command | Expected | |-------|---------|----------| | Unit + clippy | `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` | PASS | | Status metrics smoke | `bash scripts/verify-indexer-rpc-metrics.sh` | PASS, `rpc_metrics` populated | | Arena head regression | `bash scripts/verify-podium-live-anvil.sh` | PASS | | RPC burst target | `BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh` | idle `peak_calls_10s` ≤ **50** | | API freshness | `curl -sf http://127.0.0.1:3103/v1/status \| jq .rpc_metrics` after warm-up | `chain_timer` dominant method still `eth_call` but rate reduced |
PlasticDigits commented 2026-06-13 06:35:43 +00:00 (Migrated from gitlab.com)

mentioned in issue #306

mentioned in issue #306
PlasticDigits commented 2026-06-13 06:54:13 +00:00 (Migrated from gitlab.com)

Note that rpc calls may have size limitations

Note that rpc calls may have size limitations
PlasticDigits commented 2026-06-13 07:28:14 +00:00 (Migrated from gitlab.com)

mentioned in merge request !456

mentioned in merge request !456
PlasticDigits commented 2026-06-13 09:31:07 +00:00 (Migrated from gitlab.com)

mentioned in commit dff689cad3

mentioned in commit dff689cad32eaeca286ee587c6d24a0156f83c7e
PlasticDigits commented 2026-06-13 10:00:06 +00:00 (Migrated from gitlab.com)

mentioned in commit c11c243660

mentioned in commit c11c243660b13ac2108addfcbade7203d51d1453
PlasticDigits commented 2026-06-13 14:00:29 +00:00 (Migrated from gitlab.com)

Verification — #307 (agent:verify)

Verdict: FAIL — Multicall3 batching not implemented on main. Issue remains open for implementation.

Code inspection

Check Result
indexer/src/multicall.rs or aggregate3 helper in rpc_http.rs FAIL — no matches in indexer/
poll_once uses Multicall3 batching FAIL — chain_timer.rs still issues ~30 sequential eth_call_u256 / podium_at_block_id / try_join! sale-head reads per cycle
docs/indexer/rpc-load-benchmark.md mitigation status updated FAIL — still lists batch/multicall as proposed, not shipped

Automated checks

Acceptance item Command Result
Unit + clippy cd indexer && cargo clippy --all-targets -- -D warnings && cargo test PASS — clippy clean; 39 unit tests pass
INV-INDEXER-306-STATUS-METRICS bash scripts/verify-indexer-rpc-metrics.sh PASS — schema=2.11.0, total_calls=1266, peak_calls_10s=354
INV-INDEXER-306-NO-REGRESSION (podium-live) bash scripts/verify-podium-live-anvil.sh FAIL — on-chain podium parity steps pass; script fails at bundled cargo test --test integration_stage2 (arena_wallet_stats_two_epochs_and_bonus_fields: expected 750, got 1000) — unrelated to multicall, but blocks full green
Idle peak_calls_10s ≤ 50 verify-indexer-rpc-metrics.sh warm-up metrics FAIL — peak_calls_10s=354 (matches pre-change baseline docs/indexer/benchmarks/rpc-benchmark-20260610T111415Z.json)
by_caller.chain_timer eth_call ≥40% reduction vs baseline compare to rpc-benchmark-20260610T111415Z.json (eth_call:chain_timer 352/min sample) FAIL — 0% reduction; current code unchanged
Full benchmark harness BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh SKIP — not re-run (≈6 min); smoke metrics already reproduce baseline burst (354 / 10s); no artifact under docs/indexer/benchmarks/ post-change
GET /v1/arena/timers freshness ≤3s curl :3104/v1/arena/timers after warm-up PASS (proxy) — HTTP 200, read_block_number tracks Anvil head; polled_at_ms is not exposed in the JSON body (only stored in ChainTimerSnapshot internally)
Attack/abuse tests (malformed aggregate, partial batch, metrics gaming) — SKIP — no aggregate path to exercise

How verified

  • glab issue view 307 --comments
  • Static review: indexer/src/chain_timer.rs, sale_state.rs, rpc_http.rs
  • bash scripts/verify-cloud-postgres.sh — PASS
  • Release indexer pre-built (cargo build --release) before smoke reruns
  • Native Postgres :5433, Anvil with --code-size-limit 524288

Follow-ups

  1. Implement per issue direction: multicall.rs + aggregate3 batches in poll_once / sale_state; split batches if RPC payload limits hit (per issue comment).
  2. Re-run BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh and commit new docs/indexer/benchmarks/*.json + update baseline table in rpc-load-benchmark.md.
  3. Consider exposing polled_at_ms on GET /v1/arena/timers if freshness SLO is operator-facing.
  4. Fix flaky arena_wallet_stats_two_epochs_and_bonus_fields integration test (blocks verify-podium-live-anvil.sh).
## Verification — #307 (agent:verify) **Verdict: FAIL — Multicall3 batching not implemented on `main`.** Issue remains open for implementation. ### Code inspection | Check | Result | |-------|--------| | `indexer/src/multicall.rs` or `aggregate3` helper in `rpc_http.rs` | **FAIL** — no matches in `indexer/` | | `poll_once` uses Multicall3 batching | **FAIL** — `chain_timer.rs` still issues ~30 sequential `eth_call_u256` / `podium_at_block_id` / `try_join!` sale-head reads per cycle | | `docs/indexer/rpc-load-benchmark.md` mitigation status updated | **FAIL** — still lists batch/multicall as proposed, not shipped | ### Automated checks | Acceptance item | Command | Result | |-----------------|---------|--------| | Unit + clippy | `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` | **PASS** — clippy clean; 39 unit tests pass | | `INV-INDEXER-306-STATUS-METRICS` | `bash scripts/verify-indexer-rpc-metrics.sh` | **PASS** — `schema=2.11.0`, `total_calls=1266`, **`peak_calls_10s=354`** | | `INV-INDEXER-306-NO-REGRESSION` (podium-live) | `bash scripts/verify-podium-live-anvil.sh` | **FAIL** — on-chain podium parity steps pass; script fails at bundled `cargo test --test integration_stage2` (`arena_wallet_stats_two_epochs_and_bonus_fields`: expected `750`, got `1000`) — unrelated to multicall, but blocks full green | | Idle `peak_calls_10s` ≤ 50 | `verify-indexer-rpc-metrics.sh` warm-up metrics | **FAIL** — `peak_calls_10s=354` (matches pre-change baseline `docs/indexer/benchmarks/rpc-benchmark-20260610T111415Z.json`) | | `by_caller.chain_timer` `eth_call` ≥40% reduction vs baseline | compare to `rpc-benchmark-20260610T111415Z.json` (`eth_call:chain_timer` 352/min sample) | **FAIL** — **0% reduction**; current code unchanged | | Full benchmark harness | `BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh` | **SKIP** — not re-run (≈6 min); smoke metrics already reproduce baseline burst (**354 / 10s**); no artifact under `docs/indexer/benchmarks/` post-change | | `GET /v1/arena/timers` freshness ≤3s | curl `:3104/v1/arena/timers` after warm-up | **PASS** (proxy) — HTTP 200, `read_block_number` tracks Anvil head; **`polled_at_ms` is not exposed** in the JSON body (only stored in `ChainTimerSnapshot` internally) | | Attack/abuse tests (malformed aggregate, partial batch, metrics gaming) | — | **SKIP** — no aggregate path to exercise | ### How verified - `glab issue view 307 --comments` - Static review: `indexer/src/chain_timer.rs`, `sale_state.rs`, `rpc_http.rs` - `bash scripts/verify-cloud-postgres.sh` — PASS - Release indexer pre-built (`cargo build --release`) before smoke reruns - Native Postgres `:5433`, Anvil with `--code-size-limit 524288` ### Follow-ups 1. **Implement** per issue direction: `multicall.rs` + `aggregate3` batches in `poll_once` / `sale_state`; split batches if RPC payload limits hit (per issue comment). 2. Re-run `BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh` and commit new `docs/indexer/benchmarks/*.json` + update baseline table in `rpc-load-benchmark.md`. 3. Consider exposing `polled_at_ms` on `GET /v1/arena/timers` if freshness SLO is operator-facing. 4. Fix flaky `arena_wallet_stats_two_epochs_and_bonus_fields` integration test (blocks `verify-podium-live-anvil.sh`).
PlasticDigits commented 2026-06-13 14:13:17 +00:00 (Migrated from gitlab.com)

mentioned in merge request !471

mentioned in merge request !471
PlasticDigits commented 2026-06-14 02:54:33 +00:00 (Migrated from gitlab.com)

Verification — #307 (agent:verify)

Verdict: FAIL — Multicall3 batching not implemented on main. Issue remains open for implementation.

Code inspection

Check Result
indexer/src/multicall.rs or aggregate3 helper in rpc_http.rs FAIL — no multicall / aggregate3 matches under indexer/
poll_once uses Multicall3 batching FAIL — chain_timer.rs still issues sequential eth_call_u256 / podium_at_block_id / try_join! sale-head reads (~30 round-trips per ~1 Hz cycle)
docs/indexer/rpc-load-benchmark.md mitigation status updated FAIL — batch/multicall still listed as proposed, not shipped

Automated checks

Acceptance item Command Result
Unit + clippy cd indexer && cargo clippy --all-targets -- -D warnings && cargo test PASS — clippy clean; 39 unit + 4 integration tests pass
INV-INDEXER-306-STATUS-METRICS bash scripts/verify-indexer-rpc-metrics.sh PASS — schema=2.11.0, total_calls=1266, peak_calls_10s=354
INV-INDEXER-306-NO-REGRESSION (podium-live) bash scripts/verify-podium-live-anvil.sh FAIL — on-chain podium parity steps pass; script fails at bundled cargo test --test integration_stage2 (arena_wallet_stats_two_epochs_and_bonus_fields: expected warbow_battle_points=750, got 1000). Unrelated to multicall; same test passes in standalone cargo test (possible ordering/env flake in verify harness)
Idle peak_calls_10s ≤ 50 BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh FAIL — idle final sample peak_calls_10s=354 (matches pre-change baseline rpc-benchmark-20260610T111415Z.json)
by_caller.chain_timer eth_call ≥40% reduction vs baseline compare idle warm-up sample to rpc-benchmark-20260610T111415Z.json (eth_call:chain_timer 352/10s window) FAIL — 0% reduction; new 120s run reproduces 352 eth_call:chain_timer per 10s window
GET /v1/arena/timers freshness ≤3s api_arena.rs + smoke HTTP 200 during verify scripts PASS (proxy) — read_block_number tracks Anvil head; polled_at_ms not exposed in JSON body (only in internal ChainTimerSnapshot)
Attack/abuse tests (malformed aggregate, partial batch, metrics gaming) — SKIP — no aggregate decode path to exercise

How verified

  • glab issue view 307 --comments
  • Static review: indexer/src/chain_timer.rs, sale_state.rs, rpc_http.rs
  • bash scripts/verify-cloud-postgres.sh — PASS
  • Pre-built release indexer (cargo build --release) before smoke reruns (first smoke attempt timed out during release compile)
  • Native Postgres :5433, Anvil with --code-size-limit 524288

Follow-ups

  1. Implement per issue direction: multicall.rs + aggregate3 batches in poll_once / sale_state; split batches if RPC payload limits hit (per issue comment).
  2. Re-run BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh, commit new docs/indexer/benchmarks/*.json, and update baseline table in rpc-load-benchmark.md.
  3. Consider exposing polled_at_ms on GET /v1/arena/timers if freshness SLO is operator-facing.
  4. Investigate arena_wallet_stats_two_epochs_and_bonus_fields flake in verify-podium-live-anvil.sh harness (blocks full green INV-INDEXER-306-NO-REGRESSION).
## Verification — #307 (agent:verify) **Verdict: FAIL — Multicall3 batching not implemented on `main`.** Issue remains open for implementation. ### Code inspection | Check | Result | |-------|--------| | `indexer/src/multicall.rs` or `aggregate3` helper in `rpc_http.rs` | **FAIL** — no `multicall` / `aggregate3` matches under `indexer/` | | `poll_once` uses Multicall3 batching | **FAIL** — `chain_timer.rs` still issues sequential `eth_call_u256` / `podium_at_block_id` / `try_join!` sale-head reads (~30 round-trips per ~1 Hz cycle) | | `docs/indexer/rpc-load-benchmark.md` mitigation status updated | **FAIL** — batch/multicall still listed as proposed, not shipped | ### Automated checks | Acceptance item | Command | Result | |-----------------|---------|--------| | Unit + clippy | `cd indexer && cargo clippy --all-targets -- -D warnings && cargo test` | **PASS** — clippy clean; 39 unit + 4 integration tests pass | | `INV-INDEXER-306-STATUS-METRICS` | `bash scripts/verify-indexer-rpc-metrics.sh` | **PASS** — `schema=2.11.0`, `total_calls=1266`, **`peak_calls_10s=354`** | | `INV-INDEXER-306-NO-REGRESSION` (podium-live) | `bash scripts/verify-podium-live-anvil.sh` | **FAIL** — on-chain podium parity steps pass; script fails at bundled `cargo test --test integration_stage2` (`arena_wallet_stats_two_epochs_and_bonus_fields`: expected `warbow_battle_points=750`, got `1000`). Unrelated to multicall; same test passes in standalone `cargo test` (possible ordering/env flake in verify harness) | | Idle `peak_calls_10s` ≤ 50 | `BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh` | **FAIL** — idle final sample **`peak_calls_10s=354`** (matches pre-change baseline `rpc-benchmark-20260610T111415Z.json`) | | `by_caller.chain_timer` `eth_call` ≥40% reduction vs baseline | compare idle warm-up sample to `rpc-benchmark-20260610T111415Z.json` (`eth_call:chain_timer` 352/10s window) | **FAIL** — **0% reduction**; new 120s run reproduces **352** `eth_call:chain_timer` per 10s window | | `GET /v1/arena/timers` freshness ≤3s | `api_arena.rs` + smoke HTTP 200 during verify scripts | **PASS (proxy)** — `read_block_number` tracks Anvil head; **`polled_at_ms` not exposed** in JSON body (only in internal `ChainTimerSnapshot`) | | Attack/abuse tests (malformed aggregate, partial batch, metrics gaming) | — | **SKIP** — no aggregate decode path to exercise | ### How verified - `glab issue view 307 --comments` - Static review: `indexer/src/chain_timer.rs`, `sale_state.rs`, `rpc_http.rs` - `bash scripts/verify-cloud-postgres.sh` — **PASS** - Pre-built release indexer (`cargo build --release`) before smoke reruns (first smoke attempt timed out during release compile) - Native Postgres `:5433`, Anvil with `--code-size-limit 524288` ### Follow-ups 1. **Implement** per issue direction: `multicall.rs` + `aggregate3` batches in `poll_once` / `sale_state`; split batches if RPC payload limits hit (per issue comment). 2. Re-run `BENCHMARK_SCENARIO_SEC=120 bash scripts/benchmark-indexer-rpc-anvil.sh`, commit new `docs/indexer/benchmarks/*.json`, and update baseline table in `rpc-load-benchmark.md`. 3. Consider exposing `polled_at_ms` on `GET /v1/arena/timers` if freshness SLO is operator-facing. 4. Investigate `arena_wallet_stats_two_epochs_and_bonus_fields` flake in `verify-podium-live-anvil.sh` harness (blocks full green `INV-INDEXER-306-NO-REGRESSION`).
PlasticDigits commented 2026-06-14 05:39:45 +00:00 (Migrated from gitlab.com)

mentioned in merge request !488

mentioned in merge request !488
PlasticDigits commented 2026-06-14 06:56:06 +00:00 (Migrated from gitlab.com)

mentioned in commit c432cee3fe

mentioned in commit c432cee3fe1b58dcce2f2cc87bad8963aaf0dd44
PlasticDigits commented 2026-06-14 06:56:58 +00:00 (Migrated from gitlab.com)

mentioned in merge request !489

mentioned in merge request !489
PlasticDigits commented 2026-06-14 07:11:20 +00:00 (Migrated from gitlab.com)

mentioned in commit bd2d20e94b

mentioned in commit bd2d20e94b93865a5ac7e9e393a8de7dac615618
PlasticDigits (Migrated from gitlab.com) closed this issue 2026-06-14 07:11:20 +00:00
PlasticDigits commented 2026-06-15 02:27:09 +00:00 (Migrated from gitlab.com)

mentioned in merge request !503

mentioned in merge request !503
PlasticDigits commented 2026-06-28 05:40:33 +00:00 (Migrated from gitlab.com)

marked as related to #356

marked as related to #356
PlasticDigits commented 2026-06-28 05:40:33 +00:00 (Migrated from gitlab.com)

mentioned in issue #356

mentioned in issue #356
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
code/yieldomega#307
No description provided.