Skip to content

Conversation

@longfellowone
Copy link
Contributor

Fixes #808

Summary

  • Prevent OPFS lock stalls on rapid refresh by deferring worker termination when pagehide fires while open is in-flight.
  • Make pagehide handling idempotent and ensure a deferred close runs after open resolves.

Changes

  • WASQLiteOpenFactory: ignore repeat pagehide; if open is in-flight, defer close until after open completes; terminate worker on open error.
  • Add opfs_pagehide regression tests (pagehide during open, idempotent pagehide, deferred close).

Tests

  • pnpm --filter @powersync/web run build:tsc
  • pnpm vitest --run tests/opfs_pagehide.test.ts
  • pnpm vitest

@changeset-bot
Copy link

changeset-bot bot commented Jan 4, 2026

🦋 Changeset detected

Latest commit: 1d7e9bb

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 Patch
@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

@stevensJourney
Copy link
Collaborator

It would be good to rebase this PR on #786 for extra sanity. However, I do acknowledge there is still room for improvement there when it comes to Safari.

It looks like the gist of this PR is to terminate the OPFS worker from the previous page before the next page is loaded. I imagine there are some timing concerns where Safari might not be automatically terminating the previous dedicated worker in time.

A few clarifying questions:

What exactly "times out"? Looking at the code, I don't see any current timeout for these lock aquisitions. If there's no timeout, the new worker should just wait indefinitely until the old worker is terminated and releases its lock. Is this a hang rather than a timeout?

Worker termination should release locks automatically. When the browser terminates a worker, any OPFS SyncAccessHandles and locks held by that worker should be released. If the old worker is eventually terminated on page unload (even if delayed), the new worker's lock acquisition should eventually succeed.

Is the bfcache involved? I notice the test sets persisted: true on the pagehide event. Is Safari potentially keeping the old page (and its worker) alive in the back-forward cache, preventing lock release entirely?

For extra context: Could you share which lock is being held that causes this issue? Note that navigator.locks.query() will show Web Locks API locks (like the open-wasqlite-db lock used in the worker).

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.

OPFSCoopSyncVFS: Database initialization timeout on page refresh (Safari)

2 participants