Skip to content

[zero] feat(zebra-consensus): time each transaction check separately - #74

Merged
aphelionz merged 2 commits into
mainfrom
zero/consensus-check-timers
Sep 7, 2026
Merged

aphelionz merged 2 commits into
mainfrom
zero/consensus-check-timers

Conversation

@aphelionz

Copy link
Copy Markdown
Member

Why

The checks phase of zebra_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

  • New zebra_consensus_transaction_check_duration_seconds{check,request}: one sample per check future, wall time from first poll to completion. check is one of script, sprout_proof, sprout_sig, sapling, orchard, state (mempool only); request is block or mempool. The checks run concurrently, so the kinds overlap rather than sum to checks. Subtracting zebra_consensus_batch_duration_seconds{verifier} isolates queue wait from batch work.
  • New phase="prepare" on the existing metric: sighash and txid digests, bundle extraction and check construction, previously untimed.
  • AsyncChecks::push takes the label and check takes the request label and records. The uncacheable script path now joins its per-input checks like the cache-miss path, so every path emits one script sample per transaction, and the FromIterator impl is gone. Fail-fast semantics are unchanged.
  • Unit test for the labelled wrapper and for fail-fast without waiting on a pending check.
  • Root CHANGELOG entry under Unreleased.

Verification

  • cargo fmt and cargo clippy -p zebra-consensus --all-targets -- -D warnings: clean
  • cargo nextest run -p zebra-consensus: 147 passed
  • cargo nextest run -p zebrad --lib mempool: 76 passed
  • Regtest scrape of the new series: the regtest harness spend scenario against this build, with [metrics] endpoint_addr set, renders the new series with the expected labels. One orchard-deshielding transaction gave check="orchard" 108.7 ms and check="state" 76 us on the mempool path, check="orchard" 108.2 ms on the block path, and phase="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 ms

Reading the series

zebrad's exporter renders histograms as summaries (no buckets), so use rate(_sum)/rate(_count) per check; the quantile series are sliding-window and not comparable across nodes.

🤖 Generated with Claude Code

Mark Henderson and others added 2 commits September 7, 2026 10:59
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>
Copilot AI lite review requested due to automatic review settings September 7, 2026 15:18

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔵 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.

@aphelionz
aphelionz merged commit dee13c3 into main Sep 7, 2026
23 checks passed
@aphelionz
aphelionz deleted the zero/consensus-check-timers branch September 7, 2026 15:37
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