-
Notifications
You must be signed in to change notification settings - Fork 927
Add a type parameter to Persistence #1047
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 18 commits
2c93300
96d732c
59121ba
4b75267
29505fc
d03f608
e7efc7b
fd1359a
9a76422
a7b46dd
3abdbbe
5b66917
a2848ff
70e8e51
ca4bc5d
7909aef
1bd431e
9fc2b89
d4f1dcc
a94c026
fa05379
068a4af
8e46ab9
091038c
f0ed2dd
ca53e31
4193323
1a39850
80a2459
7d14f14
9e570c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,9 +38,10 @@ import { | |
} from './indexeddb_schema'; | ||
import { LocalSerializer } from './local_serializer'; | ||
import { MutationQueue } from './mutation_queue'; | ||
import { PersistenceTransaction } from './persistence'; | ||
import { PersistencePromise } from './persistence_promise'; | ||
import { SimpleDb, SimpleDbStore } from './simple_db'; | ||
import { IndexedDbTransaction } from './indexeddb_persistence'; | ||
import { PersistenceTransaction } from './persistence'; | ||
|
||
/** A mutation queue for a specific user, backed by IndexedDB. */ | ||
export class IndexedDbMutationQueue implements MutationQueue { | ||
|
@@ -564,7 +565,7 @@ function mutationsStore( | |
txn: PersistenceTransaction | ||
): SimpleDbStore<DbMutationBatchKey, DbMutationBatch> { | ||
return SimpleDb.getStore<DbMutationBatchKey, DbMutationBatch>( | ||
txn, | ||
(txn as IndexedDbTransaction).simpleDbTransaction, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. How about an IndexedDbPersistence.getStore<>() helper to centralize this cast (and add a runtime assert please so if we do somehow mix/match things we get a helpful error immediately). (FYI- And then the IndexedDbTransaction import will probably no longe be needed). |
||
DbMutationBatch.store | ||
); | ||
} | ||
|
@@ -576,7 +577,7 @@ function documentMutationsStore( | |
txn: PersistenceTransaction | ||
): SimpleDbStore<DbDocumentMutationKey, DbDocumentMutation> { | ||
return SimpleDb.getStore<DbDocumentMutationKey, DbDocumentMutation>( | ||
txn, | ||
(txn as IndexedDbTransaction).simpleDbTransaction, | ||
DbDocumentMutation.store | ||
); | ||
} | ||
|
@@ -588,7 +589,7 @@ function mutationQueuesStore( | |
txn: PersistenceTransaction | ||
): SimpleDbStore<DbMutationQueueKey, DbMutationQueue> { | ||
return SimpleDb.getStore<DbMutationQueueKey, DbMutationQueue>( | ||
txn, | ||
(txn as IndexedDbTransaction).simpleDbTransaction, | ||
DbMutationQueue.store | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,7 @@ import { | |
} from './indexeddb_schema'; | ||
import { LocalSerializer } from './local_serializer'; | ||
import { MutationQueue } from './mutation_queue'; | ||
import { Persistence } from './persistence'; | ||
import { Persistence, PersistenceTransaction } from './persistence'; | ||
import { PersistencePromise } from './persistence_promise'; | ||
import { QueryCache } from './query_cache'; | ||
import { RemoteDocumentCache } from './remote_document_cache'; | ||
|
@@ -59,6 +59,12 @@ const UNSUPPORTED_PLATFORM_ERROR_MSG = | |
' IndexedDB or is known to have an incomplete implementation. Offline' + | ||
' persistence has been disabled.'; | ||
|
||
export class IndexedDbTransaction extends PersistenceTransaction { | ||
constructor(readonly simpleDbTransaction: SimpleDbTransaction) { | ||
super(); | ||
} | ||
} | ||
|
||
/** | ||
* An IndexedDB-backed instance of Persistence. Data is stored persistently | ||
* across sessions. | ||
|
@@ -176,7 +182,7 @@ export class IndexedDbPersistence implements Persistence { | |
|
||
runTransaction<T>( | ||
action: string, | ||
operation: (transaction: SimpleDbTransaction) => PersistencePromise<T> | ||
operation: (transaction: IndexedDbTransaction) => PersistencePromise<T> | ||
): Promise<T> { | ||
if (this.persistenceError) { | ||
return Promise.reject(this.persistenceError); | ||
|
@@ -186,10 +192,17 @@ export class IndexedDbPersistence implements Persistence { | |
|
||
// Do all transactions as readwrite against all object stores, since we | ||
// are the only reader/writer. | ||
return this.simpleDb.runTransaction('readwrite', ALL_STORES, txn => { | ||
// Verify that we still have the owner lease as part of every transaction. | ||
return this.ensureOwnerLease(txn).next(() => operation(txn)); | ||
}); | ||
return this.simpleDb.runTransaction( | ||
'readwrite', | ||
ALL_STORES, | ||
simpleDbTxn => { | ||
// Verify that we still have the owner lease as part of every transaction. | ||
const txn = new IndexedDbTransaction(simpleDbTxn); | ||
return this.ensureOwnerLease(txn.simpleDbTransaction).next(() => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If it's all the same, I'd rather keep the longer name. I think the abbreviation is a little harder to read. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I was suggesting you use the existing variable that's in scope. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah. That makes more sense. Done. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a general rule if I ever ask somebody to abbreviate a name in code review feedback you can assume my account has been hacked. 😁 |
||
operation(txn) | ||
); | ||
} | ||
); | ||
} | ||
|
||
static isAvailable(): boolean { | ||
|
@@ -326,12 +339,16 @@ export class IndexedDbPersistence implements Persistence { | |
// would increase the chances of us not refreshing on time if the queue is | ||
// backed up for some reason. | ||
this.ownerLeaseRefreshHandle = setInterval(() => { | ||
const txResult = this.runTransaction('Refresh owner timestamp', txn => { | ||
// NOTE: We don't need to validate the current owner contents, since | ||
// runTransaction does that automatically. | ||
const store = txn.store<DbOwnerKey, DbOwner>(DbOwner.store); | ||
return store.put('owner', new DbOwner(this.ownerId, Date.now())); | ||
}); | ||
const txResult = this.simpleDb.runTransaction( | ||
'readwrite', | ||
ALL_STORES, | ||
txn => { | ||
// NOTE: We don't need to validate the current owner contents, since | ||
// runTransaction does that automatically. | ||
const store = txn.store<DbOwnerKey, DbOwner>(DbOwner.store); | ||
return store.put('owner', new DbOwner(this.ownerId, Date.now())); | ||
} | ||
); | ||
|
||
txResult.catch(reason => { | ||
// Probably means we lost the lease. Report the error and stop trying to | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,11 +34,12 @@ import { | |
DbTargetKey | ||
} from './indexeddb_schema'; | ||
import { LocalSerializer } from './local_serializer'; | ||
import { PersistenceTransaction } from './persistence'; | ||
import { PersistencePromise } from './persistence_promise'; | ||
import { QueryCache } from './query_cache'; | ||
import { QueryData } from './query_data'; | ||
import { SimpleDb, SimpleDbStore } from './simple_db'; | ||
import { IndexedDbTransaction } from './indexeddb_persistence'; | ||
import { PersistenceTransaction } from './persistence'; | ||
|
||
export class IndexedDbQueryCache implements QueryCache { | ||
constructor(private serializer: LocalSerializer) {} | ||
|
@@ -52,7 +53,7 @@ export class IndexedDbQueryCache implements QueryCache { | |
/** | ||
* A cached copy of the metadata for the query cache. | ||
*/ | ||
private metadata = null; | ||
private metadata: DbTargetGlobal = null; | ||
|
||
/** The garbage collector to notify about potential garbage keys. */ | ||
private garbageCollector: GarbageCollector | null = null; | ||
|
@@ -297,6 +298,8 @@ export class IndexedDbQueryCache implements QueryCache { | |
this.garbageCollector = gc; | ||
} | ||
|
||
// TODO(gsoltis): we can let the compiler assert that txn !== null if we | ||
// drop null from the type bounds on txn. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there something blocking us from doing this right now? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, kinda. It compiles, but only because we don't enforce strict function types. This is replicating the type signature from The solution will eventually arrive when we get rid of |
||
containsKey( | ||
txn: PersistenceTransaction | null, | ||
key: DocumentKey | ||
|
@@ -335,7 +338,10 @@ export class IndexedDbQueryCache implements QueryCache { | |
function targetsStore( | ||
txn: PersistenceTransaction | ||
): SimpleDbStore<DbTargetKey, DbTarget> { | ||
return SimpleDb.getStore<DbTargetKey, DbTarget>(txn, DbTarget.store); | ||
return SimpleDb.getStore<DbTargetKey, DbTarget>( | ||
(txn as IndexedDbTransaction).simpleDbTransaction, | ||
DbTarget.store | ||
); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same feedback here (and elsewhere) about an IndexedDbPersistence.getStore<>() helper. |
||
} | ||
|
||
/** | ||
|
@@ -345,7 +351,7 @@ function globalTargetStore( | |
txn: PersistenceTransaction | ||
): SimpleDbStore<DbTargetGlobalKey, DbTargetGlobal> { | ||
return SimpleDb.getStore<DbTargetGlobalKey, DbTargetGlobal>( | ||
txn, | ||
(txn as IndexedDbTransaction).simpleDbTransaction, | ||
DbTargetGlobal.store | ||
); | ||
} | ||
|
@@ -357,7 +363,7 @@ function documentTargetStore( | |
txn: PersistenceTransaction | ||
): SimpleDbStore<DbTargetDocumentKey, DbTargetDocument> { | ||
return SimpleDb.getStore<DbTargetDocumentKey, DbTargetDocument>( | ||
txn, | ||
(txn as IndexedDbTransaction).simpleDbTransaction, | ||
DbTargetDocument.store | ||
); | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -41,7 +41,9 @@ export class MemoryPersistence implements Persistence { | |
* will make the in-memory persistence layer behave as if it were actually | ||
* persisting values. | ||
*/ | ||
private mutationQueues: { [user: string]: MutationQueue } = {}; | ||
private mutationQueues: { | ||
[user: string]: MutationQueue; | ||
} = {}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any idea why there are spurious re-formattings in this PR? Maybe run |
||
private remoteDocumentCache = new MemoryRemoteDocumentCache(); | ||
private queryCache = new MemoryQueryCache(); | ||
|
||
|
@@ -85,9 +87,12 @@ export class MemoryPersistence implements Persistence { | |
operation: (transaction: PersistenceTransaction) => PersistencePromise<T> | ||
): Promise<T> { | ||
debug(LOG_TAG, 'Starting transaction:', action); | ||
return operation(new MemoryPersistenceTransaction()).toPromise(); | ||
return operation(new MemoryTransaction()).toPromise(); | ||
} | ||
} | ||
|
||
/** Dummy class since memory persistence doesn't actually use transactions. */ | ||
class MemoryPersistenceTransaction implements PersistenceTransaction {} | ||
/** | ||
* Memory persistence is not actually transactional, but future implementations | ||
* may have transaction-scoped state. | ||
*/ | ||
export class MemoryTransaction implements PersistenceTransaction {} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,10 +22,9 @@ import { | |
} from '../model/collections'; | ||
import { Document, MaybeDocument } from '../model/document'; | ||
import { DocumentKey } from '../model/document_key'; | ||
|
||
import { PersistenceTransaction } from './persistence'; | ||
import { PersistencePromise } from './persistence_promise'; | ||
import { RemoteDocumentCache } from './remote_document_cache'; | ||
import { PersistenceTransaction } from './persistence'; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hrm. Does prettier not order these? (I'm not sure why this moved here) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I asked vscode to organize imports. And I guess prettier didn't object either way? I'll put it back and see what happens. |
||
|
||
export class MemoryRemoteDocumentCache implements RemoteDocumentCache { | ||
private docs = maybeDocumentMap(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -24,7 +24,10 @@ import { IndexedDbPersistence } from '../../../src/local/indexeddb_persistence'; | |
import { LocalStore, LocalWriteResult } from '../../../src/local/local_store'; | ||
import { LocalViewChanges } from '../../../src/local/local_view_changes'; | ||
import { NoOpGarbageCollector } from '../../../src/local/no_op_garbage_collector'; | ||
import { Persistence } from '../../../src/local/persistence'; | ||
import { | ||
Persistence, | ||
PersistenceTransaction | ||
} from '../../../src/local/persistence'; | ||
import { | ||
documentKeySet, | ||
DocumentMap, | ||
|
@@ -270,7 +273,7 @@ describe('LocalStore w/ IndexedDB Persistence', () => { | |
genericLocalStoreTests(persistenceHelpers.testIndexedDbPersistence); | ||
}); | ||
|
||
function genericLocalStoreTests( | ||
function genericLocalStoreTests<TransactionType extends PersistenceTransaction>( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. obsolete? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
getPersistence: () => Promise<Persistence> | ||
): void { | ||
let persistence: Persistence; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
obsolete