Skip to content

v9: Major rework - #18

Merged
prabhu merged 33 commits into
masterfrom
release/v9
Aug 29, 2026
Merged

prabhu merged 33 commits into
masterfrom
release/v9

Conversation

@prabhu

@prabhu prabhu commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

No description provided.

prabhu added 30 commits August 25, 2026 06:45
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
… result codes

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…nsaction and prepare-failure fixes

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…ull query, 20x

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…s little

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…pool

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…r and packaging tests

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…s left running

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…ainer test matrix

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…n Windows in CI

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
… on every check

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
Node availability was checked per target before adding combinations:

- setup-node delivers 26 on every hosted runner (linux/darwin/win,
  x64 and arm64), so the fast `test` job now runs os x node with
  {24, 26} and gains ubuntu-22.04-arm (arm64 Linux was previously
  suite-tested only inside the slower `build` matrix).
- alpine3.20 — the musl build variant — has no Node 26 image
  (node:26-alpine tags start at 3.22), so build-qemu stays on 24 and
  the Node-major musl coverage comes from a consumer: the new
  prebuild-consumer-musl job runs the suite against the
  alpine3.20-built artifact inside node:26-alpine3.22.
- prebuild-consumer (glibc/darwin/win, node 26) downloads the build
  artifacts and runs the suite with PREBUILDS_ONLY=1 — no compile, so
  green means the shipped binary itself passed, proving the napi
  one-build-many-Nodes promise the package is sold on.
- Electron is unaffected: 43.4.1 embeds Node 24.18.1 (measured), so
  those jobs add a different runtime, not a different Node major.

Deliberately not added: node 26 on windows-11-arm and macos-15-intel
(slow runners, napi-identical to covered pairs), musl arm64 x 26
(arch covered by build-qemu), and any change to the musl build floor
(alpine3.20 went EOL 2026-04-01 — bumping the floor is its own
decision). publish gates on both new jobs.

Verified locally: full suite in node:26-alpine3.22 against a musl
prebuild built in-container (750/748/0/2, same as darwin/node 26);
PREBUILDS_ONLY=1 suite green on this machine; actionlint reports no
new findings.
…pins

test (windows-latest, node=26) failed at link: LNK1117 syntax error in
option 'opt:lldltojobs=2', with -flto=thin D9002/LNK4044 warnings on
every project. Chain: node-gyp builds build/config.gypi from the
RUNNING node's process.config (not the headers' config.gypi — that is
only read with --nodedir/--dist-url); the official Windows Node 26
builds are clang-cl/ThinLTO, so process.config carries
enable_thin_lto="true" and lto_jobs="2"; Node 26's common.gypi (new
since 24) turns those into -flto=thin and /opt:lldltojobs=2
AdditionalOptions on Windows; MSVC link.exe is not lld-link and the
latter is a hard LNK1117. Linux/macOS and Node 24 are unaffected
(headers config and official 24 builds say false), which is exactly
the observed matrix.

Fixed upstream in node-gyp 13.0.0 ("disable LTO for addon builds on
Windows", nodejs/node-gyp#3331; 13.0.1 adds a VS2026 fix), so the pin
moves 12.x -> 13.x and resolves to 13.0.1 — 13.0.2 is 23h old and
inside the pnpm minimumReleaseAge gate. The only 13.0.0 breaking
change is the node engine range ^22.22.2 || ^24.15.0 || >=26, above
our >=24 floor in practice.

The three prebuild-consumer failures were transient: all died at
"Set up job" with "Unable to resolve action actions/checkout@<sha>"
before any step, while the same SHA resolved in every other job of
the same run — a GitHub action-resolution blip, not a workflow bug.
All 24 action pins refreshed with gh actlock -u (SHAs spot-verified
against the git refs API): checkout v7.0.1, setup-node v7.0.0,
setup-python v7.0.0, upload-artifact v7.0.1; the rest were already
latest.

Verified locally through node-gyp 13.0.1: full rebuild + suite
(750/748/0/2), and the prebuildify -> PREBUILDS_ONLY=1 path the build
matrix uses (750/748/0). Windows itself can only be proven in CI.
On the ubuntu-22.04-arm build job (run 33074153055), 'round-trips
function' failed with "database is busy: sync methods require a fully
idle database" instead of the expected bind TypeError — once, in the
first integer mode, with the same test green 100ms later in the next
mode. Mechanism, reproduced deterministically on the local prebuild:

a bind-rejected value throws synchronously out of the db-level
wrappers (established v9 semantics), and that throw skips their
trailing finalize — the call's async prepare is abandoned in flight,
so db.pending stays elevated past the point where the driver's await
resolved. Instrumenting the exact 15-path sequence showed +1 pending
accumulated per rejected db-level path (5 after db.run). On a fast
machine the four awaited stmt paths that follow always drain the
stragglers before db.getSync; on a slow, preempted runner one can
still be in flight, and the first sync path meets a busy gate.

The async paths cannot promise idleness at await-resolution (their
internal prepare/finalize round trips are deliberately not part of
the awaited op), so the sync drivers in test/support/bindpaths.js now
await db.wait() first — an exclusive call that runs only at
pending == 0, the same drain discipline the stmt drivers already
apply by resolving from inside stmt.finalize's callback. No assertion
changed: the sync paths must still produce the bind TypeError.

Evidence: PREBUILDS_ONLY node repro shows pending=1 after a
sync-throwing db.get, getSync busy, then wait -> 0 -> getSync OK;
marshalling 20/20 clean; full suite 750/748/0/2.
…eate

electron (ubuntu-latest) on run 33078211871 failed its whole backup
suite: the describe's before-hook threw EEXIST on mkdir 'test/tmp',
cancelling all 14 tests. node --test runs each file in its own
process, and test/support/helper.js's ensureExists did
existsSync-then-mkdirSync — two before-hooks creating test/tmp in the
same instant race the check, and the loser gets EEXIST. Electron
child processes start slower than plain Node's, which widened the
window enough to hit it; the same latent race existed for every
plain-Node run.

mkdirSync(recursive) has mkdir -p semantics: race-free, and an
existing directory is fine. Proven with a 6-process concurrent probe
on a fresh directory, 40 rounds: the old form threw EEXIST in 40/40
rounds, recursive in 0/40. Every other mkdir in test/ and tools/
already used recursive; this was the only holdout. No assertion
changed.

Verified: backup.test.js 14/14, full suite 750/748/0/2, lint clean.
…sion load

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…g SQLCipher build path

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…w builder

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
…er field

Signed-off-by: Prabhu Subramanian <prabhu@appthreat.com>
prabhu added 3 commits August 28, 2026 16:17
The SQLCipher section was renamed at some point but two links still
pointed at #building-for-sqlcipher -- one in the README, one in
docs/install.md. Both are now the real anchor. A link/anchor sweep over
README, MIGRATING-TO-V9, SECURITY and docs/ finds no others, and 33 of
the 35 documented JS snippets parse clean (the other two are deliberate
fragments: a "{ ... }" elision and an anonymous generated function).

docs/performance.md gains "Against a C-extension driver in another
language". Everything there so far compares Node against Node; this adds
an outside check against CPython's apsw over a real 13 GB store, which
is worth recording because it isolates the mechanism rather than just a
ratio. Widening the projection over one fixed plan shows count(*) at
1.02x -- parity, so execution, binding and call overhead are not the
gap -- with the whole difference appearing only once rows are built:
+50 ns per row, +6.5 ns per value. The per-row half is a boundary
crossing, not object construction (calling the generated builder from JS
costs ~7 ns/row), because CPython lets an extension fill a tuple in C
while Node-API has no bulk constructor. Two consequences for callers:
projecting fewer columns is the lever that works, and row mode is not
one -- rowMode: 'array' moves nothing, which the existing "How rows are
built" section already predicted.

That measurement did not come from pnpm run bench, so the file's opening
claim that nothing is hand-timed is now qualified, and the caveats
(warm-cache-only, a 3.53.3-vs-3.53.4 SQLite mismatch, private dataset,
one machine) are listed under Limits of these numbers.
@prabhu
prabhu merged commit 1d2029d into master Aug 29, 2026
55 checks passed
@prabhu
prabhu deleted the release/v9 branch August 29, 2026 10:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant