Skip to content

Characterise scale on all four engines - #18

Merged
chrismuench merged 1 commit into
mainfrom
perf/cross-engine-volume
Jul 20, 2026
Merged

Characterise scale on all four engines#18
chrismuench merged 1 commit into
mainfrom
perf/cross-engine-volume

Conversation

@chrismuench

Copy link
Copy Markdown
Collaborator

Requested follow-up: measure the bring-your-own-server engines at volume rather than assume they scale like the bundled SQLite default.

CrossEngineVolumeTests is fixture-driven — one test profiles whichever engine DISPATCH_TEST_ENGINE selects — and exercises both the keyset cursor and the dashboard dedup-plus-total list (PageAsync, whose grouping was just made sargable in #15).

Measured at 100k rows (full table in docs/database.md):

SQLite PostgreSQL MariaDB SQL Server¹
Keyset page 1 193 ms 137 ms 124 ms 155 ms
Keyset page 20 0 ms 0 ms 0 ms 1 ms
Dashboard list (dedup+total) 518 ms 436 ms 906 ms 1,739 ms
Purge 63k 7.5 s 7.5 s 7.9 s 14.7 s

¹ under x86 emulation on arm64 — its write/purge times are inflated by that, not the engine.

Findings:

  • Keyset pagination is flat on every engine — page 20 = page 1 (~0-1 ms). The property the Message Log depends on holds everywhere.
  • The dashboard list is the heaviest read on every engine and grows with total rows, because it dedups and returns an exact total over the whole filtered set, running its grouping twice per page. So the deferred double-execution optimisation is a real, measured, cross-engine cost — not a SQLite quirk. This is the number that says when it starts to matter.
  • Purge is dominated by its deliberate 100 ms inter-batch pause, by design, on every engine.

Opt-in (DISPATCH_VOLUME_TEST); normal CI skips it.

🤖 Generated with Claude Code

SqliteVolumeTests measured the bundled default at scale; the bring-your-own-server
engines were assumed to behave the same. CrossEngineVolumeTests measures them - it is
fixture-driven, so one test profiles whichever engine DISPATCH_TEST_ENGINE selects,
and it exercises BOTH read paths: the keyset cursor (QueryAsync) and the dashboard
dedup-plus-total list (PageAsync, the path whose grouping was just made sargable).

Measured at 100k rows (docs/database.md has the table). The findings that matter:

  * Keyset pagination is flat on every engine - page 20 costs the same as page 1
    (~0-1 ms), an index seek not a scan. This is the property the Message Log relies
    on and it holds on SQLite, PostgreSQL, MariaDB and SQL Server alike.
  * The dashboard list (PageAsync) is the heaviest read on EVERY engine, 436 ms to
    1.7 s at 100k and growing with total rows, because it deduplicates and returns an
    exact total over the whole filtered set - running its grouping twice per page. So
    the deferred double-execution optimisation is a real, measured, cross-engine cost,
    not a SQLite quirk. This is the number that says when it starts to matter.
  * Purge time is dominated by its deliberate 100 ms inter-batch pause on every
    engine, not the delete - by design, so ingest can take the write lock between
    batches.

Opt-in (DISPATCH_VOLUME_TEST), so normal CI skips it. Assertions catch a collapse (a
scan where a seek was intended), never a slow agent. The printed numbers are the
deliverable; the docs table records a baseline.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@chrismuench
chrismuench merged commit 2f2c571 into main Jul 20, 2026
6 checks passed
@chrismuench
chrismuench deleted the perf/cross-engine-volume branch July 20, 2026 22:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant