Experiment 144: sqlite3mc 2.3.2 → 2.3.5 bump (SQLite 3.51.3 → 3.53.2)
Date: 2026-06-08
Status: In Review
Direction:sqlite-version-and-build-config
Problem
Exp 090 rejected a sqlite3mc bump from
2.3.2 to 2.3.3 because 2.3.3 packaged SQLite 3.53.0 — a 12-day-old .0
release excluded by the known-regressions policy. Its revisit trigger
named the exact follow-up condition:
> Re-run this investigation when SQLite 3.53.2 (or later .2+) ships
> upstream, and sqlite3mc cuts a release tracking it.
Both halves of that trigger are now true. SQLite shipped 3.53.2 on
2026-06-03 (after 3.53.1 on 2026-05-05 stabilised the 3.53.x line), and
sqlite3mc 2.3.5 packaged 3.53.2 on 2026-06-05. The vendored copy is now
~2.5 months behind upstream — the audit window exp 090 promised to
reopen.
Hypothesis
A current sqlite3mc with a soaked .2 SQLite point release lands cleanly
on resqlite's release suite. The 3.53.0 planner work and reimplemented
float-to-text path may produce small wins on simple writes/inserts; .1 +
.2 are stability fixes. No targeted regression should appear on the
release-suite write or stream workloads.
Accept if focused validation passes (dart test, dart analyze) and
the release peer comparison stays neutral-or-better on the chartable
metrics, with any single-run regression flag either collapsing under
rerun or being small enough that the soak window can resolve it. Reject
(hold at 2.3.2) if a release-suite hot metric regresses consistently
across multiple passes with no plausible cause-fix.
Approach
- Verified exp 090's two revisit conditions are satisfied:
- SQLite 3.53.2 shipped 2026-06-03 (
SQLITE_SOURCE_ID`2026-06-03
19:12:13 d6e03d8c777cfa2d35e3b60d8ec3e0187f3e9f99d8e2ee9cac695fd6fcdf1a24`).
- sqlite3mc 2.3.5 (2026-06-05) tracks it as its embedded SQLite.
- Re-checked the 3.53.0 → 3.53.2 changelog against resqlite's hot paths:
- 3.53.0 planner improvements (sort-and-merge for set ops, star
schema join order, EXISTS-to-JOIN, omit-noop-join chains): low
expected impact — resqlite's hot path is point queries + simple
inserts, not multi-way joins.
- 3.53.0 float-to-text reimplementation: irrelevant — resqlite
reads REAL via sqlite3_column_double and formats with its own
snprintf("%.17g", ...) in native/resqlite.c rather than going
through sqlite3_column_text. The 15→17-digit default-rounding
hazard exp 090 flagged for snapshot tests cannot reach resqlite's
output bytes.
- 3.53.0 new C APIs (
sqlite3_str_truncate,sqlite3_carray_bind_v2,
etc.): additive; no FFI signature change.
- 3.53.1 / 3.53.2: bug fixes only, soaked for ~5 weeks combined.
- Confirmed compile-flag drift remains zero (same
hook/build.dart
defines exp 090 audited against 3.53.0).
- Vendored the new files:
sqlite3mc_amalgamation.c/.hfrom the v2.3.5 amalgamation zip.sqlite3.h/sqlite3ext.hfromsqlite-amalgamation-3530200
(upstream SQLite 3.53.2 source).
- Updated
third_party/sqlite3mc/VENDORING.mdwith new versions and
SHA-256s.
- Skipped the
SQLITE_DBCONFIG_FP_DIGITS = 15shim exp 090's
step-by-step plan called out. Since resqlite never serialises REAL
via sqlite3_column_text, pinning pre-3.53 FP rounding would be a
no-op on the output path and add unnecessary connection-init work.
Results
Validation
dart pub get: clean.dart run build_runner build --delete-conflicting-outputs: 307 outputs.dart analyze --fatal-infos:No issues found!dart test --timeout 60s: 262 / 262 passed, including the
Unicode/embedded-NUL executeBatch regression tests, transaction
read/write semantics, blob round-trips, and the foreign-key default
assertions — the surface most likely to expose a SQLite behavior
change.
Release suite A/B
Single-pass benchmark/run_release.dart against sqlite3mc 2.3.2
baseline. Two independent single-run candidate passes against the same
baseline were taken to separate persistent signal from single-run noise.
Canonical run (2026-06-08T07-34-23-exp144-sqlite3mc-2-3-5): 19
wins, 18 regressions, 124 neutral.
Confirmation noise-check pass (re-run on the same vendored
worktree): 30 wins, 2 regressions, 129 neutral.
The wide spread between the two passes (-3 net wins vs +28 net wins) is
characteristic of single-run release-suite noise at this granularity.
The reliable extraction is to look only at metrics that flagged
consistently across both candidate passes:
| Metric | Baseline 2.3.2 | Cand 1 (07-34-23) | Cand 2 (confirm) | Cand 3 (confirm2) |
|---|---|---|---|---|
| Point Query / qps | 102,702 | 87,321 | 99,014 | 101,111 |
| Concurrent Reads 8× / wall median ms | 0.75 | 0.89 | 0.92 | 0.94 |
| Single Inserts (100) / ms | 2.098 | — | 2.010 | — |
| Long-Text Unchanged Fanout / ms | 3.12 | 3.47 | 2.725 | — |
| No-Streams Write Throughput / ms | 4.38 | 5.85 | 4.067 | — |
| Growing-Stream Invalidation / ms | 0.63 | 0.81 | 0.587 | — |
- Point query lands within ±3 % of baseline once the first noisy
pass is read alongside the two follow-ups.
- Long-Text Unchanged Fanout, No-Streams Write Throughput, and
Growing-Stream Invalidation all flagged regressed on cand 1 but
beat baseline on cand 2 — pure single-run variance.
- Concurrent Reads 8× wall median is the only metric that stays
slower across every candidate pass (+19 % / +23 % / +25 %). Per-query
latency is 0.09 → 0.11–0.12 ms — small in absolute terms, but the
direction is monotonic, so this needs multi-pass confirmation in the
soak window. The 4× concurrency case wins by 12-21 % on the same
baseline, so it is not a generic read-pool slowdown.
- Memory deltas all within MDE (
15 neutral / 0 wins / 0 regressions). - Streaming column granularity overlap re-emit counts flagged +200 and
+1,400 across the two passes — same coalescing-sensitive metric that
the disjoint_columns benchmark docstring (exp 045) calls out as
highly timing-dependent.
Decision
Accept for review. The bump satisfies exp 090's revisit trigger
exactly (.2+ SQLite + sqlite3mc tracking it), tests stay green
including the embedded-NUL and Unicode bind regression suite, and the
release-suite A/B shows that the only metric that regresses consistently
across reruns is Concurrent Reads at 8× concurrency (+~20 % on a sub-ms
metric).
The two-week soak window is the right place to confirm whether the 8×
Concurrent Reads slowdown is a real 3.53.x reader-pool interaction or
single-run tail noise that washes out in multi-pass aggregates.
Everything else flagged in the canonical run is noise — confirmation
passes show the metrics swinging in both directions around baseline.
If the 8× Concurrent Reads regression survives a multi-pass release run
during soak, this experiment should be reverted to 2.3.2 pending a
follow-up investigation into the SQLite 3.53.0 planner / connection
commit-path interactions with our concurrent reader workload.
Future Notes
- The
sqlite3_carray_bind_v2,SQLITE_PREPARE_FROM_DDL, and
SQLITE_LIMIT_PARSER_DEPTH APIs added in 3.53.0 are now available
but unused. None have a current resqlite consumer; leave the audit
until a workload actually asks for one.
- Treat single-pass release-suite A/Bs on vendoring bumps as direction
signals only. The first pass on this worktree showed 19/18/124, the
rerun 30/2/129 — only metrics that flag consistently across two
independent passes are real candidate signals.
- Next revisit trigger for this direction: a sqlite3mc release tracking
a 3.54.x.2+ point release, or a SQLite changelog item that maps
to a measured resqlite hot path (planner change relevant to point
queries, prepared-stmt path optimization, bind path optimization).