Repository navigation
Write-ahead logging in a VFS #315
rhashimoto
started this conversation in
Ideas
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
TL;DR
OPFSWriteAheadVFS is a new synchronous VFS that uses write-ahead logging to achieve significant performance and concurrency improvements. It uses the proposed readwrite-unsafe mode that is currently implemented only on Chromium browsers.
Overview
I wrote a post some time ago on the promise of a couple new browser features that starts with this teaser:
How would you like a persistent browser database that...
A new example VFS, named OPFSWriteAheadVFS, has all these features except one — it doesn’t have similar write transaction overhead to IndexedDB…it’s much faster than that.
OPFSWriteAheadVFS implements write-ahead logging, an alternative journaling strategy with some performance and concurrency benefits. It is very similar in concept to SQLite’s own experimental WAL2 journal mode but it is a completely separate implementation within the VFS and without SQLite knowing anything about it.
SQLite’s standard WAL implementation (aka WAL1) uses a single WAL file, with which read transactions don’t block write transactions or other read transactions, and write transactions don’t block read transactions. However, full checkpoints are needed occasionally to prevent unbounded growth of the WAL file, and performing those checkpoints blocks both readers and writers. OPFSWriteAheadVFS is more like WAL2, switching between two WAL files, so full checkpoints are not required and checkpointing does not block readers or writers.
OPFSWriteAheadVFS makes use of OPFS readwrite-unsafe mode, a proposal to allow concurrent file access from multiple synchronous access handles that is currently supported only on Chromium browsers.
OPFSWriteAheadVFS is being developed in collaboration and consultation with PowerSync.
Who is this VFS for?
OPFSWriteAheadVFS should be considered by anyone wanting more performance and/or concurrency over browser compatibility.
Advantages
Disadvantages
OPFSWriteAheadVFS usage notes
If you want to try this VFS, here are some things you should know.
Compatibility
Special restrictions
These are important workarounds for cases where ordinarily valid SQLite usage can produce an error with OPFSWriteAheadVFS.
Configuration
The default configuration should work reasonably well for typical applications, but the behavior can be adjusted with the following PRAGMA settings:
Performance
Write-ahead logging can improve performance in a couple ways:
Trading durability for performance. With a rollback journal, each write transaction must be flushed to the file system to guarantee atomicity, consistency, and durability (the A, C, and D in ACID). With WAL and WAL2, flushing is only needed for durability; transactions are atomic and consistent without it. If durability is not a requirement then writers can skip flushing, which is by far the most expensive file operation.
Increasing concurrency. Under normal conditions (i.e. without setting exclusive locking mode), readers are never blocked and writers are only blocked by other writers.
The test results below were measured with Chrome on a Mac mini M2.
Single connection
Single-connection performance is roughly equivalent to OPFSCoopSyncVFS, except that write transaction overhead is much faster (see Test 1):
Multiple connections
Here are results of a contention test with 3 readers and 1 writer with OPFSCoopSyncVFS:
With the new OPFSWriteAheadVFS:
Well, that’s just ridiculous. It’s highly unlikely that ~75x reads and ~10x writes is achievable on real workloads, but it does appear that the improvements on multiple connections could be quite dramatic.
Here are results with PRAGMA lazy_lock=READ (the default is READWRITE):
Hmm. The writer slowing down is totally expected, but I didn’t expect the readers to speed up. Maybe we’re saturating the platform capacity?
How it works
OPFSWriteAheadVFS is inspired by and conceptually similar to SQLite’s experimental WAL2 implementation (summary, source docs). It uses two WAL files, and a writer decides to switch from one to the other based on the number of pages in the WAL and whether the currently inactive one has been completely checkpointed to the actual database file. Any connection can checkpoint the inactive WAL file provided that all connections have accepted every transaction on it.
However, the mechanics of how OPFSWriteAheadVFS accomplishes those things is quite different from the SQLite implementation to adapt to the web platform. In SQLite, WAL management is done in shared memory, but in OPFSWriteAheadVFS, each connection maintains its own data structures to look up pages in the WAL, and connections broadcast transaction and checkpoint messages to inform other connections of their actions (with fallback mechanisms in case a message fails to be sent).
OPFSWriteAheadVFS reuses a lot of tricks from other VFS implementations:
A key WAL feature is that readers don’t block writers and writers don’t block readers (writers do block each other). This means that readers and writers should use separate locks to manage access. But when the VFS receives the initial lock request from SQLite, how does it know whether a read or a write transaction is starting? SQLite doesn’t tell us…until now. As of SQLite 3.53, the library sends an experimental hint that it intends to lock the database for writing. OPFSWriteAheadVFS uses this new hint to distinguish readers and writers and identify the appropriate lock to use. This is why using BEGIN or BEGIN DEFERRED for a write transaction is an error with OPFSWriteAheadVFS, because it won’t receive the write hint.
All reactions