Skip to main content

GoatDB Benchmarks

Run provenance

All numbers on this page come from a single benchmark run on 2026-07-31, on an Apple M4 Pro (arm64, 24GB RAM, NVMe SSD) running macOS (Darwin 25.5.0), with Deno 2.9.1, Node.js v24.14.1, and headless Chrome 149. Reproduce with deno task bench:json && deno task bench:update-docs — every table below is regenerated from that run's JSON output.

Shared State for Humans and Agents

Modern applications have more than humans at the keyboard. AI agents, tools, and background services read and write the same live state as the people they work alongside. That workload sets the requirements these benchmarks measure:

  • Latency — a human editing a document and an agent reacting to that edit both need in-process, low-microsecond reads (~2μs measured here). A network round-trip per interaction breaks the feedback loop for both.
  • Live updates — agents and UIs alike subscribe to state changes rather than polling for them, so reactive query refresh is on the critical path.
  • Provenance — when many actors write to shared state, signed commits let you verify which session wrote what, at a signing cost worth measuring.

GoatDB is designed for this workload: an embedded database with built-in sync where each participant — human or agent — works against a full local replica of each repository it has opened and is authorized to sync. The taxonomy below shows where that design sits relative to the databases it draws from, and the sections after it compare GoatDB head-to-head with SQLite — the only other embedded database that runs across server, browser, and edge.

Traditional databases fall into two camps. Remote databases (PostgreSQL, Redis) store data on a server — every read and every write pays a network round-trip, which dominates the operation. Embedded databases (SQLite) eliminate the network by running in-process — point reads measured here take ~13μs — but they offer no sync, no collaboration, and no per-write signed provenance (SQLite can be encrypted at rest via extensions such as SQLCipher; what it lacks is tamper-evident authorship of each individual write).

GoatDB is a third option: its memory model mirrors how desktop apps handle files. When your app opens a repository, that repository's full dataset loads into memory — like a word processor opening a document. After that one-time cost, reads complete in ~2μs with no network hop and no disk I/O. The app explicitly controls which repositories are in memory by opening and closing them, the same way users open and close files. Explicit user interactions — opening a project, switching a workspace, loading a document — map directly to db.open() and db.closeRepo() calls, so the startup cost is expected and bounded. Sync, structural merge, and secure-mode cryptographic signing run entirely in the background. The network never appears in your hot path.

The table below places GoatDB alongside the databases it draws from.

GoatDBSQLiteRedisPostgreSQL
CategoryEmbedded, memory-first, syncingEmbedded, local-onlyRemote, in-memoryRemote, disk-based
Read latency~2μs (memory lookup, measured)~13μs (FFI + B-tree, measured)Network round-trip per readNetwork round-trip per read
Runs in browserYes (OPFS)Yes (WASM)NoNo
Built-in syncCommit-graph sync with structural mergeNonePub/sub (no merge)Logical replication
Offline supportFull read/writeFull read/writeNoneNone
Memory modelOpen repos load fully into RAM, like opening a file; app explicitly controls which repos are loadedDisk-backed, pages on demandEntire dataset in RAMDisk-backed, pages on demand
Data modelCommit graph with deterministic structural mergeB-tree + ACID transactionsKey-value / streamsRelational + ACID

Redis and PostgreSQL are not benchmarked here — they require a network hop per operation, so any latency figure for them describes the network and deployment topology more than the database. They appear above for categorical contrast only; every measured comparison on this page is GoatDB vs SQLite, both running in-process on the same machine.

Head-to-Head: GoatDB vs SQLite

Both databases in their recommended production configurations: GoatDB with cryptographic signing and relaxed durability (crash-safe via append-only log), SQLite with WAL mode and synchronous=NORMAL. The highlighted value marks the robust winner of each row (gap holds even at the edges of measurement noise); all other values render plain. Reproduce locally: deno task bench

Comparison tables report the trimmed mean (outlier-resistant); Detailed Statistics reports the raw arithmetic mean alongside median, stddev, and CV. The two differ slightly for noisy operations — both come from the same run.

Read the warm rows carefully. GoatDB's warm query figures measure a cache hit on an already-materialized live-query result, not query execution. That makes the warm comparison asymmetric by construction — see Query Operations Explained.

Query Operations Explained

The query benchmarks test three distinct capabilities:

Filter queries are benchmarked in two configurations:

  • Cold — first-access cost: GoatDB creates a new query object and performs a full O(n) scan evaluating a JavaScript predicate per item; SQLite creates a new connection and runs an equivalent WHERE clause. This represents the worst case: no caching, no reuse.
  • Warm — repeated-access cost: GoatDB reuses the same live query instance (pooled by config hash) and returns its age-tracked frozen result array in O(1) when no data has changed; SQLite reuses a persistent connection with a prepared statement and a warm OS page cache. This represents steady-state performance after the first access.
The warm rows are not a like-for-like comparison

GoatDB's warm figure is cached live-query result retrieval: a cache hit returning an already-materialized frozen array. No predicate is evaluated and no rows are built. SQLite's warm figure is query execution — the prepared statement re-runs and re-materializes rows on every call, because SQLite has no subscribed, self-maintaining result set. The warm rows therefore answer "what does it cost to read a live query's current results again?", not "what does it cost to run this query?". They support the architectural claim that GoatDB keeps a result set live at near-zero cost. The cold rows are the like-for-like execution comparison.

The parenthetical shows dataset size and result count — e.g., "100k → 1k results" scans 100,000 items cold and returns 1,000 matches; warm, it returns the same 1,000 items directly from cache with no scan.

Filter + sort adds a custom sort comparator on top of predicate filtering (GoatDB) or ORDER BY (SQLite), measuring the combined cost. Same cold/warm split applies.

Live query updates measure reactive performance: a query is subscribed to a 100k-item dataset, then N items are modified across the predicate boundary, and the query results are refreshed. The measurement includes both write latency (N commits/UPDATEs) and query refresh time. In GoatDB, queries subscribe to changes and update incrementally via event propagation. In SQLite, the equivalent is N UPDATE statements followed by a full re-query — there is no subscription mechanism. In the browser, GoatDB queries yield cooperatively to keep the UI at 60fps, so wall-clock time includes scheduling overhead.

Server (Deno / Node.js)

OperationGoatDBSQLite
Create instance1.2ms80.2µs
Open database (empty)315.5µs346.2µs
Open database (100k items)629.6ms116.5µs
Create item46.7µs27.1µs
Read item1.8µs13.0µs
Update item6.6µs21.1µs
Bulk create 100 items3.8ms149.2µs
Bulk read 100 items167.0µs221.9µs
Write 100k items3610.5ms171.4ms
Read 100k items (cold)283.1ms67.8ms
Read 100k items (warm)5.0ms65.1ms
Filter query cold (100 items)31.3µs40.3µs
Filter query warm (100 items)0.4µs29.2µs
Filter query cold (100k → 1k results)121.0ms572.4µs
Filter query warm (100k → 1k results)0.7µs524.5µs
Filter query cold (100k → 10k results)122.7ms5.5ms
Filter query warm (100k → 10k results)0.3µs5.4ms
Filter + sort query cold (100 items)6.1µs ±268%21.8µs
Filter + sort query warm (100 items)0.4µs3.0µs
Live query update (100 items)177.7µs913.8µs
Live query update (1k items)913.3µs3.1ms
Live query update (10k items)8.3ms22.2ms
Count operation2.0µs7.5µs
Keys operation3.8µs11.6µs

Data: Deno. Node.js results are comparable for most operations (largest divergences in this run: warm 100k read ~1.9×, 100k write ~1.25×) — see Detailed Statistics for both.

Browser

GoatDB uses OPFS (Origin Private File System); SQLite uses WASM.

GoatDB writes return after committing to memory (OPFS persistence is batched in a background worker); SQLite WASM writes include synchronous OPFS persistence. This difference primarily affects single-item write latencies.

OperationGoatDBSQLite (WASM)
Create instance12.1ms11.6ms
Open database (empty)1.7ms1.4µs
Open database (100k items)647.2ms2.9ms
Create item52.0µs4.5ms
Read item1.5µs490.6µs
Update item7.0µs3.7ms
Bulk create 100 items4.3ms7.3ms
Bulk read 100 items186.2µs44.5ms
Read 100k items (cold)237.5ms389.8ms
Read 100k items (warm)5.3ms335.1ms
Filter query cold (100 items)28.1µs621.5µs
Filter query warm (100 items)<0.1µs557.2µs
Filter query cold (100k → 1k results)631.1ms4.1ms
Filter query warm (100k → 1k results)<0.1µs3.1ms
Filter query cold (100k → 10k results)633.3ms36.6ms
Filter query warm (100k → 10k results)<0.1µs28.5ms
Filter + sort query cold (100 items)5.6µs538.0µs
Filter + sort query warm (100 items)<0.1µs456.5µs
Live query update (100 items)208.5µs460.7ms
Live query update (1k items)1.5ms4532.3ms
Live query update (10k items)44.7ms45446.7ms
Count operation2.0µs459.4µs
Keys operation5.0µs486.5µs

Omitted rows perform comparably across environments — see Detailed Statistics for full browser data.

Why Browser Results Differ

Three phenomena explain why the server and browser tables tell such different stories:

  1. GoatDB is environment-invariant. Data lives in the JS heap regardless of runtime. Browser reads (~1.5μs) are nearly identical to server reads (~1.8μs) — both are Map lookups. The runtime boundary barely registers.

  2. SQLite pays compounding browser overhead. Every browser SQLite operation crosses JS→WASM, then WASM→OPFS with synchronous I/O. On the server, SQLite uses native FFI + OS page cache. For the point-read measurements above, the gap is about 7× on the server (1.8us vs 13.0us) and over 300× in the browser (1.5us vs 490.6us). Live-query updates are a separate measurement: they include writes plus reactive result maintenance. Each individual SQLite UPDATE in the browser pays the full WASM+OPFS round-trip, so operations that issue many writes (live query updates) see the largest amplification. SQLite's :memory: mode bypasses all kernel filesystem interactions (sqlite.org/forum/forumpost/7eee1ce2e4f97f47); browser SQLite requires workarounds (Asyncify, SharedArrayBuffer+Atomics) that add 2–5× overhead (powersync.com/blog/sqlite-persistence-on-the-web).

  3. Cold read direction flip. Read 100k cold is GoatDB-slower on server (283ms vs 68ms) but GoatDB-faster in browser (238ms vs 390ms). On the server, SQLite's memory-mapped pages-on-demand wins for sequential scans. In the browser, each page fetch pays the WASM+OPFS boundary cost, making pages-on-demand a liability — while GoatDB's bulk-load-and-deserialize pays the OPFS cost once.

Interpreting the Results

The database is the application cache. After the initial open, item data lives in memory — reads are pure Map lookups with no FFI, no B-tree traversal, and no disk I/O.

  • Point reads and metadata: GoatDB point reads are memory lookups — about 7× faster on the server and over 300× faster in the browser in these measurements. Count/keys are 3–4× faster on the server.
  • Cold filter queries: SQLite wins on large scans — B-tree indexing beats a full predicate scan by ~22× (100k → 10k results) and ~210× (100k → 1k results). At 100 items the two are within noise. Cold = first-access cost, and this is the like-for-like execution comparison.
  • Warm filter queries: not a query-execution comparison — GoatDB returns an already-computed live-query result in O(1) (cache hit), while SQLite re-runs the prepared statement. GoatDB's own cold-to-warm ratio is ~80× at 100 items and >100,000× at 100k items; SQLite's warm improvement over cold is moderate (prepared statement + page cache).
  • Live query updates: GoatDB wins — incremental event propagation vs full re-query. Browser: cooperative yielding adds scheduling overhead (not directly comparable to SQLite's blocking queries).
  • Cold start: SQLite wins — pages-on-demand = near-zero open cost.

Expected outcome after running deno task bench:

  • GoatDB warm reads: ~50× faster than cold (pure Map lookups)
  • GoatDB warm queries: orders of magnitude faster than cold (~80× at 100 items, >100,000× at 100k items) — O(1) cached array, no predicate scan
  • SQLite warm reads: similar to cold (B-tree traversal + FFI cost is irreducible)
  • SQLite warm queries: moderate improvement over cold from prepared statement reuse and warm page cache

Cold vs. warm reads: Read 100k items (cold) measures first-access deserialization — GoatDB lazily parses each commit's JSON on the first valueForKey call, so 100k items means 100k JSON parses. Read 100k items (warm) runs one untimed full scan first to populate all caches, then times a second scan — this is GoatDB's steady-state: pure Map lookups, 5.0ms to re-read all 100k items. Both databases receive identical treatment (one untimed warmup scan, then one timed scan), making the warm comparison the fairest measure of each database's in-process read throughput.

The open cost maps naturally to user-initiated actions — opening a project, switching a workspace, loading a document. Users already expect a brief load when they open something; what they don't expect is latency on every subsequent interaction. GoatDB pays the cost once at open time and then runs at memory speed, matching the mental model modern apps have trained users to expect.

RequirementBest fitWhy
Read-heavy, long-running processGoatDB~2μs reads after one-time startup load; pay the cold-start cost once
Reactive local queriesGoatDBIncremental query updates as local data changes, no query polling
Offline-first / multi-device syncGoatDBBuilt-in commit-graph sync with deterministic structural merge
Distributed AI agentsGoatDBRuns in-process on server, edge, and browser; reactive state; optional cryptographic signing
Bulk write ingestSQLiteB-tree + WAL optimized for batch inserts
Very large datasets (many repos open simultaneously)SQLite / PostgreSQLGoatDB loads each open repo fully into RAM; keep the in-memory set manageable by opening only the repos you need and closing the rest
Short-lived processes (serverless, CLI)SQLiteNear-zero startup cost; no warm-up required
Multi-tenant server with complex queriesPostgreSQLSQL, joins, stored procedures, row-level security
High-throughput caching layerRedisPurpose-built in-memory cache: TTL eviction, pub/sub (not benchmarked here)

Configuration Variants

The comparisons below isolate specific GoatDB configuration options. Only operations where configurations diverge past the color-coding threshold (>1.3× for millisecond-scale, >1.5× for microsecond-scale) are shown.

Trusted Mode — Bypassing Signatures

GoatDB signs every commit with Ed25519 by default, enabling multi-peer and untrusted environments. For single-user or fully-trusted local scenarios you can opt out with trusted: true, which removes the signing overhead.

OperationGoatDBGoatDB (Trusted)
Create item46.7µs26.3µs
Read item1.8µs ±36%1.1µs
Update item6.6µs3.9µs
Bulk create 100 items3.8ms1.1ms
Write 100k items3610.5ms704.0ms

Data: Deno. Only operations where trusted mode differs >1.3-1.5× — omitted rows are comparable. See Detailed Statistics for full data.

Trusted mode removes roughly 3–5× overhead on bulk writes (3.4× on bulk create, 5.1× on the 100k write). Reads are unaffected by signing in either mode.

Durable Mode Comparison (per-operation fsync)

GoatDB (Durable) adds per-operation fsync while keeping full cryptographic signing — isolating the cost of waiting for disk I/O. Only operations where at least one configuration differs meaningfully are shown.

Crash safety without fsync

GoatDB's append-only log remains crash-safe even without fsync — incomplete writes are discarded on restart. SQLite synchronous=OFF risks database corruption on crash. GoatDB also exposes flush() for application-controlled durability.

OperationGoatDB (Durable)GoatDBSQLiteSQLite Fast-Unsafe
Create instance1.1ms1.2ms80.2µs79.2µs
Open database (empty)293.8µs315.5µs346.2µs133.7µs
Open database (100k items)691.3ms629.6ms116.5µs154.5µs
Create item661.7µs46.7µs27.1µs13.9µs
Read item1.5µs1.8µs13.0µs10.2µs
Update item93.0µs6.6µs21.1µs10.6µs
Bulk create 100 items12.0ms3.8ms149.2µs135.8µs
Bulk read 100 items141.9µs167.0µs221.9µs147.4µs
Write 100k items3902.3ms3610.5ms171.4ms139.6ms
Read 100k items (cold)295.7ms283.1ms67.8ms68.5ms
Read 100k items (warm)6.1ms5.0ms65.1ms63.5ms
Filter query cold (100 items)10.8µs ±263%31.3µs40.3µs36.6µs
Filter query warm (100 items)0.7µs0.4µs29.2µs25.9µs
Filter query cold (100k → 1k results)120.0ms121.0ms572.4µs568.1µs
Filter query warm (100k → 1k results)0.3µs0.7µs524.5µs515.0µs
Filter query cold (100k → 10k results)125.1ms122.7ms5.5ms5.4ms
Filter query warm (100k → 10k results)1.9µs0.3µs5.4ms5.1ms
Filter + sort query cold (100 items)3.8µs6.1µs21.8µs20.2µs
Filter + sort query warm (100 items)1.5µs0.4µs3.0µs2.0µs
Live query update (100 items)165.5µs177.7µs913.8µs798.6µs
Live query update (1k items)951.9µs913.3µs3.1ms2.8ms
Live query update (10k items)8.7ms8.3ms22.2ms20.5ms
Count operation2.0µs2.0µs7.5µs7.0µs
Keys operation1.8µs3.8µs11.6µs9.6µs

Data: Deno. Only operations where at least one column differs >1.3-1.5× — omitted rows are comparable. See Detailed Statistics for full data.

Storage Formats: Binary vs JSONL

GoatDB defaults to binary format (.goat, fixed-layout binary with length-prefixed framing). An optional JSONL format stores each commit as a plain-text JSON line, making the database human-readable with cat, jq, or any text editor — useful for debugging and data recovery. Only operations where the formats diverge meaningfully are shown.

OperationBinary (default)JSONL
Create instance1.2ms6.6ms
Open database (empty)315.5µs1.6ms
Open database (100k items)629.6ms929.2ms
Create item46.7µs22.1µs
Read item1.8µs0.6µs
Update item6.6µs2.9µs
Read 100k items (cold)283.1ms104.4ms
Count operation2.0µs0.8µs
Keys operation3.8µs1.7µs

Data: GoatDB on Deno. Only operations where formats diverge >1.3-1.5× — omitted rows are comparable. See Detailed Statistics for full data.

The binary format (the default) produces smaller files on disk and enables fast sequential I/O. JSONL is useful for debugging — the log is human-readable with cat or jq. As the table above shows, JSONL is currently faster for some steady-state operations; binary encoding overhead is a known trade-off for compactness.

Methodology

1–20 samples per operation with warmup (most operations use 7–10; live query updates use 20, except the 10k case which runs once). Comparison tables report the trimmed mean; the detailed tables below report the raw arithmetic mean plus median, stddev, and CV — both from the same run, so noisy operations differ slightly between the two views. The highlighted value in each comparison row marks the robust winner — the gap (>1.3× ms / >1.5× μs) holds even at the edges of measurement noise; all other values render plain, including statistical ties (ranges overlap), with high variance flagged by a ±CV suffix.

Operations with fewer than 3 samples should be treated as indicative rather than precise. In particular, live query update at 10k items runs a single iteration due to its long execution time.

Test environment
PlatformHardwareRuntime
DenoApple M4 Pro, 24GB RAMdeno 2.9.1 (darwin aarch64)
Node.jsApple M4 Pro, 24GB RAMnode v24.14.1 (darwin arm64)
BrowserApple M4 Pro, 24GB RAM (OPFS)browser Chrome 149.0 (Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/149.0.7827.55 Safari/537.36)
Full statistics per suite

GoatDB

OperationAverageMedianStddevCVSamplesThroughput
Create instance1.2ms1.2ms217.4µs18%10811 ops/s
Open database (empty)315.5µs308.6µs29.8µs9%73K ops/s
Open database (100k items)629.6ms624.3ms22.0ms3%72 ops/s
Create item46.7µs45.4µs12.0µs26%1021K ops/s
Read item2.0µs1.8µs0.7µs36%10504K ops/s
Update item6.6µs6.6µs1.2µs18%10152K ops/s
Bulk create 100 items3.8ms3.8ms301.5µs8%10262 ops/s
Bulk read 100 items172.6µs167.1µs21.2µs12%106K ops/s
Write 100k items3610.5ms3580.2ms73.9ms2%70 ops/s
Read 100k items (cold)283.1ms283.7ms12.3ms4%74 ops/s
Read 100k items (warm)5.8ms4.9ms2.3ms39%7172 ops/s
Filter query cold (100 items)164.6µs32.2µs408.9µs248%106K ops/s
Filter query warm (100 items)1.8µs0.3µs4.5µs253%10567K ops/s
Filter query cold (100k → 1k results)121.8ms121.1ms3.1ms3%108 ops/s
Filter query warm (100k → 1k results)1.8µs0.8µs3.2µs177%10553K ops/s
Filter query cold (100k → 10k results)122.7ms122.2ms4.2ms3%78 ops/s
Filter query warm (100k → 10k results)1.0µs0.3µs1.7µs177%71.02M ops/s
Filter + sort query cold (100 items)54.3µs5.8µs145.6µs268%1018K ops/s
Filter + sort query warm (100 items)0.6µs0.4µs0.7µs108%101.58M ops/s
Live query update (100 items)177.7µs166.8µs26.4µs15%206K ops/s
Live query update (1k items)940.9µs905.3µs134.2µs14%201K ops/s
Live query update (10k items)8.3ms8.3ms<0.1µs0%1120 ops/s
Count operation2.0µs2.0µs0.5µs25%10508K ops/s
Keys operation3.8µs3.4µs1.3µs33%10263K ops/s

GoatDB (Trusted)

OperationAverageMedianStddevCVSamplesThroughput
Create instance1.1ms1.1ms214.9µs19%10900 ops/s
Open database (empty)349.4µs311.2µs92.3µs26%73K ops/s
Open database (100k items)751.9ms671.9ms174.7ms23%71 ops/s
Create item26.3µs25.4µs3.0µs11%1038K ops/s
Read item1.2µs1.1µs0.4µs31%10860K ops/s
Update item3.9µs3.8µs0.7µs17%10258K ops/s
Bulk create 100 items1.1ms1.1ms107.5µs10%10886 ops/s
Bulk read 100 items142.5µs139.5µs14.2µs10%107K ops/s
Write 100k items704.0ms688.5ms33.9ms5%71 ops/s
Read 100k items (cold)301.9ms289.4ms32.0ms11%73 ops/s
Read 100k items (warm)5.4ms5.2ms792.2µs15%7185 ops/s
Filter query cold (100 items)62.0µs10.9µs154.8µs250%1016K ops/s
Filter query warm (100 items)0.6µs0.6µs0.4µs60%101.59M ops/s
Filter query cold (100k → 1k results)111.5ms111.2ms2.8ms3%109 ops/s
Filter query warm (100k → 1k results)0.8µs0.6µs0.5µs60%101.20M ops/s
Filter query cold (100k → 10k results)118.1ms119.1ms2.0ms2%78 ops/s
Filter query warm (100k → 10k results)0.5µs0.3µs0.6µs130%72.05M ops/s
Filter + sort query cold (100 items)41.0µs3.5µs107.8µs263%1024K ops/s
Filter + sort query warm (100 items)0.6µs0.5µs0.4µs68%101.59M ops/s
Live query update (100 items)163.2µs162.3µs11.5µs7%206K ops/s
Live query update (1k items)874.8µs880.4µs56.2µs6%201K ops/s
Live query update (10k items)7.9ms7.9ms<0.1µs0%1126 ops/s
Count operation2.1µs1.8µs0.7µs32%10481K ops/s
Keys operation4.3µs2.8µs4.5µs105%10231K ops/s

GoatDB (Durable)

OperationAverageMedianStddevCVSamplesThroughput
Create instance1.1ms990.4µs223.1µs21%10935 ops/s
Open database (empty)293.8µs295.2µs26.6µs9%73K ops/s
Open database (100k items)691.3ms698.7ms15.3ms2%71 ops/s
Create item661.7µs649.3µs136.3µs21%102K ops/s
Read item1.5µs1.5µs0.4µs24%10684K ops/s
Update item99.6µs93.4µs23.6µs24%1010K ops/s
Bulk create 100 items12.6ms12.2ms1.5ms12%1079 ops/s
Bulk read 100 items144.7µs142.2µs9.9µs7%107K ops/s
Write 100k items3902.3ms3832.0ms221.7ms6%70 ops/s
Read 100k items (cold)295.7ms294.4ms7.3ms2%73 ops/s
Read 100k items (warm)7.4ms6.1ms3.3ms45%7136 ops/s
Filter query cold (100 items)76.5µs10.7µs201.6µs263%1013K ops/s
Filter query warm (100 items)0.7µs0.5µs0.4µs63%101.52M ops/s
Filter query cold (100k → 1k results)120.0ms119.3ms2.3ms2%108 ops/s
Filter query warm (100k → 1k results)0.5µs0.4µs0.3µs67%102.16M ops/s
Filter query cold (100k → 10k results)125.1ms123.9ms3.2ms3%78 ops/s
Filter query warm (100k → 10k results)2.4µs2.2µs1.5µs60%7412K ops/s
Filter + sort query cold (100 items)44.5µs3.6µs122.6µs275%1022K ops/s
Filter + sort query warm (100 items)1.5µs1.6µs0.5µs34%10686K ops/s
Live query update (100 items)177.5µs165.6µs47.6µs27%206K ops/s
Live query update (1k items)951.9µs952.3µs58.7µs6%201K ops/s
Live query update (10k items)8.7ms8.7ms<0.1µs0%1115 ops/s
Count operation4.5µs2.2µs5.7µs126%10221K ops/s
Keys operation1.9µs1.9µs0.4µs23%10531K ops/s

GoatDB JSONL

OperationAverageMedianStddevCVSamplesThroughput
Create instance6.6ms6.1ms4.7ms71%10151 ops/s
Open database (empty)1.6ms999.2µs1.2ms74%7638 ops/s
Open database (100k items)1015.3ms897.7ms243.0ms24%71 ops/s
Create item22.1µs21.5µs3.2µs15%1045K ops/s
Read item0.7µs0.6µs0.2µs29%101.53M ops/s
Update item3.2µs2.9µs1.3µs40%10309K ops/s
Bulk create 100 items3.7ms3.5ms620.2µs17%10271 ops/s
Bulk read 100 items143.9µs138.8µs24.5µs17%107K ops/s
Write 100k items3793.2ms3721.2ms167.9ms4%70 ops/s
Read 100k items (cold)108.9ms105.1ms12.5ms11%79 ops/s
Read 100k items (warm)11.2ms11.6ms5.3ms47%789 ops/s
Filter query cold (100 items)65.9µs10.8µs165.4µs251%1015K ops/s
Filter query warm (100 items)0.2µs0.2µs0.1µs49%104.36M ops/s
Filter query cold (100k → 1k results)127.9ms127.8ms7.6ms6%108 ops/s
Filter query warm (100k → 1k results)0.2µs0.2µs0.1µs41%105.57M ops/s
Filter query cold (100k → 10k results)124.1ms123.0ms3.0ms2%78 ops/s
Filter query warm (100k → 10k results)0.2µs0.2µs0.1µs64%75.60M ops/s
Filter + sort query cold (100 items)36.6µs4.4µs94.1µs257%1027K ops/s
Filter + sort query warm (100 items)0.2µs0.2µs0.1µs59%104.14M ops/s
Live query update (100 items)160.9µs161.0µs11.2µs7%206K ops/s
Live query update (1k items)913.8µs924.1µs50.2µs5%201K ops/s
Live query update (10k items)8.7ms8.7ms<0.1µs0%1114 ops/s
Count operation0.8µs0.8µs0.1µs11%101.22M ops/s
Keys operation1.7µs1.8µs0.3µs15%10580K ops/s

SQLite

OperationAverageMedianStddevCVSamplesThroughput
Create instance80.2µs80.2µs7.3µs9%1012K ops/s
Open database (empty)368.0µs356.8µs62.0µs17%73K ops/s
Open database (100k items)116.5µs114.8µs6.9µs6%79K ops/s
Create item27.1µs26.8µs3.2µs12%1037K ops/s
Read item13.0µs12.1µs2.5µs19%1077K ops/s
Update item22.5µs21.6µs3.6µs16%1044K ops/s
Bulk create 100 items150.2µs149.1µs4.3µs3%107K ops/s
Bulk read 100 items221.9µs218.5µs10.0µs4%105K ops/s
Write 100k items171.4ms166.7ms12.3ms7%76 ops/s
Read 100k items (cold)67.8ms67.2ms3.3ms5%715 ops/s
Read 100k items (warm)70.8ms63.8ms15.6ms22%714 ops/s
Filter query cold (100 items)40.3µs39.9µs3.2µs8%1025K ops/s
Filter query warm (100 items)29.2µs28.3µs2.1µs7%1034K ops/s
Filter query cold (100k → 1k results)580.6µs571.8µs29.8µs5%102K ops/s
Filter query warm (100k → 1k results)524.5µs523.5µs6.8µs1%102K ops/s
Filter query cold (100k → 10k results)5.8ms5.5ms701.5µs12%10173 ops/s
Filter query warm (100k → 10k results)5.7ms5.4ms653.5µs12%10177 ops/s
Filter + sort query cold (100 items)23.1µs22.0µs4.5µs20%1043K ops/s
Filter + sort query warm (100 items)3.2µs2.9µs0.8µs25%10310K ops/s
Live query update (100 items)920.1µs920.5µs43.4µs5%201K ops/s
Live query update (1k items)3.3ms3.2ms431.9µs13%20307 ops/s
Live query update (10k items)22.2ms21.1ms4.1ms19%2045 ops/s
Count operation7.9µs7.5µs1.6µs20%10126K ops/s
Keys operation12.4µs10.9µs2.9µs24%1081K ops/s

SQLite Fast-Unsafe

OperationAverageMedianStddevCVSamplesThroughput
Create instance79.2µs77.2µs9.6µs12%1013K ops/s
Open database (empty)133.7µs133.8µs18.9µs14%77K ops/s
Open database (100k items)172.8µs150.4µs49.7µs29%76K ops/s
Create item13.9µs13.0µs3.5µs25%1072K ops/s
Read item10.2µs9.6µs2.0µs20%1098K ops/s
Update item10.6µs10.3µs1.1µs10%1094K ops/s
Bulk create 100 items135.8µs132.3µs8.9µs7%107K ops/s
Bulk read 100 items147.4µs146.9µs11.1µs8%107K ops/s
Write 100k items143.8ms138.9ms11.3ms8%77 ops/s
Read 100k items (cold)172.0ms67.0ms274.1ms159%76 ops/s
Read 100k items (warm)63.5ms61.5ms3.8ms6%716 ops/s
Filter query cold (100 items)36.6µs36.6µs3.6µs10%1027K ops/s
Filter query warm (100 items)25.9µs25.4µs1.1µs4%1039K ops/s
Filter query cold (100k → 1k results)1.0ms558.3µs1.5ms141%10974 ops/s
Filter query warm (100k → 1k results)520.9µs507.6µs24.5µs5%102K ops/s
Filter query cold (100k → 10k results)6.0ms5.4ms1.5ms25%10166 ops/s
Filter query warm (100k → 10k results)5.3ms5.1ms564.0µs11%10189 ops/s
Filter + sort query cold (100 items)20.2µs18.2µs5.7µs28%1049K ops/s
Filter + sort query warm (100 items)2.2µs1.9µs0.9µs39%10447K ops/s
Live query update (100 items)798.6µs792.8µs47.1µs6%201K ops/s
Live query update (1k items)2.8ms2.9ms264.1µs9%20353 ops/s
Live query update (10k items)20.5ms20.2ms3.0ms14%2049 ops/s
Count operation7.0µs6.8µs1.2µs18%10144K ops/s
Keys operation9.6µs9.3µs1.5µs15%10104K ops/s

Benchmark Scope

The benchmarks on this page measure local, single-machine operations — reads, writes, queries, and startup costs — comparing GoatDB against SQLite on the same hardware. They do not measure distributed performance.

Distributed/continuity benchmarks (sync latency under partition, multi-peer convergence, recovery from replicas, offline-to-online catch-up) are planned but do not yet have results. See the section below.

SQLite and storage-format comparisons are presented as engineering evidence; the primary value proposition is GoatDB's sync, provenance, and authorization capabilities, not raw single-node throughput.

Continuity Benchmarks (Future)

The following benchmark suites are planned. No results are available yet.

Sync Latency

  • Local mutation latency
  • Remote wake and sync fan-out P50/P95/P99

Reconnect and Recovery

  • Late-join catch-up after partition of varying duration (30s, 5min, 1hr)
  • Server heal from available authorized replicas
  • Signed provenance verification throughput

Multi-Peer Scenarios

  • N humans + agents + devices concurrently editing
  • Concurrent convergence under contention
  • Dropped, reordered, and duplicated network conditions

Scalability

  • Crash recovery under load
  • N simultaneous peers (10, 100, 500)

Running Your Own Benchmarks

git clone https://github.com/goatplatform/goatdb
cd goatdb
deno task bench

The benchmark suite runs on Deno, Node.js, and browser automatically.