Skip to content

feat(pool/capacity): Capacity Planning Model Divergence Between Local Estimator and Global Coordinator #139 - #158

Merged
JamesEjembi merged 7 commits into
VeriNode-Labs:mainfrom
Mona-i:feature/capacity-planning-model-divergence-139
Aug 22, 2026
Merged

feat(pool/capacity): Capacity Planning Model Divergence Between Local Estimator and Global Coordinator #139#158
JamesEjembi merged 7 commits into
VeriNode-Labs:mainfrom
Mona-i:feature/capacity-planning-model-divergence-139

Conversation

@Mona-i

@Mona-i Mona-i commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

Capacity Planning Model Divergence Between Local Estimator and Global Coordinator

closes #139

Summary of changes

This PR implements the full capacity planning model divergence fix described in issue #139. Each shard node's local estimator now sends both raw measurements and its locally-computed non-linear estimate to the global coordinator, letting the coordinator detect and correct for model divergence.

Files added

File Purpose
\src/pool/capacity/model_linear.rs\ Simple weighted-average linear model used by the global coordinator (equal CPU/memory/bandwidth weights)
\src/pool/capacity/model_nonlinear.rs\ GC-pause and NUMA-aware non-linear model used by the local estimator
\src/pool/capacity/local_estimator.rs\ Per-node estimator (1 s interval) — sends raw measurements + both model estimates
\src/pool/capacity/global_coordinator.rs\ Aggregator (5 s sync) — applies correction factor; warns + switches to conservative estimate after 3 consecutive divergent cycles
\src/pool/capacity/mod.rs\ Module wiring and re-exports
\src/pool/mod.rs\ Top-level pool module entry point
\ ests/capacity_planning_divergence_test.rs\ Integration test (GC-pressure simulation + invariant checks)

Technical invariants satisfied

  • Local estimator update interval: 1 s (\LOCAL_ESTIMATOR_INTERVAL_S = 1)
  • Global coordinator sync interval: 5 s (\GLOBAL_COORDINATOR_SYNC_INTERVAL_S = 5)
  • Divergence tolerance: ±10% (\DIVERGENCE_TOLERANCE = 0.10)
  • Overcommit ratio: max 1.2× (\MAX_OVERCOMMIT_RATIO = 1.2)
  • NUMA node count: up to 8 (penalty capped at \MAX_NUMA_PENALTY = 0.35)

Correction formula

\
capacity_global = capacity_local * (1 - |estimate_local - estimate_linear|)
\\

After 3 consecutive cycles with divergence > 10%, a \CapacityModelDivergenceWarning\ is logged and the coordinator uses \min(estimate_local, estimate_linear)\ until convergence.

GC-pause modeling

\model_nonlinear.rs\ reduces available capacity by \gc_pause_ms / 1000\ for the 10 s following each pause (\GC_PENALTY_WINDOW_S = 10).

Testing / validation performed

  • \ ests/capacity_planning_divergence_test.rs\ covers:
    • GC-pressure simulation: 100 ms pause every 5 s stays within 10% divergence tolerance
    • Correction factor arithmetic
    • 3-consecutive-cycle divergence warning path
    • Conservative estimate after warning
    • Convergence clears conservative mode + emits \ModelConverged\
    • 8 NUMA nodes reduce local but not linear estimate
    • Technical-invariant constants match the issue spec
  • Unit tests inside each module verify individual model behaviour
  • No pre-existing files were modified except \src/lib.rs\ (added \pub mod pool;) and \Cargo.toml\ (added [[test]]\ entry)

Mona-i added 7 commits August 20, 2026 00:15
Light clients tracking multiple finality gadgets with a single fixed sync
cadence let slow chains fall behind and fast chains thrash, so a chain's
cached sync-committee view drifts out of date and finality stalls or, worse,
finalizes on a stale committee (issue VeriNode-Labs#136). This adds a cross_chain module
that derives every timing bound per chain from its own block time.

- types: ChainConfig with per-chain sync timeout = max(3 * block_time_ms,
  60_000), sync interval = block_time_ms / 4, and clock-drift budget =
  500 ms * finality_hops, plus operational constants for every VeriNode-Labs#136 invariant.
- committee_sync: per-chain sync scheduling, exponential retry backoff
  (1s -> 2s -> 4s -> ... capped at 30s), and drift detection by sampling
  staleness or observed clock skew.
- finality_verifier: 2/3+1 committee-weight threshold with a 1.5x
  sync-timeout grace period that withholds finalization while sync drift is
  detected, so a temporarily skewed committee view cannot finalize early.
- header_cache: bounded cache of the 256 most recent headers per chain.
- light_client: LightClientRegistry tying the above together and exporting
  a chain_finality_lag_ms gauge per connected chain.

All arithmetic is integer-only and saturating and the module is dependency-free
so it compiles under no_std (WASM) and is shared verbatim by off-chain relayers
and monitoring agents.

Adds an integration test simulating a 2s chain and a 15s chain sharing one
light client with 800ms injected relay latency, asserting finality lag stays
well under the 10s target on both (1.0s and 3.75s respectively), alongside
unit tests across every submodule.

Refs VeriNode-Labs#136
…rection (VeriNode-Labs#139)

- Add src/pool/capacity/model_linear.rs: weighted-average linear capacity model
  used by the global coordinator (equal CPU/memory/bandwidth weights).

- Add src/pool/capacity/model_nonlinear.rs: GC-pause and NUMA-aware non-linear
  model used by the local estimator. GC-pause penalty: reduces available
  capacity by gc_pause_ms/1000 for the next 10 s after each pause. NUMA penalty:
  +5% effective memory utilisation per extra NUMA node, capped at 35% (8 nodes).

- Add src/pool/capacity/local_estimator.rs: per-node estimator (1 s interval)
  that samples raw measurements, runs both models, and forwards the raw
  measurements plus both estimates in a LocalEstimatorSnapshot to the coordinator.

- Add src/pool/capacity/global_coordinator.rs: aggregator (5 s sync interval)
  that applies capacity_global = capacity_local * (1 - |diff|) as the correction
  factor. If divergence exceeds 10% for 3 consecutive cycles it emits a
  CapacityModelDivergence warning and switches to the conservative (lower)
  estimate. Emits ModelConverged when divergence drops back within tolerance.

- Add src/pool/capacity/mod.rs and src/pool/mod.rs: module wiring and re-exports.

- Register pub mod pool in src/lib.rs with a doc comment matching project style.

- Add tests/capacity_planning_divergence_test.rs: integration test covering the
  GC-pressure simulation (100 ms pause every 5 s stays within 10% tolerance),
  divergence correction factor, three-consecutive-cycle warning, conservative
  estimate after warning, convergence clears conservative mode, NUMA penalty
  reduces local but not linear estimate, and overcommit-ratio constant.

- Register [[test]] entry in Cargo.toml.
…nce-139

Resolved formatting-only conflicts in:
- Cargo.toml: kept capacity_planning_divergence_test entry + accepted
  origin/main's [lints.clippy] section and removal of arbitrary dep
- src/lib.rs: kept pub mod pool (issue VeriNode-Labs#139) alongside origin/main changes
- src/cross_chain/{committee_sync,finality_verifier,header_cache,light_client}.rs:
  accepted origin/main rustfmt wrapping (no logic change)
- tests/light_client_finality_skew_test.rs: accepted origin/main rustfmt
  wrapping including div_ceil refactor (no logic change)
- global_coordinator.rs: move extern crate alloc to top, replace
  alloc:: qualified paths with use imports, wrap sync_node signature
  to fit within 100-char max_width
- local_estimator.rs: prefix unused idle_inputs parameter with _,
  fix double-space in comment alignment
- model_nonlinear.rs: minor whitespace normalisation
- mod.rs: sort NonLinearInputs re-export alphabetically within use block
- tests/capacity_planning_divergence_test.rs: merge duplicate use groups
  from same crate, wrap long assert message with line continuation,
  use LocalEstimatorSnapshot directly (no full-path qualifier)
- committee_sync.rs: revert to origin/main exact bytes — rustfmt
  right-aligns the trailing comment in backoff_defers_the_next_sync
  test; our merge resolution broke that alignment
- local_estimator.rs: remove double space before inline comment on
  secs_since_gc field (rustfmt normalises to single space)
- tests/capacity_planning_divergence_test.rs: reorder use blocks so
  pool comes before pool::capacity, single space after max 1.2x comment
clippy::unnecessary_map_or fires on map_or(false, |s| s.field) —
replace with is_some_and(|s| s.field) in GlobalCoordinator::is_conservative
@JamesEjembi
JamesEjembi merged commit c236a62 into VeriNode-Labs:main Aug 22, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Capacity Planning Model Divergence Between Local Estimator and Global Coordinator

2 participants