README
¶
unified_bench
Side-by-side benchmarks for HashDB, BTreeOnHashDB, TreeDB (cached), Pebble, Badger, and LevelDB.
Run
- Build:
make unified-bench(writesbin/unified-bench) - Run:
./bin/unified-bench - Or:
go run ./cmd/unified_bench
Guardrail Check (Read Snapshot + Append-Only)
Targeted regression guardrail for append-only writes plus read-heavy snapshot acquisition:
./scripts/check_read_snapshot_guardrail.sh
The script validates that TestRunBenchmark_ReadSnapshotAppendOnlyGuardrail
actually ran (to avoid go test false-greens when -run matches nothing).
It retries once for diagnostics and still fails the job if only the retry
passes, so flaky regressions are surfaced instead of silently passing.
Direct invocation:
cd /path/to/gomap/cmd/unified_bench
GOWORK=off GOMEMLIMIT=4GiB GOMAXPROCS=2 go test -json -p 1 . \
-run '^TestRunBenchmark_ReadSnapshotAppendOnlyGuardrail$' -count=1
Reproducibility
- Randomized tests use a per-test PRNG derived from
-seedso every DB sees the same random key/query sequence. - The chosen seed is printed to stderr at startup.
Tests
write_seq— Sequential Writewrite_rand— Random Writebatch_write— Batch Writebatch_random— Batch Randombatch_delete— Batch Deletebatch_delete_range— Batch DeleteRange (dense ordered range-delete batches; result table is DeleteRange calls/sec)delete_rand— Random Deleterandom_read— Random Readrandom_read_parallel— Random Read (Parallel aggregate throughput)random_read_parallel_acquire_snapshot— Random Read (Parallel, Snapshot Per Key)random_read_batch— Random Read (Batch)full_scan— Full Scan (iterate the full keyspace)prefix_scan— Prefix Scan (range scans over[start,end))- Aliases:
scan→full_scan,range_scan→prefix_scan,read_rand→random_read,read_rand_parallel→random_read_parallel,read_rand_batch/read_random_batch→random_read_batch
- Aliases:
random_read_batch always exercises value-read paths:
- Uses
GetManywhen available. - Falls back to per-key
Getotherwise. - Any
GetMany/Geterror fails the test. - Missing keys are not treated as benchmark-fatal by default (adapter/API contract). Use
-read-require-hitto fail fast on misses and validate value lengths.
batch_delete_range loads dense sortable 8-byte keys before timing, excludes that load/checkpoint phase from delete timing, then commits configured DeleteRange calls in batches. The main result table reports DeleteRange calls/sec; the Batch DeleteRange Metrics section and benchprof_results.json also report affected keys/sec, loaded key count, range width, ranges per batch, value size, validation status, and whether each adapter path is native or fallback. TreeDB and Pebble are reported as native; LevelDB is fallback_iterator_delete because it expands each range into iterator-driven point deletes and should not be treated as native range-delete parity. Because this workload deletes the dense [0, keys) keyspace, it is opt-in and is not part of the default -test all order; run -test batch_delete_range or -test all,batch_delete_range when you want it.
Common flags
-profilebenchmark profile preset (seecmd/unified_bench/profiles.go):balanced(default)durable(strict durability)fast(benchmark-runner no-sync preset; TreeDB currently maps this to a legacy no-WAL compatibility bundle with the Celestia-aligned auto/snappy/balanced value-log compression defaults; unsafe)wal_on_fast(benchmark-runner relaxed-WAL preset; TreeDB currently maps this to a legacy compatibility bundle with the same compression defaults; unsafe)- These names are unified-bench presets shared across database adapters, not the public TreeDB server profile vocabulary. Public TreeDB servers should use
command_wal_durable,command_wal_relaxed, or explicit benchmark-onlybench.
-dbs(allor CSV):hashdb,btree,treedb,pebble,badger,leveldb- Hidden TreeDB variants can be selected explicitly, including
treedb_public_command_wal(aliastreedb_cached_command_wal) for the public cachedcommand_wal_v1path,treedb_backendfor the direct backend path andtreedb_backend_command_wal(aliastreedb_command_wal) for the direct backendcommand_wal_v1path. The command-WAL variants reporttreedb.command_wal.live_accepted_*,treedb.command_wal.live_covered_*, andtreedb.applied_command_lsnstats without scanning WAL segments, so benchmark artifacts can prove that typed frames were accepted and covered. Use-treedb-command-wal-stats-scanonly when segment inventory counters such astreedb.command_wal.typed_segmentsare required.
- Hidden TreeDB variants can be selected explicitly, including
-test(allor CSV): see list above-keysnumber of keys (default 100000)-keycountscomma-separated key counts to sweep over (overrides-keys)-keyscalegenerate keycounts by scale:log10ordoubling(uses-keys-min/-keys-max)-valsizevalue size in bytes (default 128)-val-patternvalue pattern for write tests (includingdataset_write_*) (zero|repeat|repeat_tail64|ultra_compressible_repeat|highly_compressible_notail|half_repeat_half_random|medium_compressible_sparse|celestia_height_prefix_fill|random)- Note: dataset-write generation now uses the same normalized behavior as other write tests (legacy pattern names are accepted as aliases, but generation is unified under
makeValuePool). Reusable dataset key/value fixtures fordataset_write_*anddataset_read_randomare materialized before per-test CPU/allocation/contention profiles and runtime traces start, so those artifacts represent the measured DB loops rather than fixture setup.
- Note: dataset-write generation now uses the same normalized behavior as other write tests (legacy pattern names are accepted as aliases, but generation is unified under
-val-pool-sizenumber of distinct values to cycle through for-val-pattern(0= auto)-batchsizebatch size (default 8000)-batch-delete-range-widthaffected keys perbatch_delete_rangeDeleteRange call (default 100)-batch-delete-ranges-per-batchDeleteRange calls perbatch_delete_rangebatch commit (default 100)-batch-delete-range-validatevalidate after measured deletes that loaded dense keys were removed (excluded from delete timing)-batch-delete-range-refillreload deleted dense keys after measured deletes/validation so later tests see the dataset (excluded from delete timing)-read-workersnumber of goroutines forrandom_read_parallelandrandom_read_parallel_acquire_snapshot(defaultGOMAXPROCS)-read-require-hitfail read benchmarks (random_read*,random_read_batch) on misses and validate value length matches-valsize-range-queriesnumber of prefix/range queries (default 200)-range-spannumber of keys per range (default 100)-leveldb-block-compressionLevelDB: block compression mode (default|on|off|both)-leveldb-block-sizeLevelDB: table block size in bytes (default 4096)
TreeDB Main Knobs
-treedb-chunk-sizeTreeDB: pager chunk size in bytes (default256KiB)-treedb-flush-thresholdTreeDB (cached): flush threshold in bytes (default 64MB)-treedb-maintenance-modeTreeDB maintenance preset (normal|bench)-treedb-memtable-modeTreeDB memtable implementation override-treedb-index-optimizationsTreeDB profile-style index optimization bundle-treedb-index-outer-leaves-in-vlogTreeDB: store outer leaves inleaf_vlog-treedb-leaf-page-read-cache-entriesTreeDB: outer-leaf read cache entries for leaf pages stored in the value log (0=default/env,<0=disable)-treedb-leaf-page-read-cache-write-admissionTreeDB: write-side outer-leaf read-cache admission policy (immediate|adaptive; default preserves immediate admission)-treedb-prefer-append-allocTreeDB: prefer append allocation over freelist reuse-treedb-force-value-pointersTreeDB: force all values into the value log-treedb-value-log-thresholdTreeDB: inline-vs-pointer threshold-treedb-vlog-compressionTreeDB: value-log compression mode (default|off|block|dict|auto)-treedb-vlog-block-codecTreeDB: block codec (snappy|lz4|zstd). When-treedb-vlog-compression=auto, this is the forced block-mode/default block codec, not proof that auto selected that codec; use the value-log codec summary andtreedb.cache.vlog_auto.*stats for actual per-frame selection.-treedb-vlog-auto-policyTreeDB: value-log auto policy (balanced|throughput|size)-treedb-vlog-generation-policyTreeDB: generation policy (default|off|hot_warm_cold)
TreeDB Advanced Tuning
These are mainly for experiments and should usually be left at engine defaults:
-treedb-vlog-compression-autotune-treedb-vlog-dict-*-treedb-vlog-rewrite-*-treedb-flush-build-*-treedb-flush-admission-policy=auto|explicit|off(auto defaults to the admitted hardware-aware span-native/backlog path; off is the rollback and reportsreason=policy_off, selected concurrency0, span-nativefalse, and backlog coalescingfalse)-treedb-flush-apply-concurrency,-treedb-flush-apply-min-*, and-treedb-journal-lanes(auto apply defaults to detected physical cores capped byGOMAXPROCSand 8, withmin(GOMAXPROCS,8)fallback when physical cores are unknown; default journal/value-log lanes are coalescing-safe; configured apply/lane values preserve c4/c8/c16/lane experiments)-treedb-flush-apply-span-native(M10 span-native apply/reducer override for eligible exact point spans; auto enables when admitted)-treedb-flush-span-run-target-planning(diagnostic/default-off read-only target-leaf planning for canonical flush runs)-treedb-flush-backlog-coalescing*(M11 bounded adaptive backlog coalescing controller; auto enables when admitted, byte/op budgets are soft pre-next-memtable limits)-treedb-max-queued-memtables,-treedb-slowdown-backlog-seconds,-treedb-stop-backlog-seconds
Benchmark reports include resolved TreeDB options and selected stats for flush_admission.policy, admitted, reason, configured/requested concurrency, selected/effective concurrency, concurrency cap reason, defaulting, GOMAXPROCS, physical cores, span-native enablement, and backlog-coalescing enablement. reason=unsafe_durability is the expected auto decline when WAL-off durability is allowed; support triage should treat that the same as policy_off for the span-native/backlog path, while preserving the caller's configured concurrency in the report for reproduction.
Use ./bin/unified-bench -h for the full grouped TreeDB advanced flag list.
TreeDB value-log codec matrix
For #2194-style codec characterization, use the matrix harness to run auto,
off, block/snappy, and block/lz4 with one shared dataset shape per value
pattern:
RUN_DIR=/tmp/treedb_vlog_codec_matrix_$(date +%Y%m%d_%H%M%S) \
scripts/treedb_vlog_codec_matrix.sh
Defaults cover zero, celestia_height_prefix_fill,
half_repeat_half_random, and random patterns with
dataset_write_random,batch_random,random_read_parallel,full_scan,prefix_scan.
Set KEYS, VALSIZE, BATCHSIZE, READ_WORKERS, TESTS, PATTERNS, and
leaf-mmap environment variables to match the policy matrix under review. Final
codec/default decisions must rerun this after #2190 frame grouping is merged or
explicitly deferred.
Additional Common Flags
-treedb-max-queued-memtablesTreeDB (cached) max queued immutable memtables before applying backpressure flush (0=default,<0=disable)-treedb-slowdown-backlog-secondsTreeDB (cached) start backpressure when queued backlog exceeds this many seconds of flush work-treedb-stop-backlog-secondsTreeDB (cached) block writers when queued backlog exceeds this many seconds of flush work-treedb-max-backlog-bytesTreeDB (cached) absolute cap on queued backlog bytes-treedb-writer-flush-max-memtablesTreeDB (cached) max memtables a writer will help flush per op-treedb-writer-flush-max-msTreeDB (cached) max time (ms) a writer will help flush per op-treedb-iter-debugprint prefix scan iterator timing + debug stats-treedb-iter-debug-limitmax per-query debug lines to print (default 20)-treedb-maintenance-ops-per-coalesceTreeDB: ops-per-coalesce maintenance budget (0=default,<0=disable budget)-treedb-bg-vacuum-intervalTreeDB: background index vacuum interval (0=disabled)-treedb-bg-vacuum-span-ppmTreeDB: background index vacuum span ratio threshold (ppm),0=default-treedb-allow-unsafeTreeDB: allow unsafe durability/integrity options (required for unsafe toggles)-column-store-asset-read-integritycolumn-store typed asset hot-read integrity for-suite column_storephysical paths (verify|cached_verify|skip_checksums).cached_verifyverifies each immutable typed asset ref once per process cache entry, then skips repeated hot-read CRC for that ref; post-verification file corruption may go undetected until cache eviction or process restart.skip_checksumsskips hot-read CRC. Both relaxed modes require-treedb-allow-unsafe; when unset,-treedb-disable-read-checksumalso disables typed column-asset hot-read CRC verification for the suite.-treedb-vlog-dictTreeDB: value-log dict compression mode (default|on|off|both)-treedb-vlog-rewrite-min-segment-age-msTreeDB: minimum source segment age for online generational rewrite (0=default)-treedb-vlog-dict-frame-encode-levelTreeDB: dict frame zstd encoder level (engine|fastest|default|better|best|all|<int>)-treedb-vlog-dict-frame-entropyTreeDB: dict frame entropy mode (engine|on|off|both)-seedPRNG seed for randomized tests (default 1;0= time-based)-keepkeep temp DB directories after run-settle-before-scansclose+reopen DBs beforefull_scan/prefix_scanto measure scan performance on a “settled” (fully flushed) state-progresslive table updates to stderr (default true)-formatoutput format:tableormarkdown-cpuprofilewrite per-test CPU profiles to<prefix>_<test>_<db>.pprof-cpuprofile-testsrestrict CPU profiling to a CSV list of tests (e.g.random_read,batch_random)-allocsprofilewrite per-test allocation delta profiles to<prefix>_<test>_<db>.pprof(analyzable with-sample_index=alloc_space|alloc_objects)-allocsprofile-testsrestrict allocation profiling to a CSV list of tests-allocsprofilerateallocation sampling rate in bytes forruntime.MemProfileRate(default524288)-checkpoint-cpuprofilewrite per-checkpoint CPU profiles to<prefix>_checkpoint_<test>_<db>.pprof-checkpoint-cpuprofile-testsrestrict checkpoint CPU profiling to a CSV list of tests-profile-dirwrite all profile outputs into one directory (auto-sets defaults for-cpuprofile,-allocsprofile,-checkpoint-cpuprofile,-blockprofile,-mutexprofile,-trace; explicit flags still win). Also emitsbenchprof_results.jsonandbenchprof_results.md, then automatically runsbenchprofin-process. Profile artifacts default to-path-label native-fastpath; pass-path-label oraclefor explicit oracle/comparator captures,-path-label m8-m14-10mm-gatefor the #2768+ mandatory span-run gate shape,-path-label span-native-default-gatefor the default span-native production closeout matrix, or-path-label span-native-read-scan-guardrailfor settled read/scan guardrails tied to that closeout. For TreeDB, the markdown includes selected flush/apply stage counters (treedb.cache.flush_apply.*,treedb.flush_apply.*, including publish prepare/final-install splits), raw span-native route and public-command-WAL fallback counters (treedb.raw.span_native.*), leaf-log lane summary counters (treedb.cache.leaf_log_lanes.*aggregates plus raw preserved lane keys), M8 span-run proof counters (treedb.cache.flush_span_run.*,treedb.flush_apply.span_run.*,treedb.flush_apply.span_native.*), ordered-root span-native proof counters and triage rows (treedb.publish.ordered_root_delta_group.span_native.*), checkpoint split counters, and a value-log codec summary with actual auto/write-mode/outer-leaf codec selection and block frame-K counters.-treedb-cache-stats-before-readsprint selecttreedb.cache.*stats before read/scan tests (treedb only)-blockprofile,-mutexprofilewrite global profiling artifacts to files and also emit per-test contention delta profiles in the same directory (block_<test>_<db>.pprof,mutex_<test>_<db>.pprof) when the computed delta is non-empty-tracewrite runtime execution trace to file-max-wallabort the run if wall time exceeds this duration (guardrail;0= disabled)-max-rss-mbabort the run if RSS exceeds this many MiB (guardrail;0= disabled; Linux-only)-checkpoint-between-testsforce a best-effort durability checkpoint between tests (DBs that supportCheckpoint()), and also once after the final test so end-of-run disk usage reflects a settled state-checkpoint-settle-before-testscomma-separated checkpoint labels that should first wait for TreeDB queue/background debt to drain before checkpointing (for examplerandom_read,post-run, orall; explicit labels must match the selected test order or the run fails); useful for immediate-vs-settled checkpoint comparisons without changing production behavior-checkpoint-settle-timeoutmaximum wait for-checkpoint-settle-before-tests(default10m)-vacuum-between-testsvacuum supported DBs between tests (implies-checkpoint-between-tests; TreeDB usesVacuumIndexOnline)-treedb-vlog-rewrite-after-runrun the full TreeDBCompactStoragepath after the run and report before/after disk usage + the data directory path; the flag name is kept for compatibility-treedb-vacuum-after-vlog-rewrite-runrun offline TreeDB index vacuum after-treedb-vlog-rewrite-after-runbefore final reporting (disable explicitly with-treedb-vacuum-after-vlog-rewrite-run=false)-checkpoint-every-opsforce a best-effort durability checkpoint every N ops during write-heavy tests (DBs that supportCheckpoint())-checkpoint-every-bytesforce a best-effort durability checkpoint every N approx bytes during write-heavy tests (DBs that supportCheckpoint())-suitenamed suite:geth_hot_kv— 30k-key raw-KV proxy for geth/Nitro hot database behavior: sequential point write, random read, full iteration, and denseDeleteRange; defaults totreedb_public_command_wal,pebble,leveldbwith random 128-byte values and reports a compact #2392-style summary. Usescripts/treedb_geth_hot_kv_bench.shfor the standard unified-bench wrapper. For the integrated gethnode.OpenDatabase/ethdbbenchmark and key/value/batch matrix, usebenchmarks/geth_hot_kv/testdata/treedb_nitro_soak.goviascripts/treedb_geth_hot_kv_matrix.sh.readme— generates the README graphs + sweep tableschurn— churn + settled scans (treedb,leveldb)churnvacuum— churn + settled scans, then index compaction and scan againflushdrain— write burst → checkpoint boundary → read; prints checkpoint timing (TreeDB-focused). Use-flushdrain-checkpoint-max=<duration>to fail the suite if the checkpoint beforerandom_readexceeds your latency target.flushthrash— forces a small TreeDB flush threshold; catches flush thrash / runaway backlog regressionsbigkeys_guard— small TreeDB flush threshold + large keycount, with wall/RSS caps for CI guardrailslongmix— long-ish mixed workload + settle boundary with fragmentation reportssload_readheavy— settled point reads with value-log pointers + forkchoice-style batch commitsmaintenance_budget— sweep TreeDB maintenance K values; reports checkpoint time vs index size, recommends Kcolumn_store— native TreeDB column-store benchmark/artifact suite; writes stage-separated throughput, parity, byte-accounting, manifest/recovery,column_store_results.{json,md,html},benchprof_results.{json,md}, andinsights.{md,json,html}when-profile-diris setcollection_storage— TreeDB collection storage-mode comparison suite across retained-document, typed-row-asset, typed-column-part, hybrid, and vector-typed-column layouts; writes semantic-comparability metadata, per-workload correctness/telemetry,collection_storage_results.{json,md,html},benchprof_results.{json,md}, andinsights.{md,json,html}when-profile-diris set
-outdiroutput directory for suite artifacts (plots/images; used by-suite readme)
Standard Profile Workflow (benchprof)
Use -profile-dir so all profiles and ops outputs are captured in one place.
This example uses unified-bench's legacy fast benchmark-runner preset for a
no-WAL profiling ceiling; it is not a TreeDB server profile recommendation:
OUT=$(mktemp -d /tmp/gomap_profiles_XXXXXX)
./bin/unified-bench \
-dbs treedb \
-keys 800000 \
-profile fast \
-checkpoint-between-tests \
-test random_write,random_delete,random_read,full_scan,prefix_scan \
-profile-dir "$OUT" \
-path-label native-fastpath \
-progress=false
./bin/benchprof -profiles-dir "$OUT"
This writes:
benchprof_results.json/benchprof_results.mdcpu_<test>_<db>.pprofallocs_<test>_<db>.pprofblock_<test>_<db>.pprof(when non-empty delta)mutex_<test>_<db>.pprof(when non-empty delta)checkpoint_cpu_checkpoint_<test>_<db>.pprofblock.pprof,mutex.pprof,trace.outinsights.md,insights.json,insights.html(frombenchprof)
For TreeDB runs, benchprof_results.json also preserves selected TreeDB stats
under runs[].treedb_stats, including ordered-root/root-apply, value-log mmap
read counters, and generic plus leaf-specific mmap sealed-budget caps used by
raw-engine review gates. Checkpoint-enabled runs also export
runs[].checkpoint_durations_seconds; selected settled runs export
runs[].checkpoint_settle_seconds; and TreeDB checkpoint snapshots are preserved
under runs[].checkpoint_treedb_stats so immediate-vs-settled checkpoint rows can
use the checkpoint-local *_last counters even if a final no-op checkpoint runs
before end-of-run stats are captured. Selected mmap read display fields prefer the backend
treedb.vlog.mmap_read.* family over cache aliases so counts and ratios come
from one source family. The collection_storage suite also
adds runs[].collection_workloads entries with stable mode/workload names,
semantic-equivalence flags, correctness-validation status, asset-byte splits,
and materialization / typed-column / vector counters for benchprof rendering.
Collection Storage Suite
Use collection_storage to compare logical TreeDB collection workloads across
first-class storage layouts without relying on ad-hoc collection benchmarks. The
suite builds the same deterministic JSON document fixture for every mode,
optionally checkpoints and reopens each DB before reads, then times only the
selected workload loops after a correctness pass. -profile balanced is accepted
as a durable alias; unsafe profiles are rejected because the suite is intended as
a comparable storage-mode gate.
Stable mode names:
document_only— full retained JSON document, no typed-storage owners.typed_row_asset— declared scalar fields owned by typed row assets.typed_column_part— declared scalar fields owned by typed column parts.hybrid_document_row,hybrid_document_column,hybrid_row_column,hybrid_document_row_column— explicit hybrid retained-payload/typed-owner layouts.vector_typed_column— scalar row owners plus a typed-column float32 vector field and column-graph vector index.
Stable workload names:
insert_batch,point_get,predicate_scan,aggregate,vector_search_smoke,mixed.
Unsupported combinations fail closed in the report: for example,
vector_search_smoke is marked unsupported for scalar-only modes instead of
silently comparing a different workload shape. If a selected workload has no
supported selected mode (for example -collection-storage-modes document_only -collection-storage-workloads vector_search_smoke), the command exits with a
clear semantic error.
OUT=$(mktemp -d /tmp/gomap_collection_storage_profiles_XXXXXX)
./bin/unified-bench \
-suite collection_storage \
-dbs treedb \
-profile durable \
-keys 10000 \
-batchsize 1000 \
-collection-storage-modes all \
-collection-storage-workloads all \
-profile-dir "$OUT" \
-path-label native-fastpath \
-progress=false
Collection-storage flags:
-collection-storage-modesCSV mode subset (allby default).-collection-storage-workloadsCSV workload subset (allby default).-collection-storage-query-count,-collection-storage-point-get-count.-collection-storage-selectivity,-collection-storage-cardinality,-collection-storage-payload-size,-collection-storage-field-count.-collection-storage-vector-dims,-collection-storage-vector-top-k,-collection-storage-include-final-fetch. When final fetch is enabled, the default vector response shape isprojection_without_embeddingviaProjectionOrientedVectorDocumentFetchPreset; add-collection-storage-vector-full-documentsonly for explicit full-document/embedding-echo comparison rows.-collection-storage-checkpoint-reopento include the durability/recovery boundary before read workloads (default true).-collection-storage-asset-read-integrity(verify|cached_verify|skip_checksums; relaxed modes require-treedb-allow-unsafe).
The suite writes:
collection_storage_results.json,collection_storage_results.md,collection_storage_results.htmlbenchprof_results.json,benchprof_results.mdinsights.md,insights.json,insights.html- configured runtime profiles, including
cpu_collection_storage_treedb_collection_storage.pprof,allocs_collection_storage_treedb_collection_storage.pprof,checkpoint_cpu_checkpoint_collection_storage_treedb_collection_storage.pprof,block.pprof,mutex.pprof, andtrace.out
Column Store Suite
Use column_store as the canonical native TreeDB column-store benchmark entry
point. It measures the production column-enabled collection manifest/control
path, durable command-WAL publication, isolated physical column assets, planner
diagnostics, parity, byte accounting, and executable row-store / B-tree /
physical-column labels with a deterministic JSONBench-shaped fixture.
-profile balanced is accepted as a durable alias for the column store suite
so the unified-bench default still exercises the durable gate. The runnable
execution labels are row_store_baseline, b_tree_index_baseline,
serial_column_scan, aggregate_metadata, and parallel_column_scan.
The aggregate_metadata path uses typed aggregate metadata for q1, q4b, and
q5_metadata where those assets are available; the other synthetic query shapes
reroute to serial physical scans. sum_time_second_of_day_square is an
arbitrary-expression lane that must visit typed time_us cells unless a future
planner explicitly maintains a matching generated expression or aggregate.
OUT=$(mktemp -d /tmp/gomap_column_store_profiles_XXXXXX)
./bin/unified-bench \
-suite column_store \
-dbs treedb \
-profile durable \
-keys 100000 \
-batchsize 1000 \
-profile-dir "$OUT" \
-path-label native-fastpath \
-column-store-path row_store_baseline \
-progress=false
Use -column-store-query q3 or a comma-separated subset such as
-column-store-query q2,q3,q4b,sum_time_second_of_day_square when collecting
query-isolated 100K-1M row CPU/allocation profiles. Omit it, or pass all, for
the default full q1-q5/q5_metadata/expression suite. Duplicate query names are
rejected so benchprof tables and artifact labels stay unambiguous.
Use -column-store-first-touch-after-open when collecting the secondary
first_touch_after_open lane. It closes the warmed reopened DB after the main
query pass, then reopens the DB and collection once per selected query and
records separate first_touch_queries rows with reopen/open time included in
prepare_setup_duration_ms. The primary queries rows remain the
one_shot_end_to_end lane.
Column-store non-column retained payloads default to semantic-stream-v1 so the
production storage-parity path uses retained semantic-stream side-root blocks.
Use -column-store-retained-payload-encoding template-v1, or set
TREEDB_COLUMN_STORE_RETAINED_PAYLOAD_ENCODING=template-v1, to run the legacy
template-v1 retained-payload path for comparison. b_tree_index_baseline still
keeps full retained JSON so that index-baseline comparisons remain unchanged.
The suite writes:
column_store_results.json,column_store_results.md,column_store_results.htmlbenchprof_results.json,benchprof_results.mdinsights.md,insights.json,insights.html- configured runtime profiles, including
cpu_column_store_treedb_column_store.pprof,allocs_column_store_treedb_column_store.pprof,checkpoint_cpu_checkpoint_column_store_treedb_column_store.pprof,block.pprof,mutex.pprof,trace.out, and the query-phase deltablock_column_store_treedb_column_store.pprof/mutex_column_store_treedb_column_store.pprofwhen those profile classes produce non-empty deltas
column_store_results.json includes query_mode and metadata_mode labels on
query rows and a jsonbench_cells matrix for the in-repo synthetic
JSONBench-shaped fixture. Each cell records the external-facing label
(row-scan, column-direct, column-prepared, column-direct-metadata, or
column-prepared-metadata when the mode exists), query name, sort layout,
storage source, direct/prepared mode, metadata-vs-data path, compression mode,
mutation mode, retained-payload policy, typed storage owner, row count, result
hash, and reconstruction/full-data caveats. Prepared cells are collected in a
separate jsonbench_cell_report stage after query-phase profiles finish, so the
query CPU/allocation profiles and BenchmarkColumnStoreSuite* query-loop
benchmarks remain scoped to the original measured query loop. These rows are
gomap-local smoke coverage. The external snissn/JSONBench harness owns
full-data retained-JSON/reconstruction parity (#2117) and apples-to-apples
storage accounting (#2118), so headline ClickHouse/full-data claims require a
fresh external JSONBench run against the selected gomap dependency.
PR descriptions for column-store milestones should paste the command, row count,
profile, forced path, q1-q5/q5_metadata/expression rows/sec, MiB/sec, ns/row,
planner time, scan time, reduce time, worker count, scheduled/skipped granules,
cache hit/miss counters, materialized-row count, parity status, storage source,
fallback reason, manifest root name/id, active manifest generation/checksum,
byte accounting, manifest/recovery identity, and the generated HTML artifact
paths. If a forced physical column path is not implemented yet, the PR must call that out
explicitly and include the fail-closed evidence. The suite reports measured
column_asset_bytes from the isolated column_assets/ tree after physical
assets are published, plus explicit column_asset_store_bytes,
primary_index_bytes, ordinary_value_vlog_bytes, and leaf_vlog_bytes splits
so M12+ evidence does not confuse typed column assets with row value-log,
leaf-log, or primary-index storage. durable_storage_bytes_wal_excluded is the
steady-state comparison label: it subtracts only valid command WAL segment files
named wal/commit-l<lane>-<seq>.log (numeric lane, non-zero sequence) from
db_total_bytes, while durable value_vlog, leaf_vlog, index.db, column
assets, and manifest/control bytes remain included. retained_payload_bytes
reports the row/remainder payload for the selected path; physical paths that
strip declared columns into column assets should report less retained payload
than the source JSONBench document bytes. With semantic-stream-v1,
retained_payload_bytes includes primary locator bytes plus the retained
semantic-stream side-root block bytes.
For post-V1 production-vs-experiment attribution, use the slope harness:
RUN_DIR=/tmp/treedb_column_store_slope_$(date +%Y%m%d_%H%M%S) \
KEYCOUNTS=10000,100000,500000 \
ROUTED_PATHS=serial_column_scan,aggregate_metadata,parallel_column_scan \
scripts/treedb_column_store_slope_profile.sh
The harness builds unified-bench, runs routed production column_store cases
into per-keycount/per-path profile directories, captures direct production
package benchmarks for the TCPA asset scanner / collection scanner / query
adapter, captures the older experiments/colgranule encoded-kernel baselines,
and writes summary.tsv plus summary.md. Use the generated HTML reports for
review and keep ClickHouse-equivalent JSONBench context tied to
experiments/colgranule/JSONBENCH_COMPARISON_REPORT.md.
Notes
TreeDB is a cached engine (memtable + background flush). If you run long write-heavy phases and then measure random_read/scans immediately, the results can be dominated by background flush work (“flush debt”).
Recommended:
- For settled read/scan performance: use
-checkpoint-between-testsor-settle-before-scans. - For mixed workload under flush debt: keep defaults and optionally enable
-treedb-cache-stats-before-readsto see queue/backlog stats.
Repro: mixed vs settled reads (TreeDB)
Mixed (reads under flush debt; intentionally stressful):
go run ./cmd/unified_bench -dbs treedb -profile fast -keys 900000 -valsize 128 -batchsize 1000 \\
-test sequential_write,random_write,dataset_write_random,dataset_write_sorted,batch_write,batch_random,batch_delete,batch_small_seq,random_delete,random_read \\
-treedb-cache-stats-before-reads -progress=false
Settled (reads after a durability boundary):
go run ./cmd/unified_bench -dbs treedb -profile fast -keys 900000 -valsize 128 -batchsize 1000 \\
-test sequential_write,random_write,dataset_write_random,dataset_write_sorted,batch_write,batch_random,batch_delete,batch_small_seq,random_delete,random_read \\
-checkpoint-between-tests -progress=false
Repro: compression matrix (TreeDB dict + LevelDB block compression)
Run TreeDB twice (dict on/off) and LevelDB twice (block compression on/off) in one invocation:
./bin/unified-bench -test batch_write,random_write,batch_delete -dbs treedb,leveldb -profile fast -keys 4000000 -format markdown \\
-treedb-force-value-pointers \\
-treedb-vlog-dict both \\
-leveldb-block-compression both
To sweep dict-frame encoder knobs (zstd level × entropy coding), use:
./bin/unified-bench -test batch_write -dbs treedb -profile fast -keys 1000000 -format markdown \\
-treedb-force-value-pointers \\
-treedb-vlog-dict on \\
-treedb-vlog-dict-frame-encode-level all \\
-treedb-vlog-dict-frame-entropy both
Repro: random read parallel sweep
Run random_read_parallel with separate worker counts:
./bin/unified-bench -dbs treedb,leveldb -profile fast -keys 500000 -test random_read_parallel -read-workers 1 -progress=false
./bin/unified-bench -dbs treedb,leveldb -profile fast -keys 500000 -test random_read_parallel -read-workers 2 -progress=false
./bin/unified-bench -dbs treedb,leveldb -profile fast -keys 500000 -test random_read_parallel -read-workers 4 -progress=false
./bin/unified-bench -dbs treedb,leveldb -profile fast -keys 500000 -test random_read_parallel -read-workers 8 -progress=false
-test all now includes random_read_parallel and random_read_parallel_acquire_snapshot in the output table:
./bin/unified-bench -dbs treedb,leveldb -profile fast -keys 500000 -test all -read-workers 4 -format markdown -progress=false
Source Files
¶
- adapter_badger.go
- adapter_bbolt.go
- adapter_buntdb.go
- adapter_hashdb.go
- adapter_leveldb.go
- adapter_lmdb_flags.go
- adapter_nutsdb.go
- adapter_pebble.go
- adapter_pogreb.go
- adapter_treedb.go
- adapter_treedb_backend.go
- db_registry.go
- db_variants.go
- hostinfo.go
- hostinfo_linux.go
- main.go
- plots.go
- profiles.go
- rss_linux.go
- suite_collection_storage.go
- suite_column_store.go
- suite_flushdrain.go
- suite_lanes_probe.go
- suite_maintenance_budget.go
- suite_parallel_unlock.go
- suite_vlog_dict.go
- value_pattern.go