Skip to content

perf(state): measure mined block admission timing - #781

Merged
evan-forbes merged 12 commits into
feat/optimistic-mined-block-inventoryfrom
perf/contextual-profile-milestone
Sep 7, 2026
Merged

perf(state): measure mined block admission timing#781
evan-forbes merged 12 commits into
feat/optimistic-mined-block-inventoryfrom
perf/contextual-profile-milestone

Conversation

@evan-forbes

@evan-forbes evan-forbes commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

Summary

  • measure the Tower readiness wait before buffered dispatch;
  • carry the post-readiness mined-block request instant into the inner state
    service;
  • measure buffered dispatch, prequeue checks, and queue-and-commit work
    separately;
  • carry the writer-channel send instant with each queued block;
  • measure all-block and mined-only writer queue duration when serialized
    processing starts.

Motivation

The earlier field profile measured 63.4 ms between semantic verification and
state queue admission. The writer-channel wait accounted for only about 0.3 ms
in the same data. These metrics measure both sides of that gap without relying
on differences between aggregate histogram quantiles.

Safety

This change records elapsed time on #748's existing mined-block admission path.
It does not change queue ownership, validation, branch selection, storage, or
publication.

The readiness timer starts before ready().await. The dispatch timestamp starts
after readiness succeeds and immediately before the buffered state call. The
writer timestamp starts immediately before the existing channel send.

Benchmark scope

This PR adds observability. It does not claim a runtime speedup. A local
before-and-after benchmark would not validate the change because the useful
result is the partition of the mined-block admission interval under fleet CPU
load. The state and consensus tests verify that the timestamps cross the
intended service boundaries. The next fleet run must compare the mined-only
readiness, dispatch, prequeue, queue-send, and writer-queue histograms on the
same block population.

Validation

  • cargo test -p zakura-consensus -p zakura-state --lib --locked on the perf(state): measure contextual verification phases #782 stack tip
    • 210 consensus tests passed
    • 520 state tests passed and 3 tests remained ignored
  • cargo check -p zakura-consensus -p zakura-state --all-targets --locked
  • cargo clippy -p zakura-state -p zakura-consensus --all-targets -- -D warnings
  • cargo fmt --all -- --check
  • git diff --check
  • ./scripts/changelog.py check

This PR adds docs/changelog/unreleased/781.md with an internal-only marker.

Stack

@evan-forbes
evan-forbes force-pushed the perf/contextual-snapshot-speedups branch from af21cea to 7b850da Compare August 23, 2026 04:43
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch from baf893d to 0a60c5b Compare August 23, 2026 04:43
@evan-forbes evan-forbes changed the title perf(state): split writer queue timing perf(state): measure mined block admission timing Aug 23, 2026
@evan-forbes
evan-forbes force-pushed the perf/contextual-snapshot-speedups branch from 7b850da to d0cd7e3 Compare August 23, 2026 08:11
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch 2 times, most recently from e0904a8 to cb86ca8 Compare August 23, 2026 16:46
@evan-forbes
evan-forbes marked this pull request as ready for review August 23, 2026 17:25
@evan-forbes
evan-forbes force-pushed the perf/contextual-snapshot-speedups branch from ebcd3aa to ef42546 Compare August 23, 2026 17:45
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch from cb86ca8 to da87178 Compare August 23, 2026 17:45
@evan-forbes
evan-forbes force-pushed the perf/contextual-snapshot-speedups branch from ef42546 to a0b7bbf Compare August 23, 2026 18:42
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch from da87178 to 267890e Compare August 23, 2026 18:42
@evan-forbes
evan-forbes force-pushed the perf/contextual-snapshot-speedups branch from a0b7bbf to ce9d250 Compare August 23, 2026 18:52
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch from 267890e to b8b1f4c Compare August 23, 2026 18:52
@evan-forbes
evan-forbes force-pushed the perf/contextual-snapshot-speedups branch from ce9d250 to e586448 Compare August 23, 2026 19:38
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch from b8b1f4c to bdf35bb Compare August 23, 2026 19:38
@evan-forbes
evan-forbes changed the base branch from perf/contextual-snapshot-speedups to feat/optimistic-mined-block-inventory August 23, 2026 19:38
@evan-forbes
evan-forbes changed the base branch from feat/optimistic-mined-block-inventory to main September 4, 2026 17:18
@evan-forbes
evan-forbes force-pushed the perf/contextual-profile-milestone branch from bbe0155 to 513e79b Compare September 4, 2026 18:03
@evan-forbes
evan-forbes changed the base branch from main to feat/optimistic-mined-block-inventory September 4, 2026 18:04
@evan-forbes
evan-forbes merged commit 5b6c306 into feat/optimistic-mined-block-inventory Sep 7, 2026
51 checks passed
@evan-forbes
evan-forbes deleted the perf/contextual-profile-milestone branch September 7, 2026 18:41
evan-forbes added a commit that referenced this pull request Sep 7, 2026
The mining API bump puts zakura-consensus, zakura-state and zakura-rpc in
the crates.io publish set. Their packaged lockfiles resolve the index
copies of zakura-header-chain, zakura-node-services, zakura-script,
zakura-network and zakura-utils, which still pin zakura-chain 6.3.0-rc0.
Cargo then selects two majors of zakura-chain side by side.

Patch-bump those five crates so they republish with the zakura-chain
7.0.0-rc0 requirement.

Drop the #781 fragment: #781 merged into this branch, it declares no
operator-visible change, and each PR owns one fragment.
evan-forbes added a commit that referenced this pull request Sep 8, 2026
* feat(mining): advertise admitted blocks optimistically

* fix(mining): harden optimistic inventory

* fix(mining): correct optimistic inventory validation

* test(rpc): include workid in template fixture

* fix(mining): harden optimistic block inventory

* chore(rpc): regenerate OpenRPC artifact

Main added the checked-in `rpc_openrpc.rs` artifact and its staleness
check in #764. This branch edits the `getblocktemplate` and `submitblock`
doc comments that the artifact derives from, so `cargo xtask
check-rpc-artifacts` fails after rebasing onto main. Regenerate the
artifact so it matches the doc comments.

* fix(mining): require expected work before optimistic relay

* test(mining): allow safe committed inventory fallback

* fix(mining): address final review hardening

* docs(mining): clarify hardening invariants

* fix(mining): isolate server templates from proposals

* fix(mining): harden optimistic block inventory

* chore(mining): bump public API crate versions

* chore(rpc): bump major version for mining API

* ci(semver): patch broken tinyvec release

* perf(state): measure mined block admission timing (#781)

* fix(state): measure writer queue directly

* perf(state): measure mined block admission timing

* fix(state): import writer timing clock

* fix(state): make admission metrics composable

* chore(crates): cascade the zakura-chain 7.0.0-rc0 bump

The mining API bump puts zakura-consensus, zakura-state and zakura-rpc in
the crates.io publish set. Their packaged lockfiles resolve the index
copies of zakura-header-chain, zakura-node-services, zakura-script,
zakura-network and zakura-utils, which still pin zakura-chain 6.3.0-rc0.
Cargo then selects two majors of zakura-chain side by side.

Patch-bump those five crates so they republish with the zakura-chain
7.0.0-rc0 requirement.

Drop the #781 fragment: #781 merged into this branch, it declares no
operator-visible change, and each PR owns one fragment.

* perf(state): bound the prepared mined relay ancestor walk

check_prepared_mined_relay_eligibility_for_state collected the whole
ancestor iterator, which walks to genesis, so every submitted mined block
that hit the prepared candidate cache loaded every ancestor block body
into memory at once. block_is_valid_for_recent_chain_data reads only the
most recent POW_ADJUSTMENT_BLOCK_SPAN blocks, so take that many before
collecting.

* fix(state): keep a full orphan queue from stranding its own descendants

The orphan queue bound rejected every distinct block once the queue was
full, including a block whose parent the state already had. When the
queue filled with descendants of a missing block B, B was rejected, and
the drain that follows walks forward from B's parent hash, so it never
reached the descendants indexed under B's own hash. Nothing else empties
the queue while the chain is stalled: prune_by_height only removes blocks
at or below the finalized tip, and that tip cannot advance while no block
commits. QueueFull is retryable, so the syncer retried into the same
rejection until the node restarted.

Apply the bound only to blocks that must wait for an absent parent. A
block that can extend a chain now is admitted and drains in the same
call, so it does not occupy the bound.

* fix(mining): bound optimistic relay state and keep the committed-tip fallback

`optimistic_relay_blocked_parents` held two kinds of entry with different
lifetimes and cleared neither. Every optimistically relayed block retained one
hash for the lifetime of the process, and a reconsidered parent stayed
ineligible for optimistic relay forever.

Split it so each half is bounded by its own rule. Reservations record the
height that took the parent's slot and are pruned once that height is
finalized, because a reservation is only read while its parent is the best tip.
Invalidations count how many are outstanding per hash, so a confirmed
reconsideration releases the parent while an invalidation issued after that
reconsideration was requested stays in force.

A successful early broadcast also marked the tip as gossiped, exactly like a
committed one. Early inventory advertises a hash whose body the node cannot
serve yet, so a peer can exhaust `PENDING_BLOCK_WAIT` and give up on it; the
mark then suppressed the committed-tip fallback that would prompt that peer
again if the committed broadcast failed. Only a committed broadcast marks now,
which loses nothing, because a block that commits always sends `Committed`.

* fix(state): keep the orphan queue bound hard during checkpoint write lag

The capacity exception admitted any block whose parent passed `can_fork_chain_at`.
While the write task is still committing checkpoint blocks that predicate accepts
the durable finalized tip, which lags the last hash sent to the write task for as
long as checkpoint writes are in flight. Nothing drains a block queued under the
lagging tip: it does not complete the handoff condition, and the eventual handoff
walks forward from the last hash sent, not from the tip that was durable when the
block arrived. Height pruning does not run in that state either, so a caller could
grow the nominally bounded queue for the length of the checkpoint sync.

Replace the predicate with one that asks whether the rest of this call drains the
block. After the handoff that is still `can_fork_chain_at`, because
`send_ready_non_finalized_queued` walks forward from the parent. Before it, only a
child of a durably written last sent hash qualifies, which is exactly the block
that completes the handoff condition, so the handoff drains it on the same call and
fires at most once in the life of the node.

Either way the exception overshoots the bound by one entry and no more.

* fix(mining): bound submission work and reject stale relay admission

Reject mined orphans before transaction verification and state queueing. Keep per-hash RPC ownership across cancellation and bound contextual writer bodies until publication. Block early relay behind earlier writes or invalidations. Avoid duplicate ready-peer inventory and stale completion marks.

* fix(mining): withdraw rejected templates and validate recovery
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.

2 participants