mentat scale benchmark: scale-2026-09-27T010840Z

Findings (hand-written; the tables below are generated)

Instance: r6id.metal (Intel Xeon Platinum 8375C, 2 sockets and 128 vCPU, 1 TiB RAM, 4×1.9 TB instance NVMe as XFS on md RAID-0), AL2023 kernel 6.18, us-east-2a. PostgreSQL 16.15 built from source (-O2, no cassert). pg_mentat 1.9.0 was built --release with pgrx 0.17. shared_buffers = 857 GiB (85% of RAM) on 2 MiB huge pages. The postmaster ran under numactl --interleave=all. Full settings are in env.txt, and the reasoning is in benchmarks/scale/ec2/tune.sh.

The working set is smaller than shared_buffers at every scale. pg_database_size was 0.8 GB at s (1M datoms), 8.0 GB at m (10M), 80 GB at l (98M) and 244 GB at xl (303M). xl uses 28% of s_b. On disk that is about 0.81 to 0.86 KB per datom (see sizes.txt).

Where each backend scales, and where it falls over

  1. PostgreSQL (pg_mentat) is the only deployment that reached xl (303M datoms). Bulk load ran at 131 to 206K datoms/s with 16 to 32 parallel loaders (xl: 1472 s). ANALYZE took 0.7 to 1.5 s and VACUUM 0.4 to 8.4 s. The concurrency curve (read mix q1+q2+pull, quiet machine, median of 3, ops/s at 1 / 8 / 32 / 64 / 128 clients):

    scale 1 8 32 64 128 p50 @1 → @128
    s (1M) 1,316 10,236 38,581 64,669 69,167 0.77 → 1.6 ms
    m (10M) 625 4,894 18,871 31,911 35,769 1.6 → 3.5 ms
    l (98M) 101 810 3,195 5,730 5,844 13.6 → 30.8 ms
    xl (303M) 33 265 1,061 1,922 1,932 44 → 96 ms

    Scaling is linear up to 64 clients (one per physical core), then flat. The throughput level falls with scale because of q1 (see below). pull stays flat at 0.7 to 1.3 ms p50 from s to xl, because it uses the EAVT pkey. Two query classes do not scale:

    • Point lookup by a unique attribute degrades linearly with the attribute’s size. q1 p50 was 0.41 ms at s, 1.6 ms at m, 13.6 ms at l and 49 ms at xl. plans/l-q1.json: the current_text table has only (store_id,e,a,v) and (store_id,a,e) INCLUDE (v,tx), so [?e :user/email "x"] is a Parallel Index Only Scan of every :user/email value with Filter: v = 'x'. There is no AVET index on the current-state tables. q2, input_bindings and as_of inherit the same cost. Fix (crates/pg): add (store_id, a, v) INCLUDE (e) on current_*, at least for :db/unique/:db/index attributes.
    • Full-attribute scans (q3 aggregate, q4 predicate) are O(n) and slow. q3 took 126 ms at s, 1.5 s at m and 16 s at l. At xl it exceeded the 20 s probe (ceiling). plans/*-q3.json: (count ?i) compiles to COUNT(DISTINCT e) with a full sort. At xl the first attempt died on pg_mentat’s own mentat.temp_file_limit (1 GB default). q4 at xl returns 3.2M rows, and the single jsonb result exceeds PostgreSQL’s 256 MB jsonb limit (ERROR: total size of jsonb object elements exceeds the maximum): a hard ceiling of the edn_q → jsonb API, not a tuning issue.
    • pg_mentat errors once a result passes mentat.max_result_rows (default 100000). From m up, q4 and since need it lifted. The suite sets it to 0 in the bench DBs.
    • Writes: single-datom edn_t with synchronous_commit=on ran at 1.3K tx/s at s, about 500 tx/s at m and l, and 1.0K/s at xl, with p99 of 1 to 11 ms, while 8 readers ran. All writes succeeded; there were no serialization failures.
  2. Embedded (the mentat crate on SQLite) has the best single-thread latency for entity-shaped reads, but does not scale with threads or history.

    • Point lookup was 0.09 ms at s, 0.66 ms at m and 7.9 ms at l. Pull was 0.056 to 0.074 ms from s to l, 10 to 20× faster than PG, because the call is in-process and there is an AVET index. Input bindings (100 emails) took 0.45 ms at s and 23 ms at l.
    • The joins and scans are much slower than PG. At m, q2 took 640 ms, q3 1.3 s and q4 320 ms. At l, q2 took 14.7 s and q3 16 s. as_of was a ceiling (over 20 s) at every scale from s up. It uses a correlated NOT EXISTS over timelined_transactions, which has no (e,a,v) index.
    • Concurrency falls over. Threads with one Store each (concurrency_sweep) gave 63 ops/s at 1 client and 10 to 14 ops/s at 8 to 64 clients, with p99 of 2 to 16 s. At 128 clients it hit the ceiling. At m, 8 clients already hit the ceiling. Readers during write_mixed had a p99 of 2.7 s at s and 32 s at m. The metadata mutex in Conn::q_once is per Store, and each thread has its own Store, so that mutex is not the cause. The suspected cause is SQLite’s global allocator mutex: the bundled build keeps memstatus on, and the gdb dump of the 128-thread Store::open shows every thread in sqlite3_free → pthread_mutex_lock. A profile of the query-phase collapse was not taken, so profile it with perf before fixing.
    • Store::open is O(history). It took 0.7 s at s, 6.8 s at m and about 20 s at l, because read_partition_map scans the parts view (a GROUP BY over all of timelined_transactions). 128 concurrent opens on the m store did not finish. All 128 threads sat in sqlite3_free → pthread_mutex_lock, RSS grew to 80 GB, and the kernel OOM-killed the process after 25 min (logs/embedded-m-open128-gdb.txt.gz, logs/embedded-m-oom.txt). The runner now opens stores in batches.
    • Bulk load: 42 to 55K datoms/s at m and l with direct entids (l: 39 min), against 11K/s at s when refs were (lookup-ref …). Lookup-ref resolution is the embedded bulk-load bottleneck (see gen.py). Any transaction of 5461 or more datoms panics (off-by-one assert in db.rs insert_non_fts_searches, 6*n < 32766).
    • An interrupted query (sqlite3_interrupt) panics in the projector (projectors/simple.rs unwraps the row iterator), which poisons the Store’s mutex.
  3. The SQLite loadable extension and the DuckDB extension pay a full Store::open on every call. Every scenario costs the same: 0.57 to 0.72 s per call at s and 9.5 to 11 s at m, whether it is q1, pull or q3. Bulk load through edn_t is therefore O(n²). It ran at 5.9K datoms/s (sqlite-ext) and 5.5K/s (DuckDB) at s. At m, sqlite-ext reached only 3.2M of 9.85M datoms in the 900 s cap (ceiling). Its m reads were measured on a copy of the embedded-built store. More processes help only up to the core count: sqlite-ext at s did 1.7 ops/s at 1 client and 78 ops/s at 128. The fix belongs in crates/: cache the Store per db_path per connection, or at least cache the partition map. DuckDB m was not run, within the instance budget. Its per-call cost is the same Store::open (DuckDB at s is within 5% of sqlite-ext on every scenario).

Sustained load, cold vs warm

  • sustained, 20 min (cut from the planned 30 min to stay inside the 6 h budget). PG at xl (32 readers of q1+q2+pull, plus 1 writer) and embedded at m (8 readers of the same mix, plus 1 writer) ran at the same time on the 128-vCPU box.
    • PG xl was stable. Comparing the first and last sixth of the 10 s windows, reads went from 1036 to 987 ops/s, read p50 from 44.4 to 44.9 ms and p99 from 49.6 to 50.1 ms. Writes went from 566 to 755 tx/s, with p99 of 2.6 then 2.0 ms. 1.25M reads and 0.80M writes finished with 0 errors. pg_stat_bgwriter shows 0 buffers_clean. Backend relation reads grew from 4.9M to 9.7M blocks, because first touches of the 244 GB DB came from the OS cache into s_b. temp_bytes did not grow. Postgres RSS stayed flat, since s_b is in huge pages and does not count as RSS. There was no latency drift and no throughput collapse.
    • Embedded m readers starved. 8 reader threads did 932 queries in total in 20 min: 0.8 ops/s, p50 3.9 ms, p99 35 s. The single writer meanwhile ran at 480 tx/s (p99 2.9 ms). Writer throughput was flat over the run. Reader p50 improved (11.7 s → 5.8 s) but p99 stayed around 33 to 35 s. Process RSS grew from 27 MB to 1.36 GB (8 Stores' page caches), then stayed there. The same collapse shows in write_mixed. Under a steady writer, embedded mentat’s WAL-mode readers get almost no work done against q2’s multi-second scans.
    • The windows are in logs/*-sustained-windows-*.csv. The samplers (RSS, iostat, pg_stat_io/bgwriter/database) are in logs/sampler-*.log and logs/iostat-*.log.
  • cold_vs_warm restarted PG and dropped the OS page cache. A cold first call cost 18 to 48 ms for q1 at s to l (424 ms at xl) and 11 to 14 ms for pull. After that, warm latency matched the steady state. shared_buffers starts empty after a restart. Embedded cold means a new process: the open time above plus a first query 2 to 10× slower than warm.

Correctness

Every scenario verifies its answer against the generator’s truth (checks.txt: point lookups, per-state counts, as_of pre-update states, the since count, input bindings, pull contents). No backend returned a wrong answer. 440 checks passed. The 7 FAIL lines are the annotated harness false positive described below. Some checks were SKIPped where the backend could not answer within limits: embedded/sqlite-ext/duckdb as_of (timeout), PG q4 at xl (jsonb size limit). Two harness false positives are annotated in checks.txt: a stale runner binary on the box, and a re-check without --post-write after write_mixed had mutated the data. Both are fixed in the suite.

Methodology caveats

  • All PG concurrency sweeps (s to xl) were re-run at the end with nothing else on the box, and the tables show those runs. The earlier sweeps overlapped other backends' phases and are kept in logs/pg-sweeps-contended.csv.
  • The single-client scenario phases of different backends sometimes overlapped: PG l while embedded l loaded, and embedded m while PG xl loaded. Each phase is at most one busy core plus IO on a 128-vCPU box with 7 TB of NVMe, but it is not a quiet machine. Use compare.py against a rerun before citing a single-client number to better than about 10%.
  • Embedded, sqlite-ext and duckdb load through tempids with @@ entid substitution. PG loads caller-chosen entids. The datoms are the same.
  • The mentat_git value in env.txt (815164a1+) is the suite commit when the run began. The engine is v1.9.0 (tag, 8c6c8816). Later suite commits changed only benchmarks/.

Environment (abridged; full detail in env.txt)

instance_type:   r6id.metal
kernel:          6.18.48-109.150.amzn2023.x86_64
cpu_model:       Intel(R) Xeon(R) Platinum 8375C CPU @ 2.90GHz
cpus:            128
sockets/numa:    2 2 
mem_total_gib:   1007.7
hugepages:       HugePages_Total:=452730 HugePages_Free:=445145 Hugepagesize:=2048 
data_fs:         /dev/md0       xfs   7.0T   53G  6.9T   1% /nvme
mentat_git:      815164a1+
duckdb:          1.5.5
postgres:        postgres (PostgreSQL) 16.15

Dataset and PG size vs shared_buffers

s: 984803 datoms (1600 users, 130000 issues, 6492 history updates)
m: 9850258 datoms (16000 users, 1300000 issues, 64986 history updates)
m: 9850258 datoms (16000 users, 1300000 issues, 64986 history updates)
pg m: pg_database_size=8493931543 (8.0G) shared_buffers=919707058176 (857G) ratio=.009
l: 98474009 datoms (160000 users, 13000000 issues, 650127 history updates)
pg s: pg_database_size=840973335 (803M) shared_buffers=919707058176 (857G) ratio=0
pg l: pg_database_size=85055028247 (80G) shared_buffers=919707058176 (857G) ratio=.092
sqlite-ext m: reads measured on a copy of the embedded-built store (ext bulk load capped at 900s)
xl: 303006946 datoms (500000 users, 40000000 issues, 2000847 history updates)
embedded m concurrency_sweep: first attempt opened 128 Stores concurrently; they serialized on SQLite malloc mutex, grew to 80 GB RSS and were OOM-killed after 25 min (logs/embedded-m-open128-gdb.txt.gz). Rerun with batched opens (OPEN_PAR=8).
pg xl: pg_database_size=261716278295 (244G) shared_buffers=919707058176 (857G) ratio=.284

bulk_load

backend scale datoms wall s datoms/s ANALYZE s VACUUM s store size note
embedded s 984,803 88.3 11,150 0.15 GiB
sqlite-ext s 984,803 167.4 5,884 0.15 GiB
pg m 9,850,258 54.8 179,611 1.3 1.4 7.91 GiB
embedded m 9,850,258 180.0 54,725 1.57 GiB
pg s 984,803 31.4 31,405 0.7 0.4 0.78 GiB
pg l 98,474,009 750.5 131,205 1.5 2.9 79.21 GiB
duckdb s 984,803 180.5 5,457 0.15 GiB
embedded l 98,474,009 2,338 42,119 16.29 GiB
sqlite-ext m 9,850,258 902.2 3,546 load cap hit after ~3199031 datoms (850 txs)
pg xl 303,006,946 1,472 205,820 1.5 8.4 243.74 GiB

point_lookup

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 600.398 607.858 609.573 609.573 1.7 0 3
embedded s 984,803 1 read 109741 0.091 0.095 0.095 0.213 10,974 0 3
embedded m 9,850,258 1 read 14722 0.673 0.718 0.723 0.957 1,472 0 8
embedded l 98,474,009 1 read 631 7.879 8.380 8.423 8.632 126.1 0 1
pg s 984,803 1 read 24369 0.409 0.418 0.429 2.819 2,437 0 3
pg m 9,850,258 1 read 6185 1.624 1.638 1.647 4.210 618.5 0 3
pg l 98,474,009 1 read 741 13.609 13.732 13.879 17.346 74.1 0 3
pg xl 303,006,946 1 read 205 48.555 52.635 52.795 53.025 20.5 0 3
sqlite-ext s 984,803 1 read 30 566.533 586.447 590.944 590.944 1.8 0 3
sqlite-ext m 9,850,258 1 read 7 9,461 9,556 9,556 9,556 0.1 0 1

ref_traversal

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 652.060 658.923 661.180 661.180 1.5 0 3
embedded s 984,803 1 read 202 49.587 50.396 50.518 51.086 20.1 0 3
embedded m 9,850,258 1 read 30 641.709 649.716 649.918 649.918 1.6 0 8
embedded l 98,474,009 1 read 2 14,734 15,050 15,050 15,050 0.1 0 1
pg s 984,803 1 read 8719 1.143 1.270 1.331 4.549 871.9 0 3
pg m 9,850,258 1 read 3651 2.733 3.088 3.187 7.123 365.1 0 3
pg l 98,474,009 1 read 499 20.053 21.634 22.700 24.794 49.9 0 3
pg xl 303,006,946 1 read 200 49.731 54.195 54.436 55.938 20.0 0 3
sqlite-ext s 984,803 1 read 30 625.060 634.004 635.052 635.052 1.6 0 3
sqlite-ext m 9,850,258 1 read 6 10,602 10,831 10,831 10,831 0.1 0 1

aggregate

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 716.032 725.491 726.401 726.401 1.4 0 3
embedded s 984,803 1 read 90 111.074 113.793 114.363 114.363 9.0 0 3
embedded m 9,850,258 1 read 30 1,372 1,380 1,380 1,380 0.7 0 6
embedded l 98,474,009 1 read 2 16,051 16,091 16,091 16,091 0.1 0 1
pg s 984,803 1 read 80 126.280 127.706 128.723 128.723 8.0 0 3
pg m 9,850,258 1 read 40 1,548 1,563 1,565 1,565 0.7 0 3
pg l 98,474,009 1 read 4 16,201 16,385 16,385 16,385 0.1 0 3
pg xl 303,006,946 1 CEILING (first call) 1 20,030 20,030 20,030 20,030 0.0 0 1
sqlite-ext s 984,803 1 read 30 671.278 680.317 684.244 684.244 1.5 0 3
sqlite-ext m 9,850,258 1 read 6 11,321 11,414 11,414 11,414 0.1 0 1

predicate_scan

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 649.345 656.557 657.599 657.599 1.5 0 3
embedded s 984,803 1 read 361 27.739 28.198 28.396 28.455 36.0 0 3
embedded m 9,850,258 1 read 32 320.583 322.175 322.612 322.612 3.1 0 6
embedded l 98,474,009 1 read 6 3,416 3,448 3,448 3,448 0.3 0 1
pg s 984,803 1 read 110 91.622 93.795 96.215 98.377 11.0 0 3
pg m 9,850,258 1 read 74 818.639 822.771 837.992 837.992 1.2 0 3
pg l 98,474,009 1 read 10 6,593 6,827 6,827 6,827 0.2 0 3
pg xl 303,006,946 1 CEILING (first call) 1 20,632 20,632 20,632 20,632 0.0 0 1
sqlite-ext s 984,803 1 read 30 602.532 607.607 609.374 609.374 1.7 0 3
sqlite-ext m 9,850,258 1 read 6 10,149 10,328 10,328 10,328 0.1 0 1

pull

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 596.480 622.530 626.476 626.476 1.7 0 3
embedded s 984,803 1 read 178370 0.056 0.057 0.058 0.120 17,837 0 3
embedded m 9,850,258 1 read 151806 0.066 0.069 0.071 0.250 15,181 0 6
embedded l 98,474,009 1 read 64363 0.074 0.081 0.179 0.647 12,872 0 1
pg s 984,803 1 read 13884 0.716 0.744 0.788 3.660 1,388 0 3
pg m 9,850,258 1 read 7499 1.326 2.063 2.300 3.942 749.9 0 3
pg l 98,474,009 1 read 8640 1.147 1.950 2.451 4.529 864.0 0 3
pg xl 303,006,946 1 read 12938 0.770 0.794 0.804 3.474 1,294 0 3
sqlite-ext s 984,803 1 read 30 563.650 566.986 575.377 575.377 1.8 0 3
sqlite-ext m 9,850,258 1 read 7 9,647 9,703 9,703 9,703 0.1 0 1

as_of

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 CEILING (first call) 1 20,001 20,001 20,001 20,001 0.1 0 1
embedded s 984,803 1 CEILING (first call) 1 20,000 20,000 20,000 20,000 0.1 1 1
embedded m 9,850,258 1 CEILING (first call) 1 20,001 20,001 20,001 20,001 0.1 1 2
embedded l 98,474,009 1 CEILING (first call) 1 20,001 20,001 20,001 20,001 0.1 1 1
pg s 984,803 1 read 5377 1.847 2.130 2.251 8.682 537.7 0 3
pg m 9,850,258 1 read 3876 2.238 5.397 8.150 15.996 387.6 0 3
pg l 98,474,009 1 read 316 31.497 38.420 42.198 49.109 31.6 0 3
pg xl 303,006,946 1 read 4111 2.421 2.760 2.921 9.512 411.1 0 3
sqlite-ext s 984,803 1 CEILING (first call) 1 20,001 20,001 20,001 20,001 0.1 0 1
sqlite-ext m 9,850,258 1 CEILING (first call) 1 20,002 20,002 20,002 20,002 0.1 0 1

since

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 659.851 666.914 669.019 669.019 1.5 0 3
embedded s 984,803 1 read 170 58.770 60.548 60.790 60.796 16.9 0 3
embedded m 9,850,258 1 read 30 616.014 625.418 625.522 625.522 1.6 0 6
embedded l 98,474,009 1 read 3 7,270 10,655 10,655 10,655 0.1 0 1
pg s 984,803 1 read 4634 2.177 2.210 2.266 4.359 463.4 0 3
pg m 9,850,258 1 read 2750 3.668 3.708 3.732 5.770 275.0 0 3
pg l 98,474,009 1 read 1914 5.357 5.477 5.577 8.042 191.4 0 3
pg xl 303,006,946 1 read 2761 3.620 3.735 3.762 6.135 276.1 0 3
sqlite-ext s 984,803 1 read 30 621.869 630.645 631.770 631.770 1.6 0 3
sqlite-ext m 9,850,258 1 read 6 10,839 11,116 11,116 11,116 0.1 0 1

input_bindings

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 600.932 611.881 615.419 615.419 1.7 0 3
embedded s 984,803 1 read 22158 0.450 0.460 0.467 0.533 2,216 0 3
embedded m 9,850,258 1 read 4090 2.433 2.484 2.519 2.686 409.0 0 6
embedded l 98,474,009 1 read 216 23.105 24.086 25.205 25.328 43.0 0 1
pg s 984,803 1 read 9061 1.100 1.123 1.140 3.662 906.1 0 3
pg m 9,850,258 1 read 3676 2.711 2.794 2.876 5.183 367.6 0 3
pg l 98,474,009 1 read 375 26.933 28.642 29.138 32.303 37.5 0 3
pg xl 303,006,946 1 read 152 65.495 69.615 70.441 71.864 15.2 0 3
sqlite-ext s 984,803 1 read 30 581.022 590.974 594.505 594.505 1.7 0 3
sqlite-ext m 9,850,258 1 read 7 9,994 10,008 10,008 10,008 0.1 0 1

write_mixed

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 write 99 604.345 639.948 653.626 665.383 1.6 0 3
duckdb s 984,803 8 read 757 644.791 681.616 713.756 827.788 12.5 0 3
embedded s 984,803 1 write 35420 1.673 2.089 2.259 11.668 590.3 0 3
embedded s 984,803 8 read 406 1.292 2,615 2,676 2,764 6.6 0 3
embedded m 9,850,258 1 write 16714 1.787 2.189 2.332 4.097 557.1 0 3
embedded m 9,850,258 8 read 16 5.459 32,454 32,454 32,454 0.5 0 3
pg s 984,803 1 write 79793 0.748 0.840 0.918 4.818 1,330 0 3
pg s 984,803 8 read 632802 0.927 1.179 1.240 5.400 10,547 0 3
pg m 9,850,258 1 write 32247 1.584 3.611 11.398 95.498 537.5 0 2
pg m 9,850,258 8 read 182278 2.461 5.554 6.184 50.079 3,038 0 2
pg l 98,474,009 1 write 30232 2.055 2.655 2.970 7.100 503.9 0 3
pg l 98,474,009 8 read 22776 20.582 28.614 30.190 33.396 379.6 0 3
pg xl 303,006,946 1 write 30025 0.888 1.311 1.407 4.766 1,001 0 3
pg xl 303,006,946 8 read 4982 47.890 51.886 53.177 55.611 166.1 0 3
sqlite-ext s 984,803 1 write 100 602.451 610.964 615.191 617.044 1.7 0 3
sqlite-ext s 984,803 8 read 767 634.448 658.032 685.394 698.568 12.7 0 3
sqlite-ext m 9,850,258 1 write 5 6,142 6,213 6,213 6,213 0.2 0 1
sqlite-ext m 9,850,258 8 read 40 6,262 6,866 6,888 6,888 1.2 0 1

concurrency_sweep

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
duckdb s 984,803 1 read 30 597.077 648.648 650.873 650.873 1.6 0 3
duckdb s 984,803 8 read 127 625.179 669.466 1,253 1,260 12.0 0 3
duckdb s 984,803 32 read 392 724.401 1,407 1,620 2,056 33.2 0 3
duckdb s 984,803 64 read 682 778.102 1,566 1,673 2,630 62.1 0 3
duckdb s 984,803 128 read 751 1,688 2,761 3,243 3,740 60.7 0 3
embedded s 984,803 1 read 633 0.112 48.884 49.300 49.688 63.1 0 1
embedded s 984,803 8 read 104 0.567 2,146 2,182 2,189 9.8 0 1
embedded s 984,803 32 read 229 2.022 8,531 8,647 8,755 13.8 0 1
embedded s 984,803 64 read 207 5.292 16,525 16,538 16,541 12.5 0 1
embedded s 984,803 128 CEILING (first call) 1 20,001 20,001 20,001 20,001 0.1 41 1
embedded m 9,850,258 1 read 30 0.779 2,227 2,245 2,245 1.4 0 1
embedded m 9,850,258 8 CEILING (first call) 1 20,001 20,001 20,001 20,001 0.1 3 1
pg s 984,803 1 read 13157 0.765 1.122 1.171 4.568 1,316 0 3
pg s 984,803 8 read 102355 0.784 1.174 1.233 5.193 10,236 0 3
pg s 984,803 32 read 385808 0.808 1.283 1.364 10.105 38,581 0 3
pg s 984,803 64 read 646687 0.906 1.657 2.229 22.443 64,669 0 3
pg s 984,803 128 read 691665 1.599 3.380 3.758 49.906 69,166 0 3
pg m 9,850,258 1 read 6254 1.649 2.466 2.532 5.833 625.4 0 3
pg m 9,850,258 8 read 48938 1.667 2.512 2.585 6.609 4,894 0 3
pg m 9,850,258 32 read 188711 1.705 2.625 2.717 12.648 18,871 0 3
pg m 9,850,258 64 read 319114 1.781 4.449 4.880 20.147 31,911 0 3
pg m 9,850,258 128 read 357688 3.479 5.904 6.196 51.406 35,769 0 3
pg l 98,474,009 1 read 1012 13.631 14.832 15.010 19.628 101.2 0 3
pg l 98,474,009 8 read 8103 13.836 15.139 15.375 25.852 810.3 0 3
pg l 98,474,009 32 read 31947 13.886 15.337 15.734 68.933 3,195 0 3
pg l 98,474,009 64 read 57302 14.265 28.887 30.971 59.543 5,730 0 3
pg l 98,474,009 128 read 58437 30.840 33.529 34.020 58.975 5,844 0 3
pg xl 303,006,946 1 read 331 44.040 46.847 47.165 48.336 33.1 0 3
pg xl 303,006,946 8 read 2648 43.947 47.184 48.226 98.954 264.8 0 3
pg xl 303,006,946 32 read 10614 43.912 46.996 48.231 118.241 1,061 0 3
pg xl 303,006,946 64 read 19216 44.912 88.219 94.511 131.946 1,922 0 3
pg xl 303,006,946 128 read 19315 96.238 101.670 103.081 126.931 1,932 0 3
sqlite-ext s 984,803 1 read 30 581.313 636.360 642.068 642.068 1.7 0 3
sqlite-ext s 984,803 8 read 136 596.196 646.695 654.216 655.798 13.1 0 3
sqlite-ext s 984,803 32 read 509 647.670 704.418 719.418 723.723 48.0 0 3
sqlite-ext s 984,803 64 read 613 783.971 1,678 1,684 1,687 56.5 0 3
sqlite-ext s 984,803 128 read 881 1,589 1,716 2,046 2,715 77.5 0 3
sqlite-ext m 9,850,258 1 read 7 9,662 10,552 10,552 10,552 0.1 0 1
sqlite-ext m 9,850,258 8 read 32 9,800 15,081 15,084 15,084 0.6 0 1
sqlite-ext m 9,850,258 32 read 50 9,934 13,971 13,977 13,977 2.5 0 1
sqlite-ext m 9,850,258 128 CEILING (first call) 1 24,047 24,047 24,047 24,047 0.0 0 1

cold_vs_warm

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
embedded s 984,803 1 cold_aggregate 1 108.952 108.952 108.952 108.952 9.2 0 1
embedded s 984,803 1 cold_point_lookup 1 4.532 4.532 4.532 4.532 220.7 0 1
embedded s 984,803 1 cold_pull 1 0.159 0.159 0.159 0.159 6,306 0 1
embedded s 984,803 1 cold_ref_traversal 1 93.052 93.052 93.052 93.052 10.8 0 1
embedded s 984,803 1 open 1 692.955 692.955 692.955 692.955 1.4 0 1
embedded s 984,803 1 warm_aggregate 30 107.523 108.189 108.350 108.350 9.3 0 1
embedded s 984,803 1 warm_point_lookup 30 0.412 0.688 0.732 0.732 2,496 0 1
embedded s 984,803 1 warm_pull 30 0.058 0.072 0.087 0.087 16,640 0 1
embedded s 984,803 1 warm_ref_traversal 30 49.145 54.790 58.725 58.725 20.0 0 1
embedded m 9,850,258 1 cold_aggregate 1 1,375 1,375 1,375 1,375 0.7 0 1
embedded m 9,850,258 1 cold_point_lookup 1 6.681 6.681 6.681 6.681 149.7 0 1
embedded m 9,850,258 1 cold_pull 1 1.233 1.233 1.233 1.233 810.9 0 1
embedded m 9,850,258 1 cold_ref_traversal 1 907.559 907.559 907.559 907.559 1.1 0 1
embedded m 9,850,258 1 open 1 6,847 6,847 6,847 6,847 0.1 0 1
embedded m 9,850,258 1 warm_aggregate 30 1,294 1,301 1,303 1,303 0.8 0 1
embedded m 9,850,258 1 warm_point_lookup 30 0.673 0.930 0.953 0.953 1,408 0 1
embedded m 9,850,258 1 warm_pull 30 0.286 1.906 2.031 2.031 2,660 0 1
embedded m 9,850,258 1 warm_ref_traversal 30 619.233 628.565 639.100 639.100 1.6 0 1
pg s 984,803 1 cold_aggregate 1 221.549 221.549 221.549 221.549 4.5 0 1
pg s 984,803 1 cold_point_lookup 1 18.445 18.445 18.445 18.445 54.2 0 1
pg s 984,803 1 cold_pull 1 11.245 11.245 11.245 11.245 88.9 0 1
pg s 984,803 1 cold_ref_traversal 1 59.282 59.282 59.282 59.282 16.9 0 1
pg s 984,803 1 warm_aggregate 30 152.492 154.643 154.714 154.714 6.6 0 1
pg s 984,803 1 warm_point_lookup 30 0.410 0.597 0.668 0.668 2,255 0 1
pg s 984,803 1 warm_pull 30 1.561 2.204 2.310 2.310 593.7 0 1
pg s 984,803 1 warm_ref_traversal 30 12.432 50.984 51.230 51.230 51.1 0 1
pg m 9,850,258 1 cold_aggregate 1 1,834 1,834 1,834 1,834 0.6 0 1
pg m 9,850,258 1 cold_point_lookup 1 43.171 43.171 43.171 43.171 23.2 0 1
pg m 9,850,258 1 cold_pull 1 12.591 12.591 12.591 12.591 79.4 0 1
pg m 9,850,258 1 cold_ref_traversal 1 74.238 74.238 74.238 74.238 13.5 0 1
pg m 9,850,258 1 warm_aggregate 30 1,751 1,807 1,809 1,809 0.6 0 1
pg m 9,850,258 1 warm_point_lookup 30 1.670 2.097 2.236 2.236 573.4 0 1
pg m 9,850,258 1 warm_pull 30 2.505 3.108 3.148 3.148 405.2 0 1
pg m 9,850,258 1 warm_ref_traversal 30 46.117 59.961 69.697 69.697 21.0 0 1
pg l 98,474,009 1 cold_aggregate 1 24,632 24,632 24,632 24,632 0.0 0 1
pg l 98,474,009 1 cold_point_lookup 1 47.845 47.845 47.845 47.845 20.9 0 1
pg l 98,474,009 1 cold_pull 1 14.200 14.200 14.200 14.200 70.4 0 1
pg l 98,474,009 1 cold_ref_traversal 1 112.136 112.136 112.136 112.136 8.9 0 1
pg l 98,474,009 1 warm_aggregate 30 23,967 24,651 25,280 25,280 0.0 0 1
pg l 98,474,009 1 warm_point_lookup 30 20.514 21.707 21.722 21.722 48.7 0 1
pg l 98,474,009 1 warm_pull 30 3.519 4.498 5.057 5.057 277.5 0 1
pg l 98,474,009 1 warm_ref_traversal 30 76.824 110.491 113.612 113.612 12.3 0 1
pg xl 303,006,946 1 cold_aggregate 1 51,250 51,250 51,250 51,250 0.0 0 1
pg xl 303,006,946 1 cold_point_lookup 1 424.274 424.274 424.274 424.274 2.4 0 1
pg xl 303,006,946 1 cold_pull 1 8.338 8.338 8.338 8.338 119.9 0 1
pg xl 303,006,946 1 cold_ref_traversal 1 144.522 144.522 144.522 144.522 6.9 0 1
pg xl 303,006,946 1 warm_aggregate 1 50,047 50,047 50,047 50,047 0.0 0 1
pg xl 303,006,946 1 warm_point_lookup 30 44.977 47.505 48.050 48.050 22.5 0 1
pg xl 303,006,946 1 warm_pull 30 1.623 2.129 4.637 4.637 563.9 0 1
pg xl 303,006,946 1 warm_ref_traversal 30 118.829 137.079 144.253 144.253 8.4 0 1

sustained

backend scale datoms clients op n p50 ms p95 ms p99 ms max ms ops/s errors reps
embedded m 9,850,258 1 write 576679 2.072 2.386 2.934 221.349 480.6 0 1
embedded m 9,850,258 8 read 932 3.855 34,985 35,795 36,392 0.8 0 1
pg xl 303,006,946 1 write 800706 1.591 2.024 2.326 31.047 667.2 0 1
pg xl 303,006,946 32 read 1245425 44.499 47.821 49.381 246.675 1,038 0 1

Correctness checks

439 PASS, 7 FAIL, 32 SKIP; 30 backend check runs OK, 2 FAILED (all FAIL/FAILED lines, with annotations, in checks.txt)

  • == pg m re-check (mentat.max_result_rows=0)
  • check embedded: HARNESS ERROR (stale mentat-scale binary on the box lacked the serve command); re-run below
  • check embedded: HARNESS ERROR (stale mentat-scale binary on the box lacked the serve command); re-run below
  • == pg xl re-check (mentat.temp_file_limit=100GB; the first two xl checks hit the 1GB default in aggregate)
  • == pg xl re-check #3 (q4 result exceeds PG's 256MB jsonb limit at xl; recorded as SKIP/ceiling)
  • check predicate_scan: SKIP (backend limit: ERROR: total size of jsonb object elements exceeds the maximum of 268435455 bytes)
  • == NOTE (harness false positive, fixed in aa744ad6): the next two blocks re-checked pg l/xl AFTER write_mixed had mutated them, without --post-write. Only the state-dependent checks (by_state, predicate_scan, since) diff
  • check aggregate.by_state: FAIL ({':state/closed': 2600208, ':state/in-progress': 2600236, ':state/open': 2600471, ':state/reopened': 2602203, ':state/resolved': 2596882}, {':state/open': 2600341, ':state/in-progress': 26
  • check predicate_scan: FAIL (1039447, 1039352)
  • check since: FAIL (38802, 2497)
  • check pg: FAILED aggregate.by_state,predicate_scan,since
  • check aggregate.by_state: FAIL ({':state/closed': 8001517, ':state/in-progress': 8004259, ':state/open': 7997805, ':state/reopened': 8000162, ':state/resolved': 7996257}, {':state/open': 7997738, ':state/in-progress': 80
  • check predicate_scan: SKIP (backend limit: ERROR: total size of jsonb object elements exceeds the maximum of 268435455 bytes)
  • check since: FAIL (38448, 2513)
  • check pg: FAILED aggregate.by_state,since