Size Stress Testing
Current Status
package.json currently contains stability:size and stability:size:10m, but the referenced runner file src/stability-test/size-stress/size-stress-runner.ts is not present in this workspace. Treat this document as the intended size/load stress design until that runner is restored or reimplemented.
The intended harness lives under src/stability-test/size-stress.
This is not a normal E2E test. It is designed to grow the chain with a large stream of generated transactions while measuring block creation, execution, SMT updates, multi-node HTTP sync, snapshots, and payload availability behavior.
Safety
The intended runner refuses NODE_ENV=production. It creates isolated schemas named with noos_size_stress_<runId>_node_<index> and drops only those schemas unless NOOS_SIZE_STRESS_KEEP_DB=true.
The default run is configurable and does not require loading all transactions into memory. Transactions are generated, signed, submitted, committed, and discarded incrementally.
Intended 10M mode is guarded:
$env:NOOS_SIZE_STRESS_TX_COUNT="10000000"
$env:NOOS_SIZE_STRESS_ALLOW_10M="true"
$env:NOOS_SIZE_STRESS_TXS_PER_BLOCK="1000"
npm run stability:sizeBash:
NOOS_SIZE_STRESS_TX_COUNT=10000000 NOOS_SIZE_STRESS_ALLOW_10M=true NOOS_SIZE_STRESS_TXS_PER_BLOCK=1000 npm run stability:sizeConfiguration
NOOS_SIZE_STRESS_TX_COUNTdefault100000NOOS_SIZE_STRESS_ALLOW_10MdefaultfalseNOOS_SIZE_STRESS_NODE_COUNTdefault3NOOS_SIZE_STRESS_TXS_PER_BLOCKdefault1000NOOS_SIZE_STRESS_SYNC_EVERY_BLOCKSdefault10NOOS_SIZE_STRESS_SNAPSHOT_EVERY_BLOCKSdefault100NOOS_SIZE_STRESS_VERIFY_EVERY_BLOCKSdefault100NOOS_SIZE_STRESS_REPLAY_EVERY_BLOCKSdefault0NOOS_SIZE_STRESS_PAYLOAD_SIZE_BYTESdefault128NOOS_SIZE_STRESS_INDEX_KEY_COUNTdefault3NOOS_SIZE_STRESS_BUCKET_COUNTdefault6NOOS_SIZE_STRESS_MIX_PER_RECORD_KEYdefaulttrueNOOS_SIZE_STRESS_MIX_PER_BUCKET_KEYdefaulttrueNOOS_SIZE_STRESS_MIX_METADATA_ONLYdefaulttrueNOOS_SIZE_STRESS_MIX_AUTHORIZED_ONLYdefaulttrueNOOS_SIZE_STRESS_KEEP_DBdefaultfalseNOOS_SIZE_STRESS_RESUMEdefaultfalseNOOS_SIZE_STRESS_RUN_IDdefault generatedNOOS_SIZE_STRESS_EXPORT_REPORT_PATHdefault unsetNOOS_SIZE_STRESS_PROGRESS_INTERVAL_MSdefault5000NOOS_SIZE_STRESS_VERBOSE_EVENTSdefaultfalse; set totrueto print low-levelsize_stress_eventJSON in addition to the human node timelineNOOS_SIZE_STRESS_REAL_ENCRYPTIONdefaulttrueup to 100kNOOS_SIZE_STRESS_FINAL_REPLAY_MAX_TXdefault250000NOOS_SIZE_STRESS_FORCE_FINAL_REPLAYdefaultfalse
Runtime sync serving also honors NOOS_SYNC_MAX_BLOCK_RANGE, default 1000. Increase it cautiously for high-bandwidth local testing; the code refuses values above 10000. Larger ranges reduce HTTP round trips but increase response serialization and per-request memory pressure.
Nodes
The intended harness starts multiple local Fastify NOOSChain nodes with isolated schemas. Node 0 is the producer. Followers sync from node 0 over HTTP using the normal block sync path. Only node 0 generates and commits transactions.
By default, the intended size-stress harness focuses on block creation and finalized block sync. It inserts generated transactions directly into Node A's mempool, then Node A proposes blocks and followers catch up through /chain/blocks. This mode intentionally does not exercise transaction gossip on the hot path. Follower nodes are observers: they read, verify, sync, serve permitted data, and perform availability-layer payload backfill, but they reject transaction admission and gossip.
The intended size-stress harness disables Fastify's raw per-request logger for these temporary nodes and emits node-labelled lifecycle lines instead. Live output is intended to explain what each participant is doing, for example:
[2026-05-11T12:00:00.000Z] System: Creating and submitting transaction batch to Node A {"blockIndex":0,"transactionCount":1000}
[2026-05-11T12:00:01.000Z] Node A: Proposed and committed block {"height":"10","blocksCommitted":1}
[2026-05-11T12:00:02.000Z] Node B: Syncing committed blocks from Node A {"blocksCommitted":10}
[2026-05-11T12:00:02.150Z] Node B: HTTP GET /chain/blocks?from=10&to=19&limit=10 -> 200 {"durationMs":150}
[2026-05-11T12:00:02.151Z] Node B: Downloaded block batch from Node A {"fetchedBlocks":10,"firstHeight":"10","lastHeight":"19"}
[2026-05-11T12:00:02.152Z] Node B: Executing synced block locally {"height":"10","transactionCount":500}
[2026-05-11T12:00:04.000Z] Node B: Verified synced block locally {"height":"10","syncedBlocks":1}If the log appears to pause after Node A: HTTP GET /chain/blocks ... -> 200, the network fetch is already complete. The follower is then doing local sync work: inserting the block, executing its transactions, updating SMT state, verifying the resulting block hash/state root, and finally fetching any authorized availability-layer ciphertext. The Executing synced block locally and Verified synced block locally lines show that progress.
The final benchmark report remains machine-readable JSON. Periodic progress lines also remain JSON so external tooling can still parse run progress. The older low-level size_stress_event JSON stream is now opt-in via NOOS_SIZE_STRESS_VERBOSE_EVENTS=true; this keeps the default console output focused on the node timeline.
The normal sync path uses bounded /chain/blocks ranges, batched block transaction loading, optional Brotli/gzip response compression, and batched payload backfill. These are the same mechanisms used outside the stress harness.
Genesis includes six buckets:
- per-record key, replicate all
- per-record key, authorized only
- per-record key, metadata only
- per-bucket key, replicate all
- per-bucket key, authorized only
- per-bucket key, metadata only
Per-bucket-key buckets receive initial bucket keys. Payload availability is checked on followers after sync.
Replay and Snapshots
Full replay is expensive at high transaction counts. The intended runner performs chain verification and snapshot verification periodically. Final replay runs only when TX_COUNT <= NOOS_SIZE_STRESS_FINAL_REPLAY_MAX_TX unless NOOS_SIZE_STRESS_FORCE_FINAL_REPLAY=true.
Snapshots are exported and verified at configured block intervals. Snapshot import is spot-checked for smaller runs. Large snapshot import currently needs chunked importer work to avoid PostgreSQL parameter pressure, so high-volume runs verify snapshot integrity without importing the full document.
Report
The final JSON report includes:
- submitted/executed/failed transaction counts
- committed blocks
- throughput
- latency summaries
- storage/row counts
- memory usage
- chain/snapshot/replay/follower verification
- replication policy checks
- issues
Set NOOS_SIZE_STRESS_EXPORT_REPORT_PATH to write the full report to disk.
Current Limitations
- No Raft, CometBFT, or libp2p.
- No Byzantine consensus simulation.
- Resume metadata is persisted in
size_stress_runs; full resume of exact signer nonce/bucket rotation schedule is a first-pass implementation and will be expanded with stronger checkpoint manifests. - Snapshot import spot-checking is intentionally limited for larger runs until the snapshot importer uses chunked inserts.
- Production object storage is not tested.