Skip to content

Commit 0676c24

Browse files
authored
Merge 2ff82d8 into a058309
2 parents a058309 + 2ff82d8 commit 0676c24

File tree

6 files changed

+90
-32
lines changed

6 files changed

+90
-32
lines changed

.changeset/long-pets-sell.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@firebase/firestore': patch
3+
---
4+
5+
Internal listener registration change for IndexedDB "versionchange" events.

packages/firestore/src/core/firestore_client.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -230,11 +230,23 @@ export async function setOfflineComponentProvider(
230230
}
231231
});
232232

233-
// When a user calls clearPersistence() in one client, all other clients
234-
// need to be terminated to allow the delete to succeed.
235-
offlineComponentProvider.persistence.setDatabaseDeletedListener(() =>
236-
client.terminate()
237-
);
233+
offlineComponentProvider.persistence.setDatabaseDeletedListener(() => {
234+
logWarn('Terminating Firestore due to IndexedDb database deletion');
235+
client
236+
.terminate()
237+
.then(() => {
238+
logDebug(
239+
'Terminating Firestore due to IndexedDb database deletion ' +
240+
'completed successfully'
241+
);
242+
})
243+
.catch(error => {
244+
logWarn(
245+
'Terminating Firestore due to IndexedDb database deletion failed',
246+
error
247+
);
248+
});
249+
});
238250

239251
client._offlineComponents = offlineComponentProvider;
240252
}

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 19 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,11 @@ import { IndexedDbTargetCache } from './indexeddb_target_cache';
5858
import { getStore, IndexedDbTransaction } from './indexeddb_transaction';
5959
import { LocalSerializer } from './local_serializer';
6060
import { LruParams } from './lru_garbage_collector';
61-
import { Persistence, PrimaryStateListener } from './persistence';
61+
import {
62+
DatabaseDeletedListener,
63+
Persistence,
64+
PrimaryStateListener
65+
} from './persistence';
6266
import { PersistencePromise } from './persistence_promise';
6367
import {
6468
PersistenceTransaction,
@@ -324,20 +328,25 @@ export class IndexedDbPersistence implements Persistence {
324328
}
325329

326330
/**
327-
* Registers a listener that gets called when the database receives a
328-
* version change event indicating that it has deleted.
331+
* Registers a listener that gets called when the underlying database receives
332+
* an event indicating that it either has been deleted or is pending deletion
333+
* and must be closed.
334+
*
335+
* For example, this callback will be called in the case that multi-tab
336+
* IndexedDB persistence is in use and another tab calls
337+
* clearIndexedDbPersistence(). In that case, this Firestore instance must
338+
* close its IndexedDB connection in order to allow the deletion initiated by
339+
* the other tab to proceed.
340+
*
341+
* This method may only be called once; subsequent invocations will result in
342+
* an exception, refusing to supersede the previously-registered listener.
329343
*
330344
* PORTING NOTE: This is only used for Web multi-tab.
331345
*/
332346
setDatabaseDeletedListener(
333-
databaseDeletedListener: () => Promise<void>
347+
databaseDeletedListener: DatabaseDeletedListener
334348
): void {
335-
this.simpleDb.setVersionChangeListener(async event => {
336-
// Check if an attempt is made to delete IndexedDB.
337-
if (event.newVersion === null) {
338-
await databaseDeletedListener();
339-
}
340-
});
349+
this.simpleDb.setDatabaseDeletedListener(databaseDeletedListener);
341350
}
342351

343352
/**

packages/firestore/src/local/persistence.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ export interface ReferenceDelegate {
9898
): PersistencePromise<void>;
9999
}
100100

101+
export type DatabaseDeletedListener = () => void;
102+
101103
/**
102104
* Persistence is the lowest-level shared interface to persistent storage in
103105
* Firestore.
@@ -151,13 +153,23 @@ export interface Persistence {
151153
shutdown(): Promise<void>;
152154

153155
/**
154-
* Registers a listener that gets called when the database receives a
155-
* version change event indicating that it has deleted.
156+
* Registers a listener that gets called when the underlying database receives
157+
* an event indicating that it either has been deleted or is pending deletion
158+
* and must be closed.
159+
*
160+
* For example, this callback will be called in the case that multi-tab
161+
* IndexedDB persistence is in use and another tab calls
162+
* clearIndexedDbPersistence(). In that case, this Firestore instance must
163+
* close its IndexedDB connection in order to allow the deletion initiated by
164+
* the other tab to proceed.
165+
*
166+
* This method may only be called once; subsequent invocations will result in
167+
* an exception, refusing to supersede the previously-registered listener.
156168
*
157169
* PORTING NOTE: This is only used for Web multi-tab.
158170
*/
159171
setDatabaseDeletedListener(
160-
databaseDeletedListener: () => Promise<void>
172+
databaseDeletedListener: DatabaseDeletedListener
161173
): void;
162174

163175
/**

packages/firestore/src/local/simple_db.ts

Lines changed: 26 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ import { getGlobal, getUA, isIndexedDBAvailable } from '@firebase/util';
1919

2020
import { debugAssert } from '../util/assert';
2121
import { Code, FirestoreError } from '../util/error';
22-
import { logDebug, logError } from '../util/log';
22+
import { logDebug, logError, logWarn } from '../util/log';
2323
import { Deferred } from '../util/promise';
2424

25+
import { DatabaseDeletedListener } from './persistence';
2526
import { PersistencePromise } from './persistence_promise';
2627

2728
// References to `indexedDB` are guarded by SimpleDb.isAvailable() and getGlobal()
@@ -158,8 +159,8 @@ export class SimpleDbTransaction {
158159
*/
159160
export class SimpleDb {
160161
private db?: IDBDatabase;
162+
private databaseDeletedListener?: DatabaseDeletedListener;
161163
private lastClosedDbVersion: number | null = null;
162-
private versionchangelistener?: (event: IDBVersionChangeEvent) => void;
163164

164165
/** Deletes the specified database. */
165166
static delete(name: string): Promise<void> {
@@ -392,22 +393,35 @@ export class SimpleDb {
392393
);
393394
}
394395

395-
if (this.versionchangelistener) {
396-
this.db.onversionchange = event => this.versionchangelistener!(event);
397-
}
396+
this.db.addEventListener(
397+
'versionchange',
398+
event => {
399+
// Notify the listener if another tab attempted to delete the IndexedDb
400+
// database, such as by calling clearIndexedDbPersistence().
401+
if (event.newVersion === null) {
402+
logWarn(
403+
`Received "versionchange" event with newVersion===null; ` +
404+
'notifying the registered DatabaseDeletedListener, if any'
405+
);
406+
this.databaseDeletedListener?.();
407+
}
408+
},
409+
{ passive: true }
410+
);
398411

399412
return this.db;
400413
}
401414

402-
setVersionChangeListener(
403-
versionChangeListener: (event: IDBVersionChangeEvent) => void
415+
setDatabaseDeletedListener(
416+
databaseDeletedListener: DatabaseDeletedListener
404417
): void {
405-
this.versionchangelistener = versionChangeListener;
406-
if (this.db) {
407-
this.db.onversionchange = (event: IDBVersionChangeEvent) => {
408-
return versionChangeListener(event);
409-
};
418+
if (this.databaseDeletedListener) {
419+
throw new Error(
420+
'setDatabaseDeletedListener() may only be called once, ' +
421+
'and it has already been called'
422+
);
410423
}
424+
this.databaseDeletedListener = databaseDeletedListener;
411425
}
412426

413427
async runTransaction<T>(

packages/firestore/test/unit/specs/spec_test_runner.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,8 +365,14 @@ abstract class TestRunner {
365365
this.eventManager.onLastRemoteStoreUnlisten =
366366
triggerRemoteStoreUnlisten.bind(null, this.syncEngine);
367367

368-
await this.persistence.setDatabaseDeletedListener(async () => {
369-
await this.shutdown();
368+
this.persistence.setDatabaseDeletedListener(() => {
369+
this.shutdown().catch(error => {
370+
console.warn(
371+
'WARNING: this.shutdown() failed in callback ' +
372+
'specified to persistence.setDatabaseDeletedListener',
373+
error
374+
);
375+
});
370376
});
371377

372378
this.started = true;

0 commit comments

Comments
 (0)