Skip to content

Commit 6fc5e05

Browse files
Review feedback
1 parent ac101a7 commit 6fc5e05

18 files changed

+265
-179
lines changed

packages/firestore/src/local/indexeddb_persistence.ts

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -70,14 +70,12 @@ import { QueryData } from './query_data';
7070
import { ReferenceSet } from './reference_set';
7171
import { ClientId } from './shared_client_state';
7272
import { SimpleDb, SimpleDbStore, SimpleDbTransaction } from './simple_db';
73-
import { SnapshotVersion } from '../core/snapshot_version';
7473

7574
const LOG_TAG = 'IndexedDbPersistence';
7675

7776
/**
7877
* Oldest acceptable age in milliseconds for client metadata before the client
79-
* is considered inactive and its associated data (such as the remote document
80-
* cache changelog) is garbage collected.
78+
* is considered inactive and its associated data is garbage collected.
8179
*/
8280
const MAX_CLIENT_AGE_MS = 30 * 60 * 1000; // 30 minutes
8381

@@ -260,7 +258,7 @@ export class IndexedDbPersistence implements Persistence {
260258
/** The client metadata refresh task. */
261259
private clientMetadataRefresher: CancelablePromise<void> | null = null;
262260

263-
/** The last time we garbage collected the Remote Document Changelog. */
261+
/** The last time we garbage collected the client metadata object store. */
264262
private lastGarbageCollectionTime = Number.NEGATIVE_INFINITY;
265263

266264
/** Whether to allow shared multi-tab access to the persistence layer. */
@@ -1212,7 +1210,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
12121210
): PersistencePromise<number> {
12131211
const documentCache = this.db.getRemoteDocumentCache();
12141212
const changeBuffer = documentCache.newChangeBuffer({
1215-
createSentinelDocumentsToTrackDeletes: false
1213+
trackRemovals: false
12161214
});
12171215

12181216
const promises: Array<PersistencePromise<void>> = [];
@@ -1240,7 +1238,7 @@ export class IndexedDbLruDelegate implements ReferenceDelegate, LruDelegate {
12401238

12411239
return iteration
12421240
.next(() => PersistencePromise.waitFor(promises))
1243-
.next(() => changeBuffer.apply(txn, SnapshotVersion.forDeletedDoc()))
1241+
.next(() => changeBuffer.apply(txn))
12441242
.next(() => documentCount);
12451243
}
12461244

packages/firestore/src/local/indexeddb_remote_document_cache.ts

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,8 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
111111
}
112112

113113
/**
114-
* Updates the document change log and adds the given delta to the cached current size.
114+
* Updates the current size.
115+
*
115116
* Callers to `addEntry()` and `removeEntry()` *must* call this afterwards to update the
116117
* cache's metadata.
117118
*/
@@ -307,6 +308,8 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
307308
.iterate(
308309
{ index: DbRemoteDocument.readTimeIndex, range },
309310
(_, dbRemoteDoc) => {
311+
// Unlike `getEntry()` and others, `getNewDocumentChanges()` uses the
312+
// serializer directly since we want to keep sentinel deletes.
310313
const doc = this.serializer.fromDbRemoteDocument(dbRemoteDoc);
311314
changedDocs = changedDocs.insert(doc.key, doc);
312315
this.lastProcessedReadTime = this.serializer.fromDbTimestampKey(
@@ -335,21 +338,19 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
335338
{ index: DbRemoteDocument.readTimeIndex, reverse: true },
336339
(key, value, control) => {
337340
if (value.readTime) {
338-
this.lastProcessedReadTime = SnapshotVersion.fromTimestamp(
339-
new Timestamp(value.readTime![0], value.readTime![1])
340-
);
341+
this.lastProcessedReadTime = this.serializer.fromDbTimestampKey(value.readTime)
341342
}
342343
control.done();
343344
}
344345
);
345346
}
346347

347348
newChangeBuffer(options: {
348-
createSentinelDocumentsToTrackDeletes: boolean;
349+
trackRemovals: boolean;
349350
}): RemoteDocumentChangeBuffer {
350351
return new IndexedDbRemoteDocumentCache.RemoteDocumentChangeBuffer(
351352
this,
352-
options.createSentinelDocumentsToTrackDeletes
353+
options.trackRemovals
353354
);
354355
}
355356

@@ -388,8 +389,8 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
388389
doc instanceof NoDocument &&
389390
doc.version.isEqual(SnapshotVersion.forDeletedDoc())
390391
) {
391-
// The document is a sentinel delete and should only be used in the
392-
// change log.
392+
// The document is a sentinel removal and should only be used in the
393+
// `getNewDocumentChanges()`.
393394
return null;
394395
}
395396

@@ -413,19 +414,18 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
413414

414415
/**
415416
* @param documentCache The IndexedDbRemoteDocumentCache to apply the changes to.
416-
* @param trackDeletes Whether to create sentinel deletes that can be tracked by
417+
* @param trackRemovals Whether to create sentinel deletes that can be tracked by
417418
* `getNewDocumentChanges()`.
418419
*/
419420
constructor(
420421
private readonly documentCache: IndexedDbRemoteDocumentCache,
421-
private readonly trackDeletes: boolean
422+
private readonly trackRemovals: boolean
422423
) {
423424
super();
424425
}
425426

426427
protected applyChanges(
427-
transaction: PersistenceTransaction,
428-
readTime: SnapshotVersion
428+
transaction: PersistenceTransaction
429429
): PersistencePromise<void> {
430430
const promises: Array<PersistencePromise<void>> = [];
431431

@@ -440,18 +440,21 @@ export class IndexedDbRemoteDocumentCache implements RemoteDocumentCache {
440440
if (maybeDocument) {
441441
const doc = this.documentCache.serializer.toDbRemoteDocument(
442442
maybeDocument,
443-
readTime
443+
this.readTime!
444444
);
445445
const size = dbDocumentSize(doc);
446446
sizeDelta += size - previousSize!;
447447
promises.push(this.documentCache.addEntry(transaction, key, doc));
448448
} else {
449449
sizeDelta -= previousSize!;
450-
if (this.trackDeletes) {
451-
// A sentinel delete is symbolized by a NoDocument with a version of 0.
450+
if (this.trackRemovals) {
451+
// In order to track removals, we store "sentinel delete" entries in
452+
// the Remote Document Cache. These entries are represented by a
453+
// NoDocument with a version of 0 and are ignored by
454+
// `maybeDecodeDocument()` but preserved in `getNewDocumentChanges()`.
452455
const deletedDoc = this.documentCache.serializer.toDbRemoteDocument(
453456
new NoDocument(key, SnapshotVersion.forDeletedDoc()),
454-
readTime
457+
this.readTime!
455458
);
456459
promises.push(
457460
this.documentCache.addEntry(transaction, key, deletedDoc)

packages/firestore/src/local/indexeddb_schema.ts

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -311,7 +311,7 @@ export class DbTimestamp {
311311
}
312312

313313
/** A timestamp type that can be used in IndexedDb keys. */
314-
export type DbTimestampKey = number[];
314+
export type DbTimestampKey = [/* seconds */ number, /* nanos */ number];
315315

316316
// The key for the singleton object in the DbPrimaryClient is a single string.
317317
export type DbPrimaryClientKey = typeof DbPrimaryClient.key;
@@ -623,13 +623,19 @@ export class DbRemoteDocument {
623623
* An index that provides access to documents in a collection sorted by read
624624
* time.
625625
*
626-
* This index is used to allow Index-Free queries to fetch newly changed
626+
* This index is used to allow the RemoteDocumentCache to fetch newly changed
627627
* documents in a collection.
628628
*/
629629
static collectionReadTimeIndex = 'collectionReadTimeIndex';
630630

631631
static collectionReadTimeIndexPath = ['parentPath', 'readTime'];
632632

633+
// TODO: We are currently storing document keys three times (once as part
634+
// of the primary key, once as `parentPath` (mostly) and once inside the
635+
// encoded documents). During our next migration, we should rewrite the
636+
// primary key as parentPath + document ID which would allow us to drop
637+
// one value.
638+
633639
constructor(
634640
/**
635641
* Set to an instance of DbUnknownDocument if the data for a document is
@@ -655,11 +661,17 @@ export class DbRemoteDocument {
655661
*/
656662
public hasCommittedMutations: boolean | undefined,
657663

658-
/** When the document was read from the backend. */
664+
/**
665+
* When the document was read from the backend. Undefined for data written
666+
* prior to schema version 9.
667+
*/
659668
public readTime: DbTimestampKey | undefined,
660669

661-
/** The path of the collection this document is part of. */
662-
public parentPath: string | undefined
670+
/**
671+
* The path of the collection this document is part of. Undefined for data
672+
* written prior to schema version 9.
673+
*/
674+
public parentPath: string[] | undefined
663675
) {}
664676
}
665677

@@ -1005,9 +1017,6 @@ export class DbClientMetadata {
10051017
static keyPath = 'clientId';
10061018

10071019
constructor(
1008-
// Note: Previous schema versions included a field
1009-
// "lastProcessedDocumentChangeId". Don't use anymore.
1010-
10111020
/** The auto-generated client id assigned at client startup. */
10121021
public clientId: string,
10131022
/** The last time this state was updated. */
@@ -1016,6 +1025,9 @@ export class DbClientMetadata {
10161025
public networkEnabled: boolean,
10171026
/** Whether this client is running in a foreground tab. */
10181027
public inForeground: boolean
1028+
1029+
// Note: Previous schema versions included a field
1030+
// "lastProcessedDocumentChangeId". Don't use anymore.
10191031
) {}
10201032
}
10211033

@@ -1046,6 +1058,7 @@ export const V1_STORES = [
10461058
export const V3_STORES = V1_STORES;
10471059

10481060
// Visible for testing
1061+
// Note: DbRemoteDocumentChanges is no longer used and dropped with version 9
10491062
export const V4_STORES = [...V3_STORES, DbClientMetadata.store];
10501063

10511064
// V5 does not change the set of stores.

packages/firestore/src/local/local_serializer.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class LocalSerializer {
7676
readTime: SnapshotVersion
7777
): DbRemoteDocument {
7878
const dbReadTime = this.toDbTimestampKey(readTime);
79-
const parentPath = maybeDoc.key.path.popLast().canonicalString();
79+
const parentPath = maybeDoc.key.path.popLast().toArray();
8080
if (maybeDoc instanceof Document) {
8181
const doc = maybeDoc.proto
8282
? maybeDoc.proto
@@ -114,7 +114,7 @@ export class LocalSerializer {
114114
parentPath
115115
);
116116
} else {
117-
return fail('Unexpected MaybeDocumment');
117+
return fail('Unexpected MaybeDocument');
118118
}
119119
}
120120

packages/firestore/src/local/local_store.ts

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -345,15 +345,15 @@ export class LocalStore {
345345
'readwrite-primary',
346346
txn => {
347347
const affected = batchResult.batch.keys();
348-
const documentBuffer = this.remoteDocuments.newChangeBuffer({
349-
createSentinelDocumentsToTrackDeletes: true
350-
});
348+
const documentBuffer = this.remoteDocuments.newChangeBuffer(
349+
{trackRemovals: true} // Make sure document removals show up in `getNewDocumentChanges()`
350+
);
351351
return this.mutationQueue
352352
.acknowledgeBatch(txn, batchResult.batch, batchResult.streamToken)
353353
.next(() =>
354354
this.applyWriteToRemoteDocuments(txn, batchResult, documentBuffer)
355355
)
356-
.next(() => documentBuffer.apply(txn, batchResult.commitVersion))
356+
.next(() => documentBuffer.apply(txn))
357357
.next(() => this.mutationQueue.performConsistencyCheck(txn))
358358
.next(() => this.localDocuments.getDocuments(txn, affected));
359359
}
@@ -451,7 +451,7 @@ export class LocalStore {
451451
*/
452452
applyRemoteEvent(remoteEvent: RemoteEvent): Promise<MaybeDocumentMap> {
453453
const documentBuffer = this.remoteDocuments.newChangeBuffer({
454-
createSentinelDocumentsToTrackDeletes: true
454+
trackRemovals: true // Make sure document removals show up in `getNewDocumentChanges()`
455455
});
456456
const remoteVersion = remoteEvent.snapshotVersion;
457457
return this.persistence.runTransaction(
@@ -526,7 +526,7 @@ export class LocalStore {
526526
(doc.version.compareTo(existingDoc.version) === 0 &&
527527
existingDoc.hasPendingWrites)
528528
) {
529-
documentBuffer.addEntry(doc);
529+
documentBuffer.addEntry(doc, remoteVersion);
530530
changedDocs = changedDocs.insert(key, doc);
531531
} else if (
532532
doc instanceof NoDocument &&
@@ -586,7 +586,7 @@ export class LocalStore {
586586
}
587587

588588
return PersistencePromise.waitFor(promises)
589-
.next(() => documentBuffer.apply(txn, remoteVersion))
589+
.next(() => documentBuffer.apply(txn))
590590
.next(() => {
591591
return this.localDocuments.getLocalViewOfDocuments(
592592
txn,
@@ -867,7 +867,10 @@ export class LocalStore {
867867
' resulted in null'
868868
);
869869
} else {
870-
documentBuffer.addEntry(doc);
870+
// We use the commitVersion as the readTime rather than the
871+
// document's updateTime since the updateTime is not advanced
872+
// for updates that do not modify the underlying document.
873+
documentBuffer.addEntry(doc, batchResult.commitVersion);
871874
}
872875
}
873876
});

packages/firestore/src/local/memory_persistence.ts

Lines changed: 5 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,6 @@ import { PersistencePromise } from './persistence_promise';
4949
import { QueryData } from './query_data';
5050
import { ReferenceSet } from './reference_set';
5151
import { ClientId } from './shared_client_state';
52-
import { SnapshotVersion } from '../core/snapshot_version';
5352

5453
const LOG_TAG = 'MemoryPersistence';
5554

@@ -271,9 +270,7 @@ export class MemoryEagerDelegate implements ReferenceDelegate {
271270
): PersistencePromise<void> {
272271
// Remove newly orphaned documents.
273272
const cache = this.persistence.getRemoteDocumentCache();
274-
const changeBuffer = cache.newChangeBuffer({
275-
createSentinelDocumentsToTrackDeletes: false
276-
});
273+
const changeBuffer = cache.newChangeBuffer();
277274
return PersistencePromise.forEach(
278275
this.orphanedDocuments,
279276
(key: DocumentKey) => {
@@ -285,7 +282,7 @@ export class MemoryEagerDelegate implements ReferenceDelegate {
285282
}
286283
).next(() => {
287284
this._orphanedDocuments = null;
288-
return changeBuffer.apply(txn, SnapshotVersion.forDeletedDoc());
285+
return changeBuffer.apply(txn);
289286
});
290287
}
291288

@@ -414,9 +411,7 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
414411
): PersistencePromise<number> {
415412
let count = 0;
416413
const cache = this.persistence.getRemoteDocumentCache();
417-
const changeBuffer = cache.newChangeBuffer({
418-
createSentinelDocumentsToTrackDeletes: false
419-
});
414+
const changeBuffer = cache.newChangeBuffer();
420415
const p = cache.forEachDocumentKey(txn, key => {
421416
return this.isPinned(txn, key, upperBound).next(isPinned => {
422417
if (!isPinned) {
@@ -425,9 +420,7 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
425420
}
426421
});
427422
});
428-
return p
429-
.next(() => changeBuffer.apply(txn, SnapshotVersion.forDeletedDoc()))
430-
.next(() => count);
423+
return p.next(() => changeBuffer.apply(txn)).next(() => count);
431424
}
432425

433426
removeMutationReference(
@@ -475,7 +468,7 @@ export class MemoryLruDelegate implements ReferenceDelegate, LruDelegate {
475468
documentSize(maybeDoc: MaybeDocument): number {
476469
const remoteDocument = this.serializer.toDbRemoteDocument(
477470
maybeDoc,
478-
SnapshotVersion.forDeletedDoc()
471+
maybeDoc.version
479472
);
480473
let value: unknown;
481474
if (remoteDocument.document) {

packages/firestore/src/local/memory_remote_document_cache.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,11 @@ export class MemoryRemoteDocumentCache implements RemoteDocumentCache {
178178
return PersistencePromise.resolve(changedDocs);
179179
}
180180

181-
newChangeBuffer(options: {
182-
createSentinelDocumentsToTrackDeletes: boolean;
181+
newChangeBuffer(options?: {
182+
trackRemovals: boolean;
183183
}): RemoteDocumentChangeBuffer {
184+
// `trackRemovals` is ignores since the memory remote document cache keeps
185+
// a separate changelog and does not need special handling for removals.
184186
return new MemoryRemoteDocumentCache.RemoteDocumentChangeBuffer(this);
185187
}
186188

0 commit comments

Comments
 (0)