Repository navigation
[zero] feat(zebra-consensus): time each transaction check separately - #74
Merged
Merged
Conversation
The `checks` phase timer wrapped every script, signature and proof check of a
transaction, plus their waits on the shared batch verifiers, in one sample, so
the largest cost in block verification could not be attributed to any of them.
`zebra.consensus.transaction.check_duration_seconds{check, request}` records
each check's wall time from first poll to completion:
script transparent input scripts, one join per transaction
(script cache hits emit no sample)
sprout_proof one per JoinSplit
sprout_sig the JoinSplit signature
sapling the Sapling bundle batch item (proofs and signatures)
orchard the Orchard or Ironwood bundle batch item
state the mempool-only nullifier and anchor check
The checks run concurrently, so the kinds overlap rather than add up to the
`checks` phase. Each wall time includes the wait for a shared batch verifier
or the script thread pool; subtracting `zebra.consensus.batch.duration_seconds`
for the matching verifier isolates queue wait from batch work.
A new `phase="prepare"` sample on `zebra.consensus.transaction.duration_seconds`
covers the sighash and transaction id digests, bundle extraction and check
construction between `utxo_fetch` and `checks`, which were untimed.
The uncacheable script path now joins its per-input checks the way the
cache-miss path does, so every path emits one `script` sample per transaction,
and the `FromIterator` impl it used is gone. Fail-fast semantics are unchanged:
the first error ends the set and drops the rest.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
It modifies consensus-critical transaction verification orchestration (even if primarily for metrics), so a final human review is warranted to confirm semantics and performance characteristics remain unchanged.
Pull request overview
This PR enhances Zebra’s transaction-verification observability by adding finer-grained timing metrics inside zebra-consensus, enabling operators to separate script verification costs from batch-verifier queueing and proof/signature work in production.
Changes:
- Adds a new per-check histogram
zebra_consensus_transaction_check_duration_seconds{check,request}by wrapping each async check future with its label and wall-time measurement. - Extends the existing per-transaction duration metric with a new
phase="prepare"timing segment to cover digest/extraction/check-construction work. - Updates script-check aggregation so every path emits a single
check="script"sample per transaction, and adds unit tests for labeling + fail-fast behavior.
File summaries
| File | Description |
|---|---|
| zebra/zebra-consensus/src/transaction.rs | Introduces per-check timing wrapper in AsyncChecks, adds prepare phase timing, and labels individual verification checks. |
| zebra/zebra-consensus/src/transaction/tests.rs | Adds a unit test validating per-check labeling and fail-fast semantics without waiting on pending checks. |
| CHANGELOG.md | Documents the new per-check metric and the new prepare phase on the existing transaction duration metric. |
Review details
- Files reviewed: 3/3 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
The
checksphase ofzebra_consensus_transaction_duration_seconds(#15) is one timer around every script, signature and proof check of a transaction and their waits on the shared batch verifiers. On mainnet it is ~99% of per-transaction verification time, and nothing separates script work from proof batches, so #45 (script cache) and #46 (UTXO overlap) cannot be evaluated from production metrics.What
zebra_consensus_transaction_check_duration_seconds{check,request}: one sample per check future, wall time from first poll to completion.checkis one ofscript,sprout_proof,sprout_sig,sapling,orchard,state(mempool only);requestisblockormempool. The checks run concurrently, so the kinds overlap rather than sum tochecks. Subtractingzebra_consensus_batch_duration_seconds{verifier}isolates queue wait from batch work.phase="prepare"on the existing metric: sighash and txid digests, bundle extraction and check construction, previously untimed.AsyncChecks::pushtakes the label andchecktakes the request label and records. The uncacheable script path now joins its per-input checks like the cache-miss path, so every path emits onescriptsample per transaction, and theFromIteratorimpl is gone. Fail-fast semantics are unchanged.Verification
cargo fmtandcargo clippy -p zebra-consensus --all-targets -- -D warnings: cleancargo nextest run -p zebra-consensus: 147 passedcargo nextest run -p zebrad --lib mempool: 76 passed[metrics] endpoint_addrset, renders the new series with the expected labels. One orchard-deshielding transaction gavecheck="orchard"108.7 ms andcheck="state"76 us on the mempool path,check="orchard"108.2 ms on the block path, andphase="checks"equal to the orchard time on both (max of the concurrent kinds, as documented).phase="prepare"on the mempool path recorded 726 ms, the one-time halo2 verifying-key load on first use in that process; the block path's was 0.19 msReading the series
zebrad's exporter renders histograms as summaries (no buckets), so use
rate(_sum)/rate(_count)percheck; the quantile series are sliding-window and not comparable across nodes.🤖 Generated with Claude Code