Repository navigation
Conversation
🦋 Changeset detectedLatest commit: 91c72c2 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
simolus3
force-pushed
the
simple-opfs-vfs
branch
from
November 2, 2026 16:33
e248083 to
c3d9b61
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This ports the simple OPFS file system from Dart's
package:sqlite3to the web SDK. Our existing OPFS options (AccessHandlePoolVFSandOPFSCoopSyncVFS) have to rely onSQLITE_BUSYretry hacks and use internal locks we'd like to avoid on Safari). The simple OPFS file system has none of these problems.The simple OPFS file system gets away without these tricks by cheating on a more fundamental level: We don't actually need a working file system, we just need to persist a single database file and its journal 1. With
PRAGMA journal_mode = TRUNCATE, we don't even have to handle deletions. So a VFS can just pre-open two files and keep synchronous file handles active as long as the database is used.That alone gives us a decent file system if we don't need support for multiple tabs. To add that, there are a few tricks we can use depending on the browser:
MessagePortto a dedicated worker it owns.readwrite-unsafe: In Chrome, multiple tabs can have synchronous file handles open with a special flag. This allows multiple tabs to access the database, we use navigator locks to avoid contention (we already have them in ourConcurrentSqliteConnectionclass, so we don't need to do this in the VFS).writeLockbefore we hand out a connection lease, avoiding the need for the VFS itself to be asynchronous.A simple check queries browser features to use the correct mode. Thanks to file system transparency, no migration is necessary between those modes. The VFS stores data in a subdirectory (
$opfsRoot/powersync/) to avoid any conflict with other file systems.This VFS would be a poor choice for
wa-sqlitewhich wants to be a generic SQLite library supporting multiple databases per opened module. But since we don't need that in the web SDK, I believeSimpleOpfsVFSis the best VFS option for all apps needing persistent data (apart fromOPFSWriteAheadVFS).Todos and questions
Web unit tests pass, I have tested
react-supabase-todolistin Chrome, Firefox and Safari.Since this VFS is a good default choice and has feature detection, I wonder if we should give it a non-OPFS name. Then we could expand feature detection to detect OPFS being unavailable (private tabs on Safari / Firefox) and fall back to an in-memory or IndexedDB VFS. We could just call it
AutoVFSperhaps.Follow-up items
An issue we've seen with Safari is that we ideally don't want workers to acquire navigator locks, as they're not reliably cleared when a tab is closed or reloads (#808). Because the simple VFS doesn't use locks internally, fixing this is "just" a matter of moving our locks (in
ConcurrentSqliteConnection) back to the main tab. That is a big refactoring, but IMO worth addressing in a follow-up PR.AI use: Claude Code helped port snippets from Dart, I also used it for reviews and minor fixes.
Footnotes
If we decide to ignore
ATTACHand always store temporary tables / materialized CTEs in memory, an important caveat but I think we can get away with that. ↩