Skip to content

Commit babdcfd

Browse files
Add test for getHighestListenSequenceNumber() recovery (#3083)
1 parent 9d87d70 commit babdcfd

File tree

6 files changed

+44
-41
lines changed

6 files changed

+44
-41
lines changed

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,13 +45,11 @@ import {
4545
DbPrimaryClient,
4646
DbPrimaryClientKey,
4747
DbTargetDocument,
48-
DbTargetGlobal,
4948
SCHEMA_VERSION,
5049
SchemaConverter
5150
} from './indexeddb_schema';
5251
import {
5352
documentTargetStore,
54-
getHighestListenSequenceNumber,
5553
IndexedDbTargetCache
5654
} from './indexeddb_target_cache';
5755
import { LocalSerializer } from './local_serializer';
@@ -306,10 +304,10 @@ export class IndexedDbPersistence implements Persistence {
306304

307305
this.scheduleClientMetadataAndPrimaryLeaseRefreshes();
308306

309-
return this.simpleDb.runTransaction(
307+
return this.runTransaction(
308+
'getHighestListenSequenceNumber',
310309
'readonly',
311-
[DbTargetGlobal.store],
312-
txn => getHighestListenSequenceNumber(txn)
310+
txn => this.targetCache.getHighestSequenceNumber(txn)
313311
);
314312
})
315313
.then(highestListenSequenceNumber => {

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@ import {
2828
EncodedResourcePath
2929
} from './encoded_resource_path';
3030
import { removeMutationBatch } from './indexeddb_mutation_queue';
31-
import { getHighestListenSequenceNumber } from './indexeddb_target_cache';
3231
import { dbDocumentSize } from './indexeddb_remote_document_cache';
3332
import { LocalSerializer } from './local_serializer';
3433
import { MemoryCollectionParentIndex } from './memory_index_manager';
@@ -231,16 +230,23 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
231230
const documentsStore = txn.store<DbRemoteDocumentKey, DbRemoteDocument>(
232231
DbRemoteDocument.store
233232
);
233+
const globalTargetStore = txn.store<DbTargetGlobalKey, DbTargetGlobal>(
234+
DbTargetGlobal.store
235+
);
234236

235-
return getHighestListenSequenceNumber(txn).next(currentSequenceNumber => {
237+
return globalTargetStore.get(DbTargetGlobal.key).next(metadata => {
238+
debugAssert(
239+
!!metadata,
240+
'Metadata should have been written during the version 3 migration'
241+
);
236242
const writeSentinelKey = (
237243
path: ResourcePath
238244
): PersistencePromise<void> => {
239245
return documentTargetStore.put(
240246
new DbTargetDocument(
241247
0,
242248
encodeResourcePath(path),
243-
currentSequenceNumber
249+
metadata!.highestListenSequenceNumber!
244250
)
245251
);
246252
};

packages/firestore/src/local/indexeddb_target_cache.ts

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,7 @@ import {
2929
} from './encoded_resource_path';
3030
import {
3131
IndexedDbLruDelegate,
32-
IndexedDbPersistence,
33-
IndexedDbTransaction
32+
IndexedDbPersistence
3433
} from './indexeddb_persistence';
3534
import {
3635
DbTarget,
@@ -46,7 +45,7 @@ import { PersistenceTransaction } from './persistence';
4645
import { PersistencePromise } from './persistence_promise';
4746
import { TargetCache } from './target_cache';
4847
import { TargetData } from './target_data';
49-
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
48+
import { SimpleDbStore } from './simple_db';
5049
import { Target } from '../core/target';
5150

5251
export class IndexedDbTargetCache implements TargetCache {
@@ -90,8 +89,8 @@ export class IndexedDbTargetCache implements TargetCache {
9089
getHighestSequenceNumber(
9190
transaction: PersistenceTransaction
9291
): PersistencePromise<ListenSequenceNumber> {
93-
return getHighestListenSequenceNumber(
94-
(transaction as IndexedDbTransaction).simpleDbTransaction
92+
return this.retrieveMetadata(transaction).next(
93+
targetGlobal => targetGlobal.highestListenSequenceNumber
9594
);
9695
}
9796

@@ -192,9 +191,12 @@ export class IndexedDbTargetCache implements TargetCache {
192191
private retrieveMetadata(
193192
transaction: PersistenceTransaction
194193
): PersistencePromise<DbTargetGlobal> {
195-
return retrieveMetadata(
196-
(transaction as IndexedDbTransaction).simpleDbTransaction
197-
);
194+
return globalTargetStore(transaction)
195+
.get(DbTargetGlobal.key)
196+
.next(metadata => {
197+
hardAssert(metadata !== null, 'Missing metadata row.');
198+
return metadata;
199+
});
198200
}
199201

200202
private saveMetadata(
@@ -422,27 +424,6 @@ function globalTargetStore(
422424
);
423425
}
424426

425-
function retrieveMetadata(
426-
txn: SimpleDbTransaction
427-
): PersistencePromise<DbTargetGlobal> {
428-
const globalStore = SimpleDb.getStore<DbTargetGlobalKey, DbTargetGlobal>(
429-
txn,
430-
DbTargetGlobal.store
431-
);
432-
return globalStore.get(DbTargetGlobal.key).next(metadata => {
433-
hardAssert(metadata !== null, 'Missing metadata row.');
434-
return metadata;
435-
});
436-
}
437-
438-
export function getHighestListenSequenceNumber(
439-
txn: SimpleDbTransaction
440-
): PersistencePromise<ListenSequenceNumber> {
441-
return retrieveMetadata(txn).next(
442-
targetGlobal => targetGlobal.highestListenSequenceNumber
443-
);
444-
}
445-
446427
/**
447428
* Helper to get a typed SimpleDbStore for the document target object store.
448429
*/

packages/firestore/test/unit/local/indexeddb_persistence.test.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,9 @@
1515
* limitations under the License.
1616
*/
1717

18-
import { expect } from 'chai';
18+
import * as chaiAsPromised from 'chai-as-promised';
19+
20+
import { expect, use } from 'chai';
1921
import { Query } from '../../../src/core/query';
2022
import { SnapshotVersion } from '../../../src/core/snapshot_version';
2123
import {
@@ -74,6 +76,8 @@ import {
7476
TEST_SERIALIZER
7577
} from './persistence_test_helpers';
7678

79+
use(chaiAsPromised);
80+
7781
/* eslint-disable no-restricted-globals */
7882

7983
function withDb(
@@ -1165,6 +1169,19 @@ describe('IndexedDb: allowTabSynchronization', () => {
11651169
);
11661170
});
11671171

1172+
it('blocks start() when getHighestListenSequenceNumber() fails', async () => {
1173+
await withUnstartedCustomPersistence(
1174+
'clientA',
1175+
/* multiClient= */ false,
1176+
async db1 => {
1177+
db1.injectFailures = ['getHighestListenSequenceNumber'];
1178+
await expect(db1.start()).to.eventually.be.rejectedWith(
1179+
'IndexedDB transaction failed'
1180+
);
1181+
}
1182+
);
1183+
});
1184+
11681185
it('ignores intermittent IndexedDbTransactionError during lease refresh', async () => {
11691186
await withPersistence('clientA', async (db, _, queue) => {
11701187
db.injectFailures = ['updateClientMetadataAndTryBecomePrimary'];

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -464,12 +464,12 @@ describeSpec('Persistence Recovery', ['no-ios', 'no-android'], () => {
464464
fromCache: true,
465465
hasPendingWrites: true
466466
})
467-
.failDatabaseTransactions({ 'Handle user change': true })
467+
.failDatabaseTransactions('Handle user change')
468468
.changeUser('user2')
469469
.recoverDatabase()
470470
.runTimer(TimerId.AsyncQueueRetry)
471471
.expectEvents(query, { removed: [doc1], fromCache: true })
472-
.failDatabaseTransactions({ 'Handle user change': true })
472+
.failDatabaseTransactions('Handle user change')
473473
.changeUser('user1')
474474
.recoverDatabase()
475475
.runTimer(TimerId.AsyncQueueRetry)

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1225,7 +1225,8 @@ export type PersistenceAction =
12251225
| 'Get target data'
12261226
| 'Get new document changes'
12271227
| 'Synchronize last document change read time'
1228-
| 'updateClientMetadataAndTryBecomePrimary';
1228+
| 'updateClientMetadataAndTryBecomePrimary'
1229+
| 'getHighestListenSequenceNumber';
12291230

12301231
/**
12311232
* Union type for each step. The step consists of exactly one `field`

0 commit comments

Comments
 (0)