Skip to content

Commit 063f729

Browse files
author
Greg Soltis
authored
Add a type parameter to Persistence (#1047)
* Cherry pick sequence number starting point * Working on typed transactions * Start plumbing in sequence number * Back out sequence number changes * [AUTOMATED]: Prettier Code Styling * Fix tests * [AUTOMATED]: Prettier Code Styling * Fix lint * [AUTOMATED]: Prettier Code Styling * Uncomment line * MemoryPersistenceTransaction -> MemoryTransaction * [AUTOMATED]: Prettier Code Styling * Review updates * Style * Lint and style * Review feedback * [AUTOMATED]: Prettier Code Styling * Revert some unintentional import churn * Line 44 should definitely be empty * Checkpoint before adding helper function for stores * Use a helper for casting PersistenceTransaction to IndexedDbTransaction * [AUTOMATED]: Prettier Code Styling * Remove errant generic type * Lint * Fix typo
1 parent 1f25d0d commit 063f729

13 files changed

+167
-155
lines changed

packages/firestore/src/local/indexeddb_mutation_queue.ts

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,8 @@ import { LocalSerializer } from './local_serializer';
4040
import { MutationQueue } from './mutation_queue';
4141
import { PersistenceTransaction } from './persistence';
4242
import { PersistencePromise } from './persistence_promise';
43-
import { SimpleDb, SimpleDbStore } from './simple_db';
43+
import { SimpleDbStore } from './simple_db';
44+
import { IndexedDbPersistence } from './indexeddb_persistence';
4445

4546
/** A mutation queue for a specific user, backed by IndexedDB. */
4647
export class IndexedDbMutationQueue implements MutationQueue {
@@ -563,7 +564,7 @@ function convertStreamToken(token: ProtoByteString): string {
563564
function mutationsStore(
564565
txn: PersistenceTransaction
565566
): SimpleDbStore<DbMutationBatchKey, DbMutationBatch> {
566-
return SimpleDb.getStore<DbMutationBatchKey, DbMutationBatch>(
567+
return IndexedDbPersistence.getStore<DbMutationBatchKey, DbMutationBatch>(
567568
txn,
568569
DbMutationBatch.store
569570
);
@@ -575,10 +576,10 @@ function mutationsStore(
575576
function documentMutationsStore(
576577
txn: PersistenceTransaction
577578
): SimpleDbStore<DbDocumentMutationKey, DbDocumentMutation> {
578-
return SimpleDb.getStore<DbDocumentMutationKey, DbDocumentMutation>(
579-
txn,
580-
DbDocumentMutation.store
581-
);
579+
return IndexedDbPersistence.getStore<
580+
DbDocumentMutationKey,
581+
DbDocumentMutation
582+
>(txn, DbDocumentMutation.store);
582583
}
583584

584585
/**
@@ -587,7 +588,7 @@ function documentMutationsStore(
587588
function mutationQueuesStore(
588589
txn: PersistenceTransaction
589590
): SimpleDbStore<DbMutationQueueKey, DbMutationQueue> {
590-
return SimpleDb.getStore<DbMutationQueueKey, DbMutationQueue>(
591+
return IndexedDbPersistence.getStore<DbMutationQueueKey, DbMutationQueue>(
591592
txn,
592593
DbMutationQueue.store
593594
);

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
import { User } from '../auth/user';
1818
import { DatabaseInfo } from '../core/database_info';
1919
import { JsonProtoSerializer } from '../remote/serializer';
20-
import { assert } from '../util/assert';
20+
import { assert, fail } from '../util/assert';
2121
import { Code, FirestoreError } from '../util/error';
2222
import * as log from '../util/log';
2323
import { AutoId } from '../util/misc';
@@ -34,11 +34,11 @@ import {
3434
} from './indexeddb_schema';
3535
import { LocalSerializer } from './local_serializer';
3636
import { MutationQueue } from './mutation_queue';
37-
import { Persistence } from './persistence';
37+
import { Persistence, PersistenceTransaction } from './persistence';
3838
import { PersistencePromise } from './persistence_promise';
3939
import { QueryCache } from './query_cache';
4040
import { RemoteDocumentCache } from './remote_document_cache';
41-
import { SimpleDb, SimpleDbTransaction } from './simple_db';
41+
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
4242

4343
const LOG_TAG = 'IndexedDbPersistence';
4444

@@ -59,6 +59,12 @@ const UNSUPPORTED_PLATFORM_ERROR_MSG =
5959
' IndexedDB or is known to have an incomplete implementation. Offline' +
6060
' persistence has been disabled.';
6161

62+
export class IndexedDbTransaction extends PersistenceTransaction {
63+
constructor(readonly simpleDbTransaction: SimpleDbTransaction) {
64+
super();
65+
}
66+
}
67+
6268
/**
6369
* An IndexedDB-backed instance of Persistence. Data is stored persistently
6470
* across sessions.
@@ -89,6 +95,17 @@ const UNSUPPORTED_PLATFORM_ERROR_MSG =
8995
* owner lease immediately regardless of the current lease timestamp.
9096
*/
9197
export class IndexedDbPersistence implements Persistence {
98+
static getStore<Key extends IDBValidKey, Value>(
99+
txn: PersistenceTransaction,
100+
store: string
101+
): SimpleDbStore<Key, Value> {
102+
if (txn instanceof IndexedDbTransaction) {
103+
return SimpleDb.getStore<Key, Value>(txn.simpleDbTransaction, store);
104+
} else {
105+
fail('IndexedDbPersistence must use instances of IndexedDbTransaction');
106+
}
107+
}
108+
92109
/**
93110
* The name of the main (and currently only) IndexedDB database. this name is
94111
* appended to the prefix provided to the IndexedDbPersistence constructor.
@@ -176,7 +193,7 @@ export class IndexedDbPersistence implements Persistence {
176193

177194
runTransaction<T>(
178195
action: string,
179-
operation: (transaction: SimpleDbTransaction) => PersistencePromise<T>
196+
operation: (transaction: IndexedDbTransaction) => PersistencePromise<T>
180197
): Promise<T> {
181198
if (this.persistenceError) {
182199
return Promise.reject(this.persistenceError);
@@ -186,10 +203,16 @@ export class IndexedDbPersistence implements Persistence {
186203

187204
// Do all transactions as readwrite against all object stores, since we
188205
// are the only reader/writer.
189-
return this.simpleDb.runTransaction('readwrite', ALL_STORES, txn => {
190-
// Verify that we still have the owner lease as part of every transaction.
191-
return this.ensureOwnerLease(txn).next(() => operation(txn));
192-
});
206+
return this.simpleDb.runTransaction(
207+
'readwrite',
208+
ALL_STORES,
209+
simpleDbTxn => {
210+
// Verify that we still have the owner lease as part of every transaction.
211+
return this.ensureOwnerLease(simpleDbTxn).next(() =>
212+
operation(new IndexedDbTransaction(simpleDbTxn))
213+
);
214+
}
215+
);
193216
}
194217

195218
static isAvailable(): boolean {
@@ -326,12 +349,16 @@ export class IndexedDbPersistence implements Persistence {
326349
// would increase the chances of us not refreshing on time if the queue is
327350
// backed up for some reason.
328351
this.ownerLeaseRefreshHandle = setInterval(() => {
329-
const txResult = this.runTransaction('Refresh owner timestamp', txn => {
330-
// NOTE: We don't need to validate the current owner contents, since
331-
// runTransaction does that automatically.
332-
const store = txn.store<DbOwnerKey, DbOwner>(DbOwner.store);
333-
return store.put('owner', new DbOwner(this.ownerId, Date.now()));
334-
});
352+
const txResult = this.simpleDb.runTransaction(
353+
'readwrite',
354+
ALL_STORES,
355+
txn => {
356+
// NOTE: We don't need to validate the current owner contents, since
357+
// runTransaction does that automatically.
358+
const store = txn.store<DbOwnerKey, DbOwner>(DbOwner.store);
359+
return store.put('owner', new DbOwner(this.ownerId, Date.now()));
360+
}
361+
);
335362

336363
txResult.catch(reason => {
337364
// Probably means we lost the lease. Report the error and stop trying to

packages/firestore/src/local/indexeddb_query_cache.ts

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ import { PersistenceTransaction } from './persistence';
3838
import { PersistencePromise } from './persistence_promise';
3939
import { QueryCache } from './query_cache';
4040
import { QueryData } from './query_data';
41-
import { SimpleDb, SimpleDbStore } from './simple_db';
41+
import { SimpleDbStore } from './simple_db';
42+
import { IndexedDbPersistence } from './indexeddb_persistence';
4243

4344
export class IndexedDbQueryCache implements QueryCache {
4445
constructor(private serializer: LocalSerializer) {}
@@ -52,7 +53,7 @@ export class IndexedDbQueryCache implements QueryCache {
5253
/**
5354
* A cached copy of the metadata for the query cache.
5455
*/
55-
private metadata = null;
56+
private metadata: DbTargetGlobal = null;
5657

5758
/** The garbage collector to notify about potential garbage keys. */
5859
private garbageCollector: GarbageCollector | null = null;
@@ -297,6 +298,8 @@ export class IndexedDbQueryCache implements QueryCache {
297298
this.garbageCollector = gc;
298299
}
299300

301+
// TODO(gsoltis): we can let the compiler assert that txn !== null if we
302+
// drop null from the type bounds on txn.
300303
containsKey(
301304
txn: PersistenceTransaction | null,
302305
key: DocumentKey
@@ -335,7 +338,10 @@ export class IndexedDbQueryCache implements QueryCache {
335338
function targetsStore(
336339
txn: PersistenceTransaction
337340
): SimpleDbStore<DbTargetKey, DbTarget> {
338-
return SimpleDb.getStore<DbTargetKey, DbTarget>(txn, DbTarget.store);
341+
return IndexedDbPersistence.getStore<DbTargetKey, DbTarget>(
342+
txn,
343+
DbTarget.store
344+
);
339345
}
340346

341347
/**
@@ -344,7 +350,7 @@ function targetsStore(
344350
function globalTargetStore(
345351
txn: PersistenceTransaction
346352
): SimpleDbStore<DbTargetGlobalKey, DbTargetGlobal> {
347-
return SimpleDb.getStore<DbTargetGlobalKey, DbTargetGlobal>(
353+
return IndexedDbPersistence.getStore<DbTargetGlobalKey, DbTargetGlobal>(
348354
txn,
349355
DbTargetGlobal.store
350356
);
@@ -356,7 +362,7 @@ function globalTargetStore(
356362
function documentTargetStore(
357363
txn: PersistenceTransaction
358364
): SimpleDbStore<DbTargetDocumentKey, DbTargetDocument> {
359-
return SimpleDb.getStore<DbTargetDocumentKey, DbTargetDocument>(
365+
return IndexedDbPersistence.getStore<DbTargetDocumentKey, DbTargetDocument>(
360366
txn,
361367
DbTargetDocument.store
362368
);

packages/firestore/src/local/indexeddb_remote_document_cache.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,12 @@ import { Document, MaybeDocument } from '../model/document';
2020
import { DocumentKey } from '../model/document_key';
2121

2222
import { DbRemoteDocument, DbRemoteDocumentKey } from './indexeddb_schema';
23+
import { IndexedDbPersistence } from './indexeddb_persistence';
2324
import { LocalSerializer } from './local_serializer';
2425
import { PersistenceTransaction } from './persistence';
2526
import { PersistencePromise } from './persistence_promise';
2627
import { RemoteDocumentCache } from './remote_document_cache';
27-
import { SimpleDb, SimpleDbStore } from './simple_db';
28+
import { SimpleDbStore } from './simple_db';
2829

2930
export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
3031
constructor(private serializer: LocalSerializer) {}
@@ -88,7 +89,7 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
8889
function remoteDocumentsStore(
8990
txn: PersistenceTransaction
9091
): SimpleDbStore<DbRemoteDocumentKey, DbRemoteDocument> {
91-
return SimpleDb.getStore<DbRemoteDocumentKey, DbRemoteDocument>(
92+
return IndexedDbPersistence.getStore<DbRemoteDocumentKey, DbRemoteDocument>(
9293
txn,
9394
DbRemoteDocument.store
9495
);

packages/firestore/src/local/memory_persistence.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -85,9 +85,12 @@ export class MemoryPersistence implements Persistence {
8585
operation: (transaction: PersistenceTransaction) => PersistencePromise<T>
8686
): Promise<T> {
8787
debug(LOG_TAG, 'Starting transaction:', action);
88-
return operation(new MemoryPersistenceTransaction()).toPromise();
88+
return operation(new MemoryTransaction()).toPromise();
8989
}
9090
}
9191

92-
/** Dummy class since memory persistence doesn't actually use transactions. */
93-
class MemoryPersistenceTransaction implements PersistenceTransaction {}
92+
/**
93+
* Memory persistence is not actually transactional, but future implementations
94+
* may have transaction-scoped state.
95+
*/
96+
export class MemoryTransaction implements PersistenceTransaction {}

packages/firestore/src/local/persistence.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717
import { User } from '../auth/user';
1818

1919
import { MutationQueue } from './mutation_queue';
20-
import { PersistenceTransaction } from './persistence';
2120
import { PersistencePromise } from './persistence_promise';
2221
import { QueryCache } from './query_cache';
2322
import { RemoteDocumentCache } from './remote_document_cache';
@@ -29,7 +28,7 @@ import { RemoteDocumentCache } from './remote_document_cache';
2928
* pass it to your callback. You then pass it to any method that operates
3029
* on persistence.
3130
*/
32-
export interface PersistenceTransaction {}
31+
export abstract class PersistenceTransaction {}
3332

3433
/**
3534
* Persistence is the lowest-level shared interface to persistent storage in

packages/firestore/src/local/simple_db.ts

Lines changed: 7 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,12 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { assert, fail } from '../util/assert';
17+
import { assert } from '../util/assert';
1818
import { debug } from '../util/log';
1919
import { AnyDuringMigration } from '../util/misc';
20-
2120
import { PersistencePromise } from './persistence_promise';
2221
import { SCHEMA_VERSION } from './indexeddb_schema';
2322
import { Deferred } from '../util/promise';
24-
import { PersistenceTransaction } from './persistence';
2523
import { Code, FirestoreError } from '../util/error';
2624

2725
const LOG_TAG = 'SimpleDb';
@@ -150,14 +148,10 @@ export class SimpleDb {
150148

151149
/** Helper to get a typed SimpleDbStore from a transaction. */
152150
static getStore<KeyType extends IDBValidKey, ValueType>(
153-
txn: PersistenceTransaction,
151+
txn: SimpleDbTransaction,
154152
store: string
155153
): SimpleDbStore<KeyType, ValueType> {
156-
if (txn instanceof SimpleDbTransaction) {
157-
return txn.store<KeyType, ValueType>(store);
158-
} else {
159-
return fail('Invalid transaction object provided!');
160-
}
154+
return txn.store<KeyType, ValueType>(store);
161155
}
162156

163157
constructor(private db: IDBDatabase) {}
@@ -534,25 +528,19 @@ export class SimpleDbStore<KeyType extends IDBValidKey, ValueType> {
534528
}
535529

536530
private cursor(options: IterateOptions): IDBRequest {
537-
let direction = 'next';
531+
let direction: IDBCursorDirection = 'next';
538532
if (options.reverse) {
539533
direction = 'prev';
540534
}
541535
if (options.index) {
542536
const index = this.store.index(options.index);
543537
if (options.keysOnly) {
544-
return index.openKeyCursor(
545-
options.range,
546-
direction as AnyDuringMigration
547-
);
538+
return index.openKeyCursor(options.range, direction);
548539
} else {
549-
return index.openCursor(options.range, direction as AnyDuringMigration);
540+
return index.openCursor(options.range, direction);
550541
}
551542
} else {
552-
return this.store.openCursor(
553-
options.range,
554-
direction as AnyDuringMigration
555-
);
543+
return this.store.openCursor(options.range, direction);
556544
}
557545
}
558546
}

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('EagerGarbageCollector', () => {
6565
expect(referenceSet.isEmpty()).to.equal(false);
6666

6767
referenceSet.removeReferencesForId(2);
68-
return gc.collectGarbage(true).toPromise();
68+
return gc.collectGarbage(null).toPromise();
6969
})
7070
.then(garbage => {
7171
expectSetToEqual(garbage, [key3]);

0 commit comments

Comments
 (0)