Skip to content

Add simple OPFS file system with auto-selection - #1187

Draft
simolus3 wants to merge 6 commits into
mainfrom
simple-opfs-vfs
Draft

simolus3 wants to merge 6 commits into
mainfrom
simple-opfs-vfs

Conversation

@simolus3

@simolus3 simolus3 commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

This ports the simple OPFS file system from Dart's package:sqlite3 to the web SDK. Our existing OPFS options (AccessHandlePoolVFS and OPFSCoopSyncVFS) have to rely on SQLITE_BUSY retry 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:

  1. Shared workers (Firefox, Safari technology preview): According to web standards and implementations in good browsers, shared workers can spawn dedicated workers. So instead of each tab opening their own dedicated worker, we can have the shared worker emit a MessagePort to a dedicated worker it owns.
  2. 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 our ConcurrentSqliteConnection class, so we don't need to do this in the VFS).
  3. Re-opening (Safari stable): We can let another tab send a signal when it wants to access files, and use that signal to close our file handles (if we're not currently in a transaction). This is used as a fallback when necessary. Acquiring these handles is asynchronous, but so is our SDK. We can do this step in writeLock before 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-sqlite which 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 believe SimpleOpfsVFS is the best VFS option for all apps needing persistent data (apart from OPFSWriteAheadVFS).

Todos and questions

Web unit tests pass, I have tested react-supabase-todolist in 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 AutoVFS perhaps.

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

  1. If we decide to ignore ATTACH and always store temporary tables / materialized CTEs in memory, an important caveat but I think we can get away with that. ↩

@changeset-bot

changeset-bot Bot commented Nov 2, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 91c72c2

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@powersync/web Minor
@powersync/diagnostics-app Patch

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant