test(simulation): halve sim wall time (31→15.6 min median)

Halves test-simulation wall time in CI: the median goes from 31.2 to 15.6 min (−50%), with no coverage dropped. Every change is either gated to mocknet builds or touches only the test harness or CI config, so non-mocknet consensus and bifrost behavior are unchanged.

Results

CI, before vs after. Successful runs only. "Before" is every other branch's sim since !5147 (merged) landed.

Before After (final branch) Change
All runners median 31.2 min (27.0-57.9, n=30) median 15.6 min (15.4-17.4, n=5) −15.6 min (−50%)
Finland median 33.3 min (n=10) 17.1 min (n=1) −49%
harry median 31.9 min (n=9) 15.4 / 15.6 / 15.4 min −52%
hermonie median 27.9 min (n=11) 17.4 min (n=1) −38%
Original baseline: job 16972355155, Finland, !5166 (merged) 29.0 min 17.1 min (Finland) −41%

Local A/B. Same host, runs interleaved, CI-equivalent build (golang 1.26 bookworm container driving the host docker daemon), PARALLELISM as configured for CI. This is the least noisy comparison.

Before (develop) After (final branch) Change
Whole run 25.4 / 25.6 min 13.7 / 13.5 min −47%
Sim phase (actors) 1411 / 1439s 729 / 748s −48%

Where the time came from. Baseline CI job 16972355155 compared with the latest Finland (16983971920) and harry (16987040973) jobs.

Phase Before After Main changes
Setup (job start → sim start) 328s 106-199s 5, 6, 10, 12
Before the actors start 128s 101-144s 3, 7, 11
Bootstrap 79s 46-47s 1
Swaps + memoless swaps 418s 183-214s 1, 8
Memoless refunds 74s 35-37s 1, 8
Churn (both) 370s 222-246s 4, 9
Ragnarok 162s 18-22s 2
Sim phase total 1403s 809-818s −42%

Progression (CI job time):

  • 29.0 min: baseline
  • 22.5-22.9 min: changes 1-3
  • 21.2 min: change 4
  • 20.6 min: change 5
  • 15.4-17.1 min: changes 6-8
  • 15.4-17.4 min: changes 9-12

Reliability. This MR's sims failed 4 of 14 times (29%), against 18 of 48 (38%) on other branches over the same period. None of the four traces to a change that is still in the branch: one registry-auth infra error, one run with the since-dropped faster-Monero-mining change, and two hits of the pre-existing refund bug #3383 (see Related). The latest merged-result pipeline, run against develop 6d14167e98, passed every job on the first attempt.

Runner time. About 15 min saved per sim job. At the recent rate (~40 sim jobs a day on other branches) that is roughly 10 runner-hours a day.

Caveats.

  • Five successful CI runs have the final branch: three on harry, one on Finland, one on hermonie.
  • CI run time varies by about ±30s, so the later 20-40s changes can't each be shown in CI alone. They are backed by the local A/B and per-phase timings. The overall halving is far outside that noise.

Changes

Mocknet-gated product code. Non-mocknet builds are unchanged.

  1. POL inbound confirmations 15 → 3 (69f4a3ae65, bifrost/pkg/chainclients/evm, build-tag constant). Mocknet bor seals a block every 2s (--dev.period 2) and never reorgs. At 15 confirmations every POL inbound waited ~30s, and POL actors took 39-49s where other chains took 5-15s. 3 matches BSC, so inbounds still go through a pre-confirmation observation before the final one. THORChain does not enforce the finalise gap.
  2. Genesis XMR monovault keygen at block 64 instead of 128 (a4ce38ef32). Mocknet builds read XMR_MONOVAULT_GENESIS_DELAY_BLOCKS, which defaults to 128, so regtest and its exports are unchanged. Compose sets 64 in the shared thornode-environment, so every validator replays the same genesis. bifrost is scanning THOR blocks by about block 6. The old delay held the DAG start ~47s after funding finished.
  3. Pipelined EVM contract deploys (6b55730d0f, build/scripts/evm/evm-tool.py, deploy action only). Thornode genesis waits for the router deploys, and each chain used to wait a block per transaction (up to ~26s on BASE's 3s blocks). The setup transactions now go out back to back in the original nonce order and are confirmed together, so the contract addresses are unchanged: token at admin nonce 1, router at 3, aggregator at 4. The token transfers sent before the token is mined take their target from the CREATE formula, checked against rlp for 304 nonces, and use a fixed gas limit. Every receipt is checked for success and for the expected contract address. Deploys went from 26s to 6-7s, and thornode start → healthy from 36s to 15s in CI.

Mocknet compose and CI configuration 4. FUND_MIGRATION_INTERVAL 30 → 15 (26c60adaaa). Each churn ran three 30-block migration rounds with ~30 idle blocks between one round being observed and the next. Since ca561d0c, a chain whose previous migrate is still unobserved skips the round, so 15 still exercises multi-round migration: the skip fired 11-27 times per run, against 2 at 30. Runs at 15 showed no stale-migrate retries, sequence mismatches or missing target vaults. The 10 → 30 bump in 7e1c2cc4 predates that per-chain gate. 5. Base images built while Go compiles (3888e73ff1, Makefile). The runtime image (apt and pip in an empty dind), the monero image and the XMR signer pulls need no Go binaries, but they ran one after another after the ~100s native build. They now run alongside the build and alongside each other, and the three thin binary images build in parallel. On harry, make → thornode up went from 3:19 to 1:49; on Finland, from 4:03 to 1:56. 6. Dependency-proxy probe uses docker buildx imagetools inspect (4e54fd30d0, .dind-registries). docker manifest inspect golang:1.26.6 took 16-32s in every dind job (test-simulation, generate, build-thornode). imagetools resolves the same manifest in 1-3s and fails the same way for a missing tag, and every job that uses the probe ships buildx. 8. PARALLELISM 4 → 8 (034bc8591a). At 4 the swap stage kept every slot busy (95%), and the memoless-refund, inactive-vault-refund and ragnarok stages are slot-bound too. At 8 the actor phase went from 910s to 717s on Finland. 10. Base images pulled before the L1 chains; two Go builds in parallel (f5e898982c). With ~16 L1 image pulls running at the same time, the runtime image's debian base took ~60s on the critical path, against ~4s on its own. Warm Go builds at 8 CPUs go from 44s to 26s when run in parallel. This helps where pulls compete for bandwidth (Finland); harry, where the Go build is the critical path, showed no change.

Simulation harness only 2. Ragnarok stage votes FundMigrationInterval=5 (2f5f99e760). checkPoolRagnarok only acts on interval boundaries, so each pool took a fixed ~39s, and gas pools behind a token pool twice that. Churn migration has finished by this stage. The stage votes from every active node, waits for the mimir to apply, then runs the unchanged pool actors and Ragnarok-Verify. features.NewMultiNodeMimirActor is exported for this; its op now ends with Continue so callers can append ops, and existing callers behave the same. 7. XMR and SOL users funded without serial waits (914c37f7eb). XMR funding ran inline in the per-chain loop while it waited for faucet outputs to unlock, which held back POL and ZEC funding by ~30s. SOL funded users one at a time, ~13s each, waiting for finalized. XMR now runs in its own goroutine like the other chains, and SOL funds all users concurrently (SOL transfers have no account nonce). 9. Churns scheduled 8 blocks ahead instead of 20 (d2f7a2683f). The CHURNINTERVAL votes land 1-2 blocks after they are sent (all 8 churns in four runs), so each churn sat idle ~25s. The schedule now waits for the vote to apply and warns if it has not by the scheduled height. It does not reschedule, so it cannot trigger a second churn. 11. EVM funding sent without per-transfer receipt waits (095e362eb2). The sim's EVM client waited ~4s for each funding transfer's receipt, and at P=8 POL (16 transfers) was the last chain funded. Funding now sends every transfer back to back (the pending nonce keeps them consecutive), then waits for all receipts and fails on any revert. The pre-actor phase went from 82-98s to 66s locally.

Tried or investigated, not changed

  • Faster Monero mining (XMR_BLOCK_TICK_SECONDS 2 → 0.5): dropped. It saved ~35s, but 2 of 4 runs failed. On CI Finland the XMR scanners fell behind and caught up in bursts while the runner stalled; locally XMRSpentRefVerify timed out. Four times the blocks means four times the scanning work for the ~11 containers that scan XMR.
  • XMR observation confirmations 10 → 3 (mocknet-gated): dropped. It sped up the early stages (XMR coverage 68s → 38s, swaps 218s → 163s), but it broke XMR migration. Observed inbounds were not yet spendable, so outbounds hit "insufficient xmr balance … locked", and one XMR migrate's finalized observation stalled for ~10 minutes. The retiring XMR vault blocked the next churn and the run ended in a solvency mismatch. Doing this safely needs unlock-aware spending in bifrost or THORChain.
  • PARALLELISM 12. The swap stage gained 22s, but funding 12 users cost 24s up front, a net of zero. It stays at 8.
  • Overlapping other stages with the churn. Memoless refunds must run before every churn (#2839 Section B). Inactive-vault refunds set HaltChurning, which would block BondYield's churn. Swaps during a migration risk outbounds failing on insufficient funds (see the comment in migrateFunds). XMR coverage withdraws half the XMR LP, which would move the pool under the swap stage's expected-output checks.
  • A migration interval below 15. The per-chain pending gate already skips chains at 15, so a lower interval would save only ~10s per churn.
  • ETH migrate confirmations are value-based (the 1-conf-value rule) and now set when each churn ends. That is real coverage, so it is left alone.

Two pre-existing failures surfaced while measuring. Both are filed and fixed in separate MRs, and neither is caused by this branch:

  • #3383: a refund out of an inactive UTXO vault holding only the inbound UTXO cannot be signed, because bifrost reserves amount + MaxGas while THORChain only deducted the outbound fee. This shows up as InactiveVaultRefund-BTC timeouts on many branches. Fix: !5169 (merged).
  • #3384: a failed XMR migrate keysign is not retried until stale-migrate pruning, which shows up as BondYield-ChurnOut timeouts. Fix: !5170 (merged).

Measurement log

CI (merged-result pipelines; change numbers refer to the list above)

Job Runner Changes Result
16972355155 (!5166 (merged)) Finland none (baseline) pass, 29.0 min
16976239745 harry 1-3 fail: registry auth error pulling xmr-frost-signer (infra)
16976932084 harry 1-3 pass, 22.9 min
16977950842 Finland 1-4 pass, 22.5 min
16978922788 Finland 1-4 pass, 21.2 min
16980046022 Finland 1-5 + Monero tick (dropped) fail: runner stall during XMR catch-up
16980586521 harry 1-5 + Monero tick (dropped) pass, 18.0 min
16981955487 Finland 1-6 fail: InactiveVaultRefund-BTC (#3383)
16982583118 Finland 1-6 pass, 20.6 min
16983971920 Finland 1-8 pass, 17.1 min
16984264379 harry 1-8 pass, 15.4 min
16985112682 harry 1-11 pass, 15.6 min
16986677618 harry 1-12 fail: InactiveVaultRefund-BTC (#3383)
16987040973 harry 1-12 pass, 15.4 min
16987623192 hermonie 1-12, against develop 6d14167e98 pass, 17.4 min

Local

Run Changes Result Wall Sim phase
base1, base2 develop pass 25.4 / 25.6 min 1411 / 1439s
cand2 1-3 pass 20.0 min 1100s
exp15, exp15b, full1 1-4 pass 16.8-18.7 min 917-1027s
p8b, p8d 1-8 pass 14.3-14.8 min 770-785s
r3b 1-11 pass 13.7 min 729s
r4c 1-12 pass 13.5 min 748s

Local failures and their causes:

  • cand1: the SOL aggregator inbound was never observed after finalizing on the 59th of 60 polls.
  • full2: XMR migrate keysign frost_share timeout (#3384).
  • p8c, p12a: InactiveVaultRefund-BTC (#3383).
  • cpu05 and r4a: dropped experiments (Monero tick, XMR confirmations), described above.

🤖 Generated with Claude Code

Edited by Son of Odin

Merge request reports

Loading
Loading