Repository navigation
Conversation
Measured comparison: serialize (#643) vs backup (#644)Recommendation for the stated priority of large databases: prefer the backup implementation in #644. It removes the need for a whole-database allocation in WASM, at a measured ~5× latency cost on the largest case. This is a WASM-headroom improvement, not a low-total-memory download implementation: the current destination is still MEMFS. A genuinely low-memory save/download should use a separate file-backed backup API (OPFS/real filesystem), not return a full Actual CI validation
Same-runner timing and memoryThe table below is from run 40303987097: Node v24.21.0 on Linux / AMD EPYC 7763. Both optimized WASM bundles ran on the same runner, in separate child processes, with alternating strategy order. Each size/strategy uses 3 fresh processes and 7 repeated exports per process, with GC between exports. First-export medians have 3 samples; repeated-export medians have 21 samples. Exported data was reopened and verified with
Peak RSS is a whole-process high-water mark measured before round-trip validation; it includes setup/runtime memory and is not an isolated SQLite allocation measurement. These are synthetic 4 KiB BLOB rows with an expression index, not guarantees for every schema or browser. The independent repeat measured 150 / 730 ms repeated-export medians for the largest case, reinforcing the ~5× difference. The decisive memory-limit testBoth production WASM modules declare a 2048 MiB maximum. For a controlled limit test, the script lowered only the WASM memory section's maximum to 64 MiB, leaving the initial memory and application code unchanged. The WebAssembly engine enforced the limit. With a 108.28 MiB database:
This establishes that backup can export databases larger than the permitted WASM heap. It does not establish a tested multi-gigabyte browser file-size ceiling. Available JS/process memory, filesystem buffering, browser buffer limits and storage quotas still matter. Serialization additionally uses one Bundle sizesCombined optimized JS + WASM: serialize 732,429 bytes raw / 353,854 bytes gzip; backup 732,552 raw / 353,688 gzip. The difference is negligible. What a low-memory download needsThe current byte-array APIs both create two full-size additional buffers: serialize uses WASM snapshot + JS output; backup uses a MEMFS destination + JS output. For file size D, both can approach roughly 3D total residency including the source, plus overhead. The next architectural step would be backing the backup destination with a seekable disk-backed file, e.g. an OPFS SyncAccessHandle through a filesystem/VFS adapter in a dedicated worker. Once complete, a browser Incremental backup alone is not a download stream: SQLite may rewrite earlier pages and finalize the header at completion, so a forward-only sink cannot directly consume the writes. The one-off artifact-dependent CI step has been removed after recording these results; the standalone comparison script remains reusable with two dist directories: node test/compare_exports.cjs /path/to/serialize-dist /path/to/backup-dist |
Optimized backup results — supersedes the earlier ~895 ms measurementThe preallocated implementation at Same-runner comparisonGreen CI run 40311200414: Node v24.21.0, Linux, AMD EPYC 7763. Compared current backup, previous backup at
For the largest case:
Independent green repeat 40311191909, on an AMD EPYC 9V45: largest-case repeated medians were 700.57 ms previous backup / 152.89 ms optimized backup / 139.48 ms serialize. Peak RSS was 1828.0 / 1276.5 / 1834.2 MiB, respectively. Exact timing and memory numbers are machine-dependent, but both runs show the same improvement. Correctness and memory limits
Complexity clarificationThe original MEMFS 12.5%-growth strategy is amortized O(n), not O(n²); earlier discussion overstated the asymptotic issue. The substantial constant overhead came from repeated growth copies, the extra final output copy and the backup write path. The optimization preallocates once and fills the result in a linear page pass. The combined optimization was measured; the individual contributors were not separately profiled. Recommendation: use the optimized backup implementation in #644. It now approaches serialize's throughput on large files without requiring a database-sized WASM allocation, and materially reduces total peak process memory. The one-off artifact-download benchmark CI step has been removed after recording these results. No builds, tests or benchmarks ran on the limited development machine. |
Use SQLite's backup API to fill a single preallocated JS-owned export buffer without closing the source connection or allocating a database-sized WASM snapshot. Preserve callbacks, statements, connection settings and transaction state; reject active write transactions without rolling them back. Add export regression coverage and prepare version 1.15.2. Supersedes #642 and #643. Validated by full CI on all WebAssembly variants.
|
Superseded by the merged #644. The optimized backup implementation preserves the connection, avoids a database-sized WASM allocation, and now writes directly into the result buffer. Comparative CI measurements are recorded above. Shipping in v1.15.2. |
Summary
Alternative to #642: use SQLite's
sqlite3_serialize()to export the main database without closing and reopening its connection, rather than restoring individual registrations after reopening.Uint8Array, then free the SQLite allocation and restore the stack, including on errors.Transaction semantics
Keep SQLite's serialization semantics: the snapshot includes uncommitted changes visible to the connection. Export neither commits nor rolls back transactions/savepoints; the original connection can still commit or roll back afterwards, without changing the snapshot.
This intentionally replaces the previous implicit rollback caused by closing the connection.
Database.export()'s doc comment documents this behavior, preserved connection state, the main-database-only scope, snapshot ownership and the fact that JS callbacks/connection-local settings are not persisted in the exported file.Regression coverage
Validation
No builds or tests run locally for this change, as requested for the resource-limited development machine. Relying on this PR's CI to rebuild distribution bundles, run lint/the full WebAssembly and distribution test suites, and generate documentation.
git diff --checkpasses.