-
Notifications
You must be signed in to change notification settings - Fork 39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Fix] OPFS Sleeping Tabs Sync Deadlock #498
base: main
Are you sure you want to change the base?
Conversation
🦋 Changeset detectedLatest commit: 9458ab5 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 |
// Hold the lock while the shared connection is in use. | ||
await new Promise<void>((releaseLock) => { | ||
// We can use the resolver to free the lock | ||
this.releaseSharedConnectionLock = releaseLock; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you just wait for the lockAbortController.signal
here, instead of using a separate callback?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah we can do that. I initially started testing without the AbortController where just this promise was used, using the AbortController signal is cleaner. This callback will be removed.
Overview
We recently introduced OPFS VFS support for the PowerSync web SDK. Unlike IndexedDB-based VFS, which allows a single shared SQLite connection within a shared web worker across multiple tabs, OPFS requires each tab to have its own dedicated SQLite worker.
To manage concurrency, we use Navigator locks, ensuring exclusive access to SQLite connections. Table change notifications are propagated using a BroadcastChannel.
In multi-tab scenarios, a shared sync worker is responsible for syncing operations. This worker requires access to the SQLite database.
MessagePort
to the shared SQLite worker is passed to the sync worker, allowing direct DB access.MessagePort
to the last connected tab’s dedicated SQLite worker is passed to the sync worker. This connection is updated as tabs are opened and closed.The shared sync worker's DB connection is linked to a specific tab. If that tab is frozen or put to sleep (e.g., in Chrome’s Tab Freezing or Edge’s Sleeping Tabs), the connection becomes inaccessible, blocking SQLite queries in the sync worker.
Luckily browsers don't freeze or sleep tabs which are actively holding a web lock. This PR adds a hold on a web lock as soon as the connection is shared to the sync worker. In local testing this prevents tabs from being put to sleep.