Skip to content

Performance Benchmarks

Performance benchmarks are developer tools for measuring hot paths, catching regressions, and collecting release evidence. They complement tests and verification commands; they do not replace them.

Use this page when you need to answer one of these questions:

  • Did a code change make block execution, sync, snapshots, or state roots slower?
  • Which subsystem is responsible for a measured slowdown?
  • Is a candidate state-root engine fast enough to keep evaluating?
  • Does a release candidate still fit the current v1 performance budget?
  • Which benchmark report should be attached to a release or optimization decision?

Benchmarks deliberately run outside the normal fast test loop. Most runners create isolated database schemas, seed synthetic or replay-shaped data, write a JSON report under benchmark-results, and drop their schema unless a KEEP_DB flag is set.

Quick Selection

GoalCommandUse when
Run the release performance matrixnpm run benchmark:v1-readinessPreparing a v1 release candidate or checking broad regressions.
Check the release matrix against budgetsnpm run benchmark:v1-readiness:checkA readiness report has been produced and needs pass/fail evaluation.
Measure block commit hot pathsnpm run benchmark:block-executionTouching handlers, indexes, state leaves, SMT application, or block finalization.
Measure PostgreSQL query/write/storage hot pathsnpm run benchmark:db-performanceAdding indexes, changing table shape, or investigating DB-heavy regressions.
Compare iden3 and Nervos state-root enginesnpm run benchmark:state-root-enginesEvaluating state-root activation, replay, snapshots, and sidecar recovery.
Measure state-root candidate apply/rebuild behaviornpm run benchmark:state-root-engineTesting an engine boundary before connecting it to real block execution.
Measure SMT adapter behaviornpm run benchmark:smtTuning persistent iden3 storage, cache behavior, or flush batching.
Evaluate future SMT candidatesnpm run benchmark:smt:evaluationChecking deterministic vectors and the external candidate process contract.
Measure sync catch-upnpm run benchmark:sync-catchupChanging sync, payload backfill, peer selection, snapshots, or observer recovery.
Measure payload recovery writesnpm run benchmark:payload-backfillPayload phases dominate sync catch-up.
Measure local payload-store reads and statsnpm run benchmark:payload-storeChanging payload lookup, payload GC, payload stats, or export-like scans.
Measure archive snapshot export/verify/importnpm run benchmark:snapshotsChanging snapshot format, import staging, verification, or restore logic.
Measure public-index search strategiesnpm run benchmark:record-index-searchChanging record_indexes query plans or index design.
Measure signing/canonicalization costnpm run benchmark:signingChanging signatures, canonical JSON, key parsing, or signer caching.

package.json still contains benchmark:performance, but the referenced src/stability-test/benchmarks/benchmark-runner.ts file is not present in this workspace. Treat that command as legacy design until the runner is restored or removed.

How To Read Benchmark Reports

Prefer reports that explain where time went, not just how long the whole run took. The useful fields vary by runner, but most reports include a combination of:

  • config: workload size, profile, batch sizes, feature flags, and report directory.
  • timings: p50, p95, p99, average, min, and max latency for named phases.
  • throughput: blocks/sec, transactions/sec, payloads/sec, rows/sec, or mutations/sec.
  • storage: table and index size for hot database objects.
  • verification: root equality, replay checks, payload availability checks, or counter consistency checks.
  • recommendations: threshold-based hints from the runner.

When comparing reports:

  1. Compare the same command, profile, environment variables, branch, database, sidecar binary, and machine class.
  2. Prefer p95 and p99 for user-facing or operator-facing latency. Averages hide tails.
  3. Record dataset size. A 2,000-row smoke profile and a 1,000,000-row snapshot profile are different evidence.
  4. Treat fast local runs as development smoke only. Release gates need production-like hardware.
  5. Run correctness tests after performance work. A faster path is not useful if replay, state-root verification, or snapshot restore breaks.

Common mistakes:

  • comparing Windows laptop results with Linux CI or production-like hardware;
  • accepting a mean-only improvement while p95/p99 regresses;
  • changing indexes because one query improved while block writes slowed down;
  • treating a benchmark pass as consensus-safety evidence;
  • ignoring sidecar version, PostgreSQL configuration, or environment flags;
  • hiding stale cached counters behind faster operator endpoints.

V1 Readiness Matrix

The v1 readiness matrix is the broad benchmark gate. It runs the most important focused benchmarks together and writes one combined report.

Fast local smoke:

powershell
$env:NOOS_V1_READINESS_FAST="true"
npm run benchmark:v1-readiness
npm run benchmark:v1-readiness:check
Remove-Item Env:\NOOS_V1_READINESS_FAST

Full matrix:

powershell
npm run benchmark:v1-readiness
npm run benchmark:v1-readiness:check

The matrix currently runs:

  • build:state-root-sidecar-nervos
  • benchmark:block-execution for configured profiles
  • benchmark:state-root-engines
  • benchmark:snapshots
  • benchmark:sync-catchup
  • benchmark:payload-backfill
  • benchmark:state-root-persistent-sidecar

The runner writes reports under benchmark-results/v1-readiness. The checker uses test/benchmarks/v1-readiness/v1-performance-budgets.json unless NOOS_V1_BUDGET_FILE points somewhere else.

Useful matrix variables:

VariableMeaning
NOOS_V1_READINESS_FASTUses smaller smoke profiles when true. Do not use this for sign-off.
NOOS_V1_READINESS_LOG_DIRCombined report directory.
NOOS_V1_READINESS_BLOCK_PROFILESComma-separated block execution profiles.
NOOS_V1_READINESS_STATE_ROOT_PROFILESComma-separated state-root comparison profiles.
NOOS_V1_READINESS_SYNC_SCENARIOSComma-separated sync scenarios.
NOOS_V1_READINESS_SNAPSHOT_ROWSSnapshot fixture size.
NOOS_V1_READINESS_PAYLOAD_BACKFILL_RECORDSPayload-backfill fixture size.
NOOS_V1_READINESS_SIDECAR_LEAVESPersistent sidecar initial leaf count.
NOOS_V1_READINESS_SIDECAR_UPDATESPersistent sidecar update count.
NOOS_V1_READINESS_REPORTExisting report consumed by the checker.
NOOS_V1_READINESS_CHECK_OUTPUTBudget-check output path.

For operator-facing budgets and sign-off workflow, see V1 Performance Targets.

Execution And Storage Benchmarks

Block Execution

Run the default block execution benchmark:

powershell
npm run benchmark:block-execution

Common profiles:

powershell
$env:NOOS_BLOCK_EXEC_BENCH_PROFILE="small"
npm run benchmark:block-execution

$env:NOOS_BLOCK_EXEC_BENCH_PROFILE="large-block"
npm run benchmark:block-execution

$env:NOOS_BLOCK_EXEC_BENCH_PROFILE="many-indexes"
npm run benchmark:block-execution

Important variables:

VariableDefaultMeaning
NOOS_BLOCK_EXEC_BENCH_PROFILEmediumsmall, medium, large-block, many-indexes, or low-cardinality-indexes.
NOOS_BLOCK_EXEC_BENCH_BLOCKSprofile-dependentBlocks to build and execute.
NOOS_BLOCK_EXEC_BENCH_TXS_PER_BLOCKprofile-dependentTransactions per block.
NOOS_BLOCK_EXEC_BENCH_INDEXES_PER_RECORDprofile-dependentPublic indexes per encrypted-record transaction.
NOOS_BLOCK_EXEC_BENCH_PAYLOAD_SIZE_BYTESprofile-dependentSynthetic encrypted payload size.
NOOS_BLOCK_EXEC_BENCH_LOG_DIR./benchmark-results/block-executionReport directory.
NOOS_BLOCK_EXEC_BENCH_KEEP_DBfalseKeep the isolated schema for inspection.

The runner submits synthetic ADD_ENCRYPTED_RECORD transactions through the mempool, builds real blocks, and executes them through the normal executor. Watch these fields first:

  • blockCommitTotalMs: total commit cost.
  • executionHandlerMs: transaction handler work, including encrypted-record writes and public-index fanout.
  • executionStateLeafMaterializeMs: cost of materializing state_leaves.
  • executionStateSmtNetApplyMs and executionSmtFlushMs: SMT mutation and persistence cost.
  • mempoolAdmissionMs and executionAuthMs: signer, nonce, and authorization cost.
  • fanout.transactionTypes: which transaction types create state rows, unique state keys, and net SMT mutations.
  • summary.smt: whether SMT dominates block time.

Use the fanout report before changing the SMT backend. If one handler creates many unique state keys per transaction, the cost is real state fanout. If many same-key mutations collapse inside the block, batching is already helping and the next optimization may be handler shape, not tree implementation.

Database Performance

Run:

powershell
npm run benchmark:db-performance

Smoke and larger profiles:

powershell
$env:NOOS_DB_PERF_ROWS="1000"
$env:NOOS_DB_PERF_ITERATIONS="3"
$env:NOOS_DB_PERF_WRITE_ROWS="300"
npm run benchmark:db-performance

$env:NOOS_DB_PERF_ROWS="50000"
$env:NOOS_DB_PERF_ITERATIONS="20"
$env:NOOS_DB_PERF_WRITE_ROWS="5000"
npm run benchmark:db-performance

Important variables:

VariableDefaultMeaning
NOOS_DB_PERF_ROWS5000Synthetic rows for read-path scenarios.
NOOS_DB_PERF_ITERATIONS10Query repetitions per scenario.
NOOS_DB_PERF_WRITE_ROWS1000Rows used in write-throughput scenarios.
NOOS_DB_PERF_RESULT_LIMIT100Result limit for bounded reads.
NOOS_DB_PERF_LOG_DIR./benchmark-results/db-performanceReport directory.
NOOS_DB_PERF_KEEP_DBfalseKeep the isolated schema.
NOOS_DB_PERF_WRITE_DIAGNOSTICSfalseRun trigger/index write-cost diagnostics.

The report covers hot query plans, write throughput, table/index storage, and recommendations. Use it before adding indexes. An index that improves a rare query but slows encrypted-record writes or block execution can be a net loss.

When encrypted-record writes are slow, run:

powershell
$env:NOOS_DB_PERF_WRITE_DIAGNOSTICS="true"
npm run benchmark:db-performance

Interpret diagnostic variants this way:

  • trigger-group speedup means cached-counter maintenance is suspect;
  • all-trigger speedup with weak individual signals means cumulative trigger overhead is suspect;
  • secondary-index speedup means the index set should be audited against real query plans;
  • no meaningful speedup means the bottleneck is likely base row size, PostgreSQL settings, WAL/disk behavior, or the benchmark machine.

Run:

powershell
npm run benchmark:record-index-search

Important variables:

VariableDefaultMeaning
NOOS_RECORD_INDEX_BENCH_RECORDS10000Synthetic records to seed.
NOOS_RECORD_INDEX_BENCH_ITERATIONS20Query iterations.
NOOS_RECORD_INDEX_BENCH_PROFILEmixed_realisticSearch workload profile.
NOOS_RECORD_INDEX_BENCH_RESULT_LIMIT100Result limit.
NOOS_RECORD_INDEX_BENCH_RESULT_OFFSET0Result offset.
NOOS_RECORD_INDEX_BENCH_WRITE_RECORDS5000Write-side comparison rows.
NOOS_RECORD_INDEX_BENCH_LOG_DIR./benchmark-results/record-index-searchReport directory.
NOOS_RECORD_INDEX_BENCH_KEEP_DBfalseKeep the isolated schema.

This benchmark compares the current record_indexes index set, targeted (bucket_id, key, typed_value) indexes, and a benchmark-only normalized-key prototype. Use it with Record Public Indexes when deciding whether a public-index query deserves schema work.

Signing

Run:

powershell
npm run benchmark:signing

Important variables:

VariableDefaultMeaning
NOOS_SIGNING_BENCH_ITERATIONS10000Iterations for small-payload operations.
NOOS_SIGNING_BENCH_LARGE_PAYLOAD_BYTES65536Synthetic large encryptedPayload size.
NOOS_SIGNING_BENCH_LOG_DIR./benchmark-results/signingReport directory.

The benchmark separates PEM parsing, canonical JSON serialization, native Node/OpenSSL Ed25519 signing/verification, signObject and verifyObjectSignature, cached signature paths, and full transaction sign/verify paths.

If native Ed25519 with pre-parsed keys is fast but signObject is slow, focus on key-object caching and canonicalization before considering a crypto backend replacement. Signature key caching is process-local and stores parsed Node KeyObjects only. It does not store signatures, transaction bodies, plaintext, DEKs, or payload bytes.

State-Root And SMT Benchmarks

State-root benchmarks are especially sensitive to engine version and sidecar binary. Record the active root engine, activation settings, platform, and sidecar build when comparing results.

State-Root Engine Comparison

Run:

powershell
npm run benchmark:state-root-engines

Smoke and multi-profile examples:

powershell
$env:NOOS_STATE_ROOT_COMPARE_PROFILES="small"
npm run benchmark:state-root-engines

$env:NOOS_STATE_ROOT_COMPARE_PROFILES="medium,large-block,many-indexes"
npm run benchmark:state-root-engines

Important variables:

VariableDefaultMeaning
NOOS_STATE_ROOT_COMPARE_PROFILESmediumComma-separated small, medium, large-block, or many-indexes.
NOOS_STATE_ROOT_COMPARE_BLOCKSprofile-dependentBlocks per engine/profile pair.
NOOS_STATE_ROOT_COMPARE_TXS_PER_BLOCKprofile-dependentTransactions per block.
NOOS_STATE_ROOT_COMPARE_INDEXES_PER_RECORDprofile-dependentPublic indexes per encrypted-record transaction.
NOOS_STATE_ROOT_COMPARE_PAYLOAD_SIZE_BYTESprofile-dependentSynthetic payload size.
NOOS_STATE_ROOT_COMPARE_LOG_DIR./benchmark-results/state-root-enginesReport, archive, and checkpoint directory.
NOOS_STATE_ROOT_COMPARE_KEEP_DBfalseKeep isolated schemas.
NOOS_STATE_ROOT_COMPARE_SKIP_SNAPSHOTSfalseSkip archive snapshot export/verify/import timings.
NOOS_STATE_ROOT_COMPARE_SKIP_RECOVERYfalseSkip Nervos sidecar restart/checkpoint recovery timing.
NOOS_STATE_ROOT_COMPARE_NERVOS_SIDECAR_COMMANDplatform defaultOverride the Nervos sidecar binary.

The benchmark executes the same workload with iden3-v1 and nervos-smt-v2. It compares block commit p95, state-root apply p95, replay verification, snapshot export/verify/import, and Nervos supervised sidecar restart recovery.

Build the sidecar first if needed:

powershell
npm run build:state-root-sidecar-nervos

Use deltas as directional evidence. They are not a production activation gate by themselves. Activation decisions also need replay verification, snapshot verification, operator recovery drills, and protocol-version review.

SMT Adapter

Run:

powershell
npm run benchmark:smt

Important variables:

VariableDefaultMeaning
NOOS_SMT_BENCH_LEAVES1000Unique leaves inserted.
NOOS_SMT_BENCH_UPDATES1000Existing leaves updated.
NOOS_SMT_BENCH_DELETES100Leaves deleted.
NOOS_SMT_BENCH_PROOF_SAMPLES10Proofs generated.
NOOS_SMT_BENCH_INCLUDE_UNBUFFEREDfalseAlso run persistent storage without buffered writes.
NOOS_SMT_BENCH_CACHE_MAX_ENTRIES250000Max PostgreSQL SMT adapter cache entries.
NOOS_SMT_BENCH_LOG_DIR./benchmark-results/smtReport directory.
NOOS_SMT_BENCH_KEEP_DBfalseKeep the isolated schema.

Compare persistent_buffered with in_memory. If persistent storage is much slower, inspect PostgreSQL cache misses, state_smt_kv reads, and flush behavior. If they are close, the dominant cost is likely iden3 traversal, hash-to-field conversion, or proof work.

Production SMT flushes also honor NOOS_SMT_FLUSH_BATCH_SIZE, default 1000. Tune it only after measuring flushMs.

NOOS_SMT_CACHE_MAX_ENTRIES bounds the block-local in-memory SMT cache. If a block reaches the guard, the adapter stops adding new cache entries for that block and continues through PostgreSQL plus the pending write buffer. This is a performance fallback, not a consensus change.

SMT Candidate Evaluation

Run:

powershell
npm run benchmark:smt:evaluation

Validate the external process contract with the included fixture:

powershell
$env:NOOS_SMT_EVAL_EXTERNAL_CANDIDATE_COMMAND=".\node_modules\.bin\tsx.cmd"
$env:NOOS_SMT_EVAL_EXTERNAL_CANDIDATE_ARGS_JSON='["test/benchmarks/smt-evaluation/external-iden3-fixture.ts","--vector","{vectorPath}"]'
npm run benchmark:smt:evaluation

Important variables:

VariableDefaultMeaning
NOOS_SMT_EVAL_LEAVES1000Unique keys in the vector.
NOOS_SMT_EVAL_UPDATE_ROUNDS1Full update passes after inserts.
NOOS_SMT_EVAL_DELETE_EVERY10Delete every Nth key, or 0 to disable deletes.
NOOS_SMT_EVAL_PROOF_SAMPLES25Proof operations appended for live keys.
NOOS_SMT_EVAL_PROFILE_INTERNAL_TIMINGStrueProfile NOOSChain adapter internals.
NOOS_SMT_EVAL_EXTERNAL_CANDIDATE_COMMANDunsetExternal executable for a candidate.
NOOS_SMT_EVAL_EXTERNAL_CANDIDATE_ARGS_JSONunsetJSON args array. {vectorPath} is replaced.
NOOS_SMT_EVAL_LOG_DIR./benchmark-results/smt-evaluationReport directory.
NOOS_SMT_EVAL_WRITE_VECTORfalsePreserve the generated vector.

The current production native path is Nervos, and Nervos is not iden3-compatible. It is a protocol-versioned root scheme. A future candidate is not eligible for production unless it produces deterministic roots, validates proof semantics, passes replay and snapshot vectors, runs consistently on Windows and Linux, and is backed by a maintained implementation.

See SMT Candidate Evaluation for the external candidate process contract.

State-Root Engine Candidate

Build tools and run:

powershell
npm run build:state-root-tools
npm run benchmark:state-root-engine

Important variables:

VariableDefaultMeaning
NOOS_STATE_ROOT_ENGINE_BENCH_LEAVES1000Initial materialized leaves.
NOOS_STATE_ROOT_ENGINE_BENCH_UPDATES1000Ordered block-style set mutations.
NOOS_STATE_ROOT_ENGINE_BENCH_DELETE_EVERY10Delete every Nth initial key, or 0 to disable deletes.
NOOS_STATE_ROOT_ENGINE_BENCH_LOG_DIR./benchmark-results/state-root-engineReport and vector directory.
NOOS_STATE_ROOT_ENGINE_BENCH_KEEP_VECTORfalsePreserve generated vectors.
NOOS_STATE_ROOT_ENGINE_RUST_COMMANDauto-detectedExternal native candidate executable.
NOOS_STATE_ROOT_ENGINE_RUST_ARGS_JSONunsetJSON args array. {vectorPath} is replaced.

Larger profile:

powershell
$env:NOOS_STATE_ROOT_ENGINE_BENCH_LEAVES="50000"
$env:NOOS_STATE_ROOT_ENGINE_BENCH_UPDATES="50000"
$env:NOOS_STATE_ROOT_ENGINE_BENCH_DELETE_EVERY="20"
npm run benchmark:state-root-engine

This benchmark applies an ordered mutation batch and rebuilds from the final leaf set. A candidate passes its internal check only when apply and rebuild produce the same root for that engine. Root equality with iden3 is informational for new root schemes.

Real State-Root Vectors

Run:

powershell
npm run build:state-root-tools
npm run benchmark:state-root-real-vectors

The runner commits real signed transactions through the single-node consensus path, extracts block_state_changes as replay/apply vectors, extracts state_leaves as snapshot/rebuild vectors, and runs iden3 plus built external utilities against those vectors.

Important variables:

VariableDefaultMeaning
NOOS_STATE_ROOT_REAL_VECTOR_LOG_DIR./benchmark-results/state-root-real-vectorsReport and vector directory.
NOOS_STATE_ROOT_REAL_VECTOR_KEEP_DBfalseKeep the isolated replay schema.
NOOS_STATE_ROOT_ENGINE_BENCH_KEEP_VECTORfalsePreserve vectors.
NOOS_STATE_ROOT_ENGINE_RUST_COMMANDauto-detectedRun one explicit external candidate.
NOOS_STATE_ROOT_ENGINE_RUST_ARGS_JSONunsetJSON args array. {vectorPath} is replaced.

Persistent State-Root Sidecar

Build, test, and benchmark:

powershell
npm run build:state-root-sidecar-nervos
npm run test:state-root-persistent-sidecar
npm run benchmark:state-root-persistent-sidecar

Important variables:

VariableDefaultMeaning
NOOS_STATE_ROOT_SIDECAR_BENCH_LEAVES5000Initial leaves loaded before the first checkpoint.
NOOS_STATE_ROOT_SIDECAR_BENCH_UPDATES5000Ordered set mutations after the first checkpoint.
NOOS_STATE_ROOT_SIDECAR_BENCH_DELETE_EVERY25Delete every Nth initial key, or 0 to disable deletes.
NOOS_STATE_ROOT_SIDECAR_BENCH_BATCH_SIZE1000Mutations per apply_batch request.
NOOS_STATE_ROOT_SIDECAR_BENCH_LOG_DIR./benchmark-results/state-root-persistent-sidecarReport and temporary checkpoint directory.
NOOS_STATE_ROOT_SIDECAR_BENCH_KEEP_CHECKPOINTfalsePreserve checkpoint files.
NOOS_STATE_ROOT_SIDECAR_COMMANDauto-detectedExplicit sidecar executable.

The benchmark measures initial load, update/delete batches, checkpoint export size/time, restart checkpoint-load time, and Node-side memory samples. The sidecar checkpoint is local sidecar state, not a NOOSChain consensus snapshot.

Sync, Payload, And Snapshot Benchmarks

Sync Catch-Up

Run:

powershell
npm run benchmark:sync-catchup

Important variables:

VariableDefaultMeaning
NOOS_SYNC_CATCHUP_BENCH_SCENARIOfresh-observerfresh-observer, lagged-observer, missing-payloads, snapshot-bootstrap, multi-peer-failover, policy-permission-changes, or observer-restart-recovery.
NOOS_SYNC_CATCHUP_BENCH_PAYLOAD_PROFILEsingle-bucketsingle-bucket or multi-policy.
NOOS_SYNC_CATCHUP_BENCH_SOURCE_BLOCKSscenario-dependentSource blocks committed.
NOOS_SYNC_CATCHUP_BENCH_PRE_SYNC_BLOCKSscenario-dependentBlocks synced before the measured window.
NOOS_SYNC_CATCHUP_BENCH_TXS_PER_BLOCK10Transactions per source block.
NOOS_SYNC_CATCHUP_BENCH_MAX_BLOCKS_PER_BATCH50Maximum blocks requested per HTTP batch.
NOOS_SYNC_CATCHUP_BENCH_PAYLOAD_SIZE_BYTES512Synthetic encrypted payload size.
NOOS_SYNC_CATCHUP_BENCH_RUN_PAYLOAD_BACKFILLtrueRun payload backfill during measured sync.
NOOS_SYNC_CATCHUP_BENCH_REPLAY_VERIFYfalseRun full replay verification after catch-up.
NOOS_SYNC_CATCHUP_BENCH_LOG_DIR./benchmark-results/sync-catchupReport directory.
NOOS_SYNC_CATCHUP_BENCH_KEEP_DBfalseKeep isolated schemas.

The runner creates a source chain, serves it through /chain/head, /chain/blocks, and /records/payloads, then syncs an isolated target node with the real sync service. It reports remote-head fetch, block-batch fetch, block execution, payload discovery, payload fetch, payload update, peer request counts, counter diagnostics, throughput, and final head/root equality.

Useful profiles:

powershell
$env:NOOS_SYNC_CATCHUP_BENCH_RUN_PAYLOAD_BACKFILL="false"
npm run benchmark:sync-catchup

$env:NOOS_SYNC_CATCHUP_BENCH_PAYLOAD_PROFILE="multi-policy"
npm run benchmark:sync-catchup

$env:NOOS_SYNC_CATCHUP_BENCH_SCENARIO="snapshot-bootstrap"
$env:NOOS_SYNC_CATCHUP_BENCH_PRE_SYNC_BLOCKS="100"
$env:NOOS_SYNC_CATCHUP_BENCH_SOURCE_BLOCKS="120"
$env:NOOS_SYNC_CATCHUP_BENCH_PAYLOAD_PROFILE="multi-policy"
$env:NOOS_SYNC_CATCHUP_BENCH_REPLAY_VERIFY="true"
npm run benchmark:sync-catchup

$env:NOOS_SYNC_CATCHUP_BENCH_SCENARIO="multi-peer-failover"
$env:NOOS_SYNC_CATCHUP_BENCH_PAYLOAD_PROFILE="multi-policy"
npm run benchmark:sync-catchup

$env:NOOS_SYNC_CATCHUP_BENCH_SCENARIO="policy-permission-changes"
npm run benchmark:sync-catchup

$env:NOOS_SYNC_CATCHUP_BENCH_SCENARIO="observer-restart-recovery"
npm run benchmark:sync-catchup

Interpretation:

  • Block fetch dominates: inspect HTTP range size, DTO query plans, compression, and serialization.
  • Block execution dominates: inspect target-side block execution and SMT metrics.
  • Payload fetch dominates: inspect peer serving, batch size, and peer scoring.
  • Payload update dominates: inspect local payload-store write batching, indexes, triggers, and availability counters.
  • Counter diagnostics mismatch: treat the benchmark as failed. Stale counters can hide sync and backfill regressions.

Payload Backfill

Run:

powershell
npm run benchmark:payload-backfill

Important variables:

VariableDefaultMeaning
NOOS_PAYLOAD_BACKFILL_BENCH_RECORDS1000Missing encrypted-record rows to seed.
NOOS_PAYLOAD_BACKFILL_BENCH_PAYLOAD_SIZE_BYTES512Synthetic ciphertext size.
NOOS_PAYLOAD_BACKFILL_BENCH_HTTP_BATCH_SIZE100Record ids per peer request, capped by API contract.
NOOS_PAYLOAD_BACKFILL_BENCH_DB_UPDATE_BATCH_SIZE500Verified payload rows per local update.
NOOS_PAYLOAD_BACKFILL_BENCH_SEED_BATCH_SIZE1000Seed insert batch size.
NOOS_PAYLOAD_BACKFILL_BENCH_REMOTE_UNAVAILABLE_EVERY0Inject unavailable peer payloads.
NOOS_PAYLOAD_BACKFILL_BENCH_HASH_MISMATCH_EVERY0Inject hash mismatches.
NOOS_PAYLOAD_BACKFILL_BENCH_DISABLE_OBSERVABILITY_UPDATE_TRIGGERfalseDiagnostic-only trigger-overhead check.
NOOS_PAYLOAD_BACKFILL_BENCH_LOG_DIR./benchmark-results/payload-backfillReport directory.
NOOS_PAYLOAD_BACKFILL_BENCH_KEEP_DBfalseKeep the isolated schema.

The runner seeds records across replication policies, serves deterministic ciphertext from a local peer, and runs the real backfill service. The report includes peer request counts, skip reasons, HTTP/DB batch counts, payloads/sec, and verification that fetched rows match policy and injected-failure expectations.

Use the trigger diagnostic only in isolated benchmark schemas. It intentionally makes cached observability counters stale and is not a production optimization.

Payload Store

Run:

powershell
npm run benchmark:payload-store

Important variables:

VariableDefaultMeaning
NOOS_PAYLOAD_STORE_BENCH_ROWS10000Payload rows to seed.
NOOS_PAYLOAD_STORE_BENCH_PAYLOAD_SIZE_BYTES512Synthetic ciphertext size.
NOOS_PAYLOAD_STORE_BENCH_SEED_BATCH_SIZE1000Seed batch size.
NOOS_PAYLOAD_STORE_BENCH_LOOKUP_SAMPLES1000Single and batch lookup samples.
NOOS_PAYLOAD_STORE_BENCH_BATCH_LOOKUP_SIZE100Batch lookup size.
NOOS_PAYLOAD_STORE_BENCH_GC_LIMIT1000GC dry-run candidate limit.
NOOS_PAYLOAD_STORE_BENCH_WRITE_OVERHEAD_SAMPLE_ROWS1000Existing rows updated to measure trigger/cache overhead.
NOOS_PAYLOAD_STORE_BENCH_LOG_DIR./benchmark-results/payload-storeReport directory.
NOOS_PAYLOAD_STORE_BENCH_KEEP_DBfalseKeep the isolated schema.

This benchmark measures read-side local payload behavior: single lookup, batch lookup, cached stats, realtime stats, update-over-existing-row overhead, GC preview/prune, and export-like scans. Use it when payload-store endpoints or operator stats change.

Snapshot Streaming

Run:

powershell
npm run benchmark:snapshots

Recommended large-file profile:

powershell
$env:NOOSCHAIN_SMALL_SNAPSHOT_FAST_PATH_BYTES="0"
$env:NOOS_SNAPSHOT_BENCH_RECORD_INDEX_ROWS="250000"
npm run benchmark:snapshots

Important variables:

VariableDefaultMeaning
NOOS_SNAPSHOT_BENCH_RECORD_INDEX_ROWS250000Synthetic record_indexes rows.
NOOS_SNAPSHOT_BENCH_INSERT_BATCH_SIZE1000Fixture-generation batch size.
NOOS_SNAPSHOT_BENCH_FORMATarchivearchive or legacy json.
NOOS_SNAPSHOT_BENCH_LOG_DIR./benchmark-results/snapshotsReport directory.
NOOS_SNAPSHOT_BENCH_KEEP_FILESfalseKeep temporary snapshot files.
NOOSCHAIN_SMALL_SNAPSHOT_FAST_PATH_BYTES33554432Set 0 to force the large streaming path.
NOOSCHAIN_SNAPSHOT_COPY_ENABLEDtrueUse PostgreSQL COPY staging for large imports.
NOOSCHAIN_SNAPSHOT_COPY_BUFFER_BYTES1048576Bytes buffered per active COPY writer.
NOOSCHAIN_SNAPSHOT_HEADER_SCAN_BYTES1048576Prefix/tail bytes for metadata reads.
NOOSCHAIN_SNAPSHOT_MAINTENANCE_WORK_MEM256MBTransaction-local import index rebuild memory.
NOOSCHAIN_SNAPSHOT_IMPORT_SYNCHRONOUS_COMMITonTransaction-local durability setting.
NOOSCHAIN_SNAPSHOT_REBUILD_RECORD_INDEXES_THRESHOLD50000Drop/rebuild secondary record_indexes indexes above this row count.
NOOSCHAIN_SNAPSHOT_DROP_RECORD_INDEX_CONSTRAINTStrueAlso drop/rebuild constraints during record-index import.

The report includes file size, export time, verification time, import time, rows imported per table, staging/COPY statistics, parse-and-stage timing, final table insertion timing, index rebuild timing, and persistent SMT rebuild timing. Use those phase timings to decide whether the next bottleneck is JSON parsing, archive table verification, PostgreSQL final insertion, index rebuild, or SMT rebuild.

Archive snapshots should be the default benchmark format. Legacy JSON remains useful for compatibility comparisons only.

Release Evidence Checklist

For release readiness or a major performance-sensitive change, keep these artifacts with the engineering decision:

  • combined benchmark:v1-readiness report;
  • benchmark:v1-readiness:check report;
  • raw child benchmark reports under benchmark-results/v1-readiness/raw;
  • hardware, OS, Node, PostgreSQL, and sidecar build details;
  • environment variables and profiles used;
  • linked correctness evidence, especially replay, state-root, snapshot, and sync verification;
  • explanation for any budget change or accepted regression.

Do not loosen a budget only to make a release pass. First decide whether the workload envelope was wrong, the benchmark machine was not representative, or the implementation needs optimization.

When To Add A Benchmark

Add or extend a benchmark when a change introduces a new hot path, a new operator-facing budget, or a performance claim that tests cannot measure.

A useful benchmark should:

  • run from package.json;
  • write a machine-readable JSON report;
  • record its workload configuration;
  • use isolated schemas or temporary files;
  • validate enough invariants to catch obviously invalid fast paths;
  • separate major phases in the report;
  • include recommendations only when they are tied to measured fields.

Do not put long-running soak, malformed-peer chaos, crash fuzzing, or fork conflict drills into a benchmark runner. Those belong in hardness, soak, or incident-response exercises.

Current Limits

The benchmark suite does not prove consensus safety, Byzantine tolerance, operator disaster recovery, or production readiness by itself. It also does not simulate every possible data shape, 10M-entry state, overnight resource leaks, malformed peers, or real network jitter.

Use benchmarks as one layer of evidence: fast enough, measured in the right place, and paired with correctness verification.

Audience-first NOOSChain documentation.