Skip to content

Commit 21385c6

Browse files
fix: improve OPFS multi-tab connection recovery
1 parent 2f8b30c commit 21385c6

File tree

3 files changed

+32
-16
lines changed

3 files changed

+32
-16
lines changed

.changeset/gorgeous-pots-repeat.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@powersync/web': patch
3+
---
4+
5+
Improve OPFS multitab connection recovery when tabs are closed.

demos/react-supabase-todolist/src/components/providers/SystemProvider.tsx

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@ import { AppSchema, ListRecord, LISTS_TABLE, TODOS_TABLE } from '@/library/power
33
import { SupabaseConnector } from '@/library/powersync/SupabaseConnector';
44
import { CircularProgress } from '@mui/material';
55
import { PowerSyncContext } from '@powersync/react';
6-
import { createBaseLogger, DifferentialWatchedQuery, LogLevel, PowerSyncDatabase } from '@powersync/web';
6+
import {
7+
createBaseLogger,
8+
DifferentialWatchedQuery,
9+
LogLevel,
10+
PowerSyncDatabase,
11+
WASQLiteOpenFactory,
12+
WASQLiteVFS
13+
} from '@powersync/web';
714
import React, { Suspense } from 'react';
815
import { NavigationPanelContextProvider } from '../navigation/NavigationPanelContext';
916

@@ -12,9 +19,10 @@ export const useSupabase = () => React.useContext(SupabaseContext);
1219

1320
export const db = new PowerSyncDatabase({
1421
schema: AppSchema,
15-
database: {
16-
dbFilename: 'example.db'
17-
}
22+
database: new WASQLiteOpenFactory({
23+
dbFilename: 'example.db',
24+
vfs: WASQLiteVFS.OPFSCoopSyncVFS
25+
})
1826
});
1927

2028
export type EnhancedListRecord = ListRecord & { total_tasks: number; completed_tasks: number };

packages/web/src/worker/sync/SharedSyncImplementation.ts

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
import {
2-
type ILogger,
3-
type ILogLevel,
4-
type PowerSyncConnectionOptions,
5-
type StreamingSyncImplementation,
6-
type StreamingSyncImplementationListener,
7-
type SyncStatusOptions,
82
AbortOperation,
93
BaseObserver,
104
ConnectionManager,
@@ -13,7 +7,13 @@ import {
137
PowerSyncBackendConnector,
148
SqliteBucketStorage,
159
SubscribedStream,
16-
SyncStatus
10+
SyncStatus,
11+
type ILogger,
12+
type ILogLevel,
13+
type PowerSyncConnectionOptions,
14+
type StreamingSyncImplementation,
15+
type StreamingSyncImplementationListener,
16+
type SyncStatusOptions
1717
} from '@powersync/common';
1818
import { Mutex } from 'async-mutex';
1919
import * as Comlink from 'comlink';
@@ -75,7 +75,7 @@ export type WrappedSyncPort = {
7575
clientProvider: Comlink.Remote<AbstractSharedSyncClientProvider>;
7676
db?: DBAdapter;
7777
currentSubscriptions: SubscribedStream[];
78-
closeListeners: (() => void)[];
78+
closeListeners: (() => void | Promise<void>)[];
7979
};
8080

8181
/**
@@ -334,12 +334,15 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
334334
}
335335

336336
for (const closeListener of trackedPort.closeListeners) {
337-
closeListener();
337+
await closeListener();
338338
}
339339

340340
if (this.dbAdapter && this.dbAdapter == trackedPort.db) {
341341
// Unconditionally close the connection because the database it's writing to has just been closed.
342-
await this.connectionManager.disconnect();
342+
// The connection has been closed previously, this might throw. We should be able to ignore it.
343+
await this.connectionManager
344+
.disconnect()
345+
.catch((ex) => this.logger.warn('Error while disconnecting. Will attempt to reconnect.', ex));
343346

344347
// Clearing the adapter will result in a new one being opened in connect
345348
this.dbAdapter = null;
@@ -482,9 +485,9 @@ export class SharedSyncImplementation extends BaseObserver<SharedSyncImplementat
482485
// that and ensure pending requests are aborted when the tab is closed.
483486
remoteCanCloseUnexpectedly: true
484487
});
485-
lastClient.closeListeners.push(() => {
488+
lastClient.closeListeners.push(async () => {
486489
this.logger.info('Aborting open connection because associated tab closed.');
487-
wrapped.close();
490+
await wrapped.close().catch((ex) => this.logger.warn('error closing database connection', ex));
488491
wrapped.markRemoteClosed();
489492
});
490493

0 commit comments

Comments
 (0)