Skip to content

Commit ff93d0d

Browse files
Add BroadcastChannel for sharing table change notifications
1 parent a1d6d64 commit ff93d0d

File tree

2 files changed

+350
-414
lines changed

2 files changed

+350
-414
lines changed

packages/web/src/db/adapters/wa-sqlite/WASQLiteConnection.ts

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,11 @@ export enum WASQLiteVFS {
1313
AccessHandlePoolVFS = 'AccessHandlePoolVFS'
1414
}
1515

16+
export type WASQLiteBroadCastTableUpdateEvent = {
17+
changedTables: Set<string>;
18+
connectionId: number;
19+
};
20+
1621
/**
1722
* @internal
1823
*/
@@ -106,11 +111,19 @@ export class WASqliteConnection extends BaseObserver<WASQLiteConnectionListener>
106111
protected updatedTables: Set<string>;
107112
protected updateTimer: ReturnType<typeof setTimeout> | null;
108113
protected statementMutex: Mutex;
114+
protected broadcastChannel: BroadcastChannel | null;
115+
/**
116+
* Unique id for this specific connection. This is used to prevent broadcast table change
117+
* notification loops.
118+
*/
119+
protected connectionId: number;
109120

110121
constructor(protected options: WASQLiteOpenOptions) {
111122
super();
112123
this.updatedTables = new Set();
113124
this.updateTimer = null;
125+
this.broadcastChannel = null;
126+
this.connectionId = new Date().valueOf() + Math.random() * 1000;
114127
this.statementMutex = new Mutex();
115128
this._moduleFactory = DEFAULT_MODULE_FACTORIES[this.options.vfs ?? WASQLiteVFS.IDBBatchAtomicVFS];
116129
}
@@ -146,24 +159,47 @@ export class WASqliteConnection extends BaseObserver<WASQLiteConnectionListener>
146159
return sqlite3;
147160
}
148161

162+
protected registerBroadcastListeners() {
163+
this.broadcastChannel = new BroadcastChannel(`${this.options.dbFilename}-table-updates`);
164+
this.broadcastChannel.addEventListener('message', (event) => {
165+
const data: WASQLiteBroadCastTableUpdateEvent = event.data;
166+
if (this.connectionId == data.connectionId) {
167+
// Ignore messages from the same connection
168+
return;
169+
}
170+
this.queueTableUpdate(data.changedTables);
171+
});
172+
}
173+
174+
protected queueTableUpdate(tableNames: Set<string>) {
175+
tableNames.forEach((tableName) => this.updatedTables.add(tableName));
176+
if (this.updateTimer == null) {
177+
this.updateTimer = setTimeout(() => this.fireUpdates(), 0);
178+
}
179+
}
180+
149181
async init() {
150182
this._sqliteAPI = await this.openSQLiteAPI();
151183
await this.openDB();
184+
this.registerBroadcastListeners();
152185

153186
this.sqliteAPI.update_hook(this.dbP, (updateType: number, dbName: string | null, tableName: string | null) => {
154187
if (!tableName) {
155188
return;
156189
}
157-
this.updatedTables.add(tableName);
158-
if (this.updateTimer == null) {
159-
this.updateTimer = setTimeout(() => this.fireUpdates(), 0);
160-
}
190+
const changedTables = new Set([tableName]);
191+
this.queueTableUpdate(changedTables);
161192
});
162193
}
163194

164195
fireUpdates() {
165196
this.updateTimer = null;
166197
const event: BatchedUpdateNotification = { tables: [...this.updatedTables], groupedUpdates: {}, rawUpdates: [] };
198+
// Share to other connections
199+
this.broadcastChannel!.postMessage({
200+
changedTables: this.updatedTables,
201+
connectionId: this.connectionId
202+
} satisfies WASQLiteBroadCastTableUpdateEvent);
167203
this.updatedTables.clear();
168204
this.iterateListeners((cb) => cb.tablesUpdated?.(event));
169205
}
@@ -238,6 +274,7 @@ export class WASqliteConnection extends BaseObserver<WASQLiteConnectionListener>
238274
}
239275

240276
async close() {
277+
this.broadcastChannel?.close();
241278
await this.sqliteAPI.close(this.dbP);
242279
}
243280

0 commit comments

Comments
 (0)