Skip to content

Commit bdd3eae

Browse files
authored
Overlay migration (#6131)
* Overlay migration * Remove intermediate state. * Add test. * Address feedback.
1 parent 104964e commit bdd3eae

File tree

4 files changed

+314
-8
lines changed

4 files changed

+314
-8
lines changed

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ import { DbTimestampKey } from './indexeddb_sentinels';
3131
// TODO(indexing): Remove this constant
3232
const INDEXING_ENABLED = false;
3333

34-
export const INDEXING_SCHEMA_VERSION = 14;
34+
export const INDEXING_SCHEMA_VERSION = 15;
3535

3636
/**
3737
* Schema Version for the Web client:
@@ -57,7 +57,7 @@ export const INDEXING_SCHEMA_VERSION = 14;
5757
* 14. Add indexing support.
5858
*/
5959

60-
export const SCHEMA_VERSION = INDEXING_ENABLED ? INDEXING_SCHEMA_VERSION : 13;
60+
export const SCHEMA_VERSION = INDEXING_ENABLED ? INDEXING_SCHEMA_VERSION : 14;
6161

6262
/**
6363
* Wrapper class to store timestamps (seconds and nanos) in IndexedDb objects.

packages/firestore/src/local/indexeddb_schema_converter.ts

Lines changed: 105 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@
1515
* limitations under the License.
1616
*/
1717

18+
import { User } from '../auth/user';
19+
import { ListenSequence } from '../core/listen_sequence';
1820
import { SnapshotVersion } from '../core/snapshot_version';
21+
import { documentKeySet } from '../model/collections';
1922
import { DocumentKey } from '../model/document_key';
2023
import { ResourcePath } from '../model/path';
2124
import { debugAssert, fail, hardAssert } from '../util/assert';
@@ -25,10 +28,13 @@ import {
2528
decodeResourcePath,
2629
encodeResourcePath
2730
} from './encoded_resource_path';
31+
import { IndexedDbDocumentOverlayCache } from './indexeddb_document_overlay_cache';
2832
import {
2933
dbDocumentSize,
3034
removeMutationBatch
3135
} from './indexeddb_mutation_batch_impl';
36+
import { IndexedDbMutationQueue } from './indexeddb_mutation_queue';
37+
import { newIndexedDbRemoteDocumentCache } from './indexeddb_remote_document_cache';
3238
import {
3339
DbCollectionParent,
3440
DbDocumentMutation,
@@ -107,13 +113,16 @@ import {
107113
DbTargetQueryTargetsKeyPath,
108114
DbTargetStore
109115
} from './indexeddb_sentinels';
116+
import { IndexedDbTransaction } from './indexeddb_transaction';
117+
import { LocalDocumentsView } from './local_documents_view';
110118
import {
111119
fromDbMutationBatch,
112120
fromDbTarget,
113121
LocalSerializer,
114122
toDbTarget
115123
} from './local_serializer';
116124
import { MemoryCollectionParentIndex } from './memory_index_manager';
125+
import { MemoryEagerDelegate, MemoryPersistence } from './memory_persistence';
117126
import { PersistencePromise } from './persistence_promise';
118127
import { SimpleDbSchemaConverter, SimpleDbTransaction } from './simple_db';
119128

@@ -240,9 +249,11 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
240249
}
241250

242251
if (fromVersion < 14 && toVersion >= 14) {
243-
p = p.next(() => {
244-
createFieldIndex(db);
245-
});
252+
p = p.next(() => this.runOverlayMigration(db, simpleDbTransaction));
253+
}
254+
255+
if (fromVersion < 15 && toVersion >= 15) {
256+
p = p.next(() => createFieldIndex(db));
246257
}
247258

248259
return p;
@@ -455,6 +466,97 @@ export class SchemaConverter implements SimpleDbSchemaConverter {
455466
})
456467
.next(() => PersistencePromise.waitFor(writes));
457468
}
469+
470+
private runOverlayMigration(
471+
db: IDBDatabase,
472+
transaction: SimpleDbTransaction
473+
): PersistencePromise<void> {
474+
const queuesStore = transaction.store<DbMutationQueueKey, DbMutationQueue>(
475+
DbMutationQueueStore
476+
);
477+
const mutationsStore = transaction.store<
478+
DbMutationBatchKey,
479+
DbMutationBatch
480+
>(DbMutationBatchStore);
481+
482+
const remoteDocumentCache = newIndexedDbRemoteDocumentCache(
483+
this.serializer
484+
);
485+
486+
const promises: Array<PersistencePromise<void>> = [];
487+
let userIds = new Set<string>();
488+
489+
return queuesStore
490+
.loadAll()
491+
.next(queues => {
492+
for (const queue of queues) {
493+
const userId = queue.userId;
494+
if (userIds.has(userId)) {
495+
// We have already processed this user.
496+
continue;
497+
}
498+
userIds = userIds.add(userId);
499+
const user = new User(userId);
500+
const documentOverlayCache = IndexedDbDocumentOverlayCache.forUser(
501+
this.serializer,
502+
user
503+
);
504+
let allDocumentKeysForUser = documentKeySet();
505+
const range = IDBKeyRange.bound(
506+
[userId, BATCHID_UNKNOWN],
507+
[userId, Number.POSITIVE_INFINITY]
508+
);
509+
promises.push(
510+
mutationsStore
511+
.loadAll(DbMutationBatchUserMutationsIndex, range)
512+
.next(dbBatches => {
513+
dbBatches.forEach(dbBatch => {
514+
hardAssert(
515+
dbBatch.userId === userId,
516+
`Cannot process batch ${dbBatch.batchId} from unexpected user`
517+
);
518+
const batch = fromDbMutationBatch(this.serializer, dbBatch);
519+
batch
520+
.keys()
521+
.forEach(
522+
key =>
523+
(allDocumentKeysForUser =
524+
allDocumentKeysForUser.add(key))
525+
);
526+
});
527+
})
528+
.next(() => {
529+
// NOTE: The index manager and the reference delegate are
530+
// irrelevant for the purpose of recalculating and saving
531+
// overlays. We can therefore simply use the memory
532+
// implementation.
533+
const memoryPersistence = new MemoryPersistence(
534+
MemoryEagerDelegate.factory,
535+
this.serializer.remoteSerializer
536+
);
537+
const indexManager = memoryPersistence.getIndexManager(user);
538+
const mutationQueue = IndexedDbMutationQueue.forUser(
539+
user,
540+
this.serializer,
541+
indexManager,
542+
memoryPersistence.referenceDelegate
543+
);
544+
const localDocumentsView = new LocalDocumentsView(
545+
remoteDocumentCache,
546+
mutationQueue,
547+
documentOverlayCache,
548+
indexManager
549+
);
550+
return localDocumentsView.recalculateAndSaveOverlaysForDocumentKeys(
551+
new IndexedDbTransaction(transaction, ListenSequence.INVALID),
552+
allDocumentKeysForUser
553+
);
554+
})
555+
);
556+
}
557+
})
558+
.next(() => PersistencePromise.waitFor(promises));
559+
}
458560
}
459561

460562
function sentinelKey(path: ResourcePath): DbTargetDocumentKey {

packages/firestore/src/local/indexeddb_sentinels.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -407,8 +407,9 @@ export const V13_STORES = [
407407
DbNamedQueryStore,
408408
DbDocumentOverlayStore
409409
];
410-
export const V14_STORES = [
411-
...V13_STORES,
410+
export const V14_STORES = V13_STORES;
411+
export const V15_STORES = [
412+
...V14_STORES,
412413
DbIndexConfigurationStore,
413414
DbIndexStateStore,
414415
DbIndexEntryStore
@@ -423,7 +424,9 @@ export const ALL_STORES = V12_STORES;
423424

424425
/** Returns the object stores for the provided schema. */
425426
export function getObjectStores(schemaVersion: number): string[] {
426-
if (schemaVersion === 14) {
427+
if (schemaVersion === 15) {
428+
return V15_STORES;
429+
} else if (schemaVersion === 14) {
427430
return V14_STORES;
428431
} else if (schemaVersion === 13) {
429432
return V13_STORES;

0 commit comments

Comments
 (0)