Skip to content
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

Draft
wants to merge 3 commits into
base: fix-tests
Choose a base branch
from
Draft

Conversation

stevensJourney
Copy link
Collaborator

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.

  • IndexedDB VFS: A MessagePort to the shared SQLite worker is passed to the sync worker, allowing direct DB access.
  • OPFS VFS: A 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.

Copy link

changeset-bot bot commented Feb 10, 2025

🦋 Changeset detected

Latest commit: 9458ab5

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

// 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;
Copy link
Contributor

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?

Copy link
Collaborator Author

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.

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.

2 participants