Skip to content

Commit 2cd9953

Browse files
committed
Avoid persisting the resume token unless required
1 parent b4397fc commit 2cd9953

File tree

1 file changed

+53
-1
lines changed

1 file changed

+53
-1
lines changed

packages/firestore/src/local/local_store.ts

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,12 @@ export interface LocalWriteResult {
111111
* unrecoverable error (should be caught / reported by the async_queue).
112112
*/
113113
export class LocalStore {
114+
/**
115+
* The maximum time to leave a resume token buffered without writing it
116+
* out.
117+
*/
118+
private static readonly MAX_RESUME_TOKEN_BUFFERING_MICROS = 5 * 60 * 1e6;
119+
114120
/**
115121
* The set of all mutations that have been sent but not yet been applied to
116122
* the backend.
@@ -469,12 +475,22 @@ export class LocalStore {
469475
// any preexisting value.
470476
const resumeToken = change.resumeToken;
471477
if (resumeToken.length > 0) {
478+
const oldQueryData = queryData;
472479
queryData = queryData.copy({
473480
resumeToken,
474481
snapshotVersion: remoteEvent.snapshotVersion
475482
});
476483
this.targetIds[targetId] = queryData;
477-
promises.push(this.queryCache.updateQueryData(txn, queryData));
484+
485+
if (
486+
LocalStore.shouldPersistResumeToken(
487+
oldQueryData,
488+
queryData,
489+
change
490+
)
491+
) {
492+
promises.push(this.queryCache.updateQueryData(txn, queryData));
493+
}
478494
}
479495
}
480496
);
@@ -550,6 +566,42 @@ export class LocalStore {
550566
});
551567
}
552568

569+
/**
570+
* Returns true if the the resume token in newQueryData should be persisted.
571+
*/
572+
private static shouldPersistResumeToken(
573+
oldQueryData: QueryData,
574+
newQueryData: QueryData,
575+
change: TargetChange
576+
): boolean {
577+
// Avoid clearing any existing value
578+
if (newQueryData.resumeToken.length === 0) return false;
579+
580+
// Any resume token is interesting if there isn't one already.
581+
if (oldQueryData.resumeToken.length === 0) return true;
582+
583+
// Don't allow resume token changes to be buffered indefinitely. This
584+
// allows us to be reasonably up-to-date after a crash and avoids needing
585+
// to loop over all active queries on shutdown. Especially in the browser
586+
// we may not get time to do anything interesting while the current tab is
587+
// closing.
588+
const timeDelta =
589+
newQueryData.snapshotVersion.toMicroseconds() -
590+
oldQueryData.snapshotVersion.toMicroseconds();
591+
if (timeDelta >= this.MAX_RESUME_TOKEN_BUFFERING_MICROS) return true;
592+
593+
// Otherwise if the only thing that has changed about a target is its resume
594+
// token it's not worth persisting. Note that the RemoteStore keeps an
595+
// in-memory view of the currently active targets which includes the current
596+
// resume token, so stream failure or user changes will still use an
597+
// up-to-date resume token regardless of what we do here.
598+
const changes =
599+
change.addedDocuments.size +
600+
change.modifiedDocuments.size +
601+
change.removedDocuments.size;
602+
return changes > 0;
603+
}
604+
553605
/**
554606
* Notify local store of the changed views to locally pin documents.
555607
*/

0 commit comments

Comments
 (0)