Skip to content

Commit 5900aa8

Browse files
committed
Implement code review feedback from @wilhuff, round 1
1 parent d8d7293 commit 5900aa8

File tree

4 files changed

+193
-145
lines changed

4 files changed

+193
-145
lines changed

packages/firestore/src/core/sync_engine.ts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ import { AsyncQueue } from '../util/async_queue';
7373
import { TransactionRunner } from './transaction_runner';
7474

7575
const LOG_TAG = 'SyncEngine';
76-
const DEFAULT_MAX_CONCURRENT_LIMBO_RESOLUTIONS = 100;
7776

7877
/**
7978
* QueryView contains all of the data that SyncEngine needs to keep track of for
@@ -149,15 +148,16 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
149148
q.canonicalId()
150149
);
151150
private queriesByTarget: { [targetId: number]: Query[] } = {};
151+
/** The keys of documents that are in limbo for which we haven't yet started a limbo resolution query. */
152+
private limboListenQueue: DocumentKey[] = [];
153+
/** Keeps track of the target ID for each document that is in limbo with an active target. */
152154
private limboTargetsByKey = new SortedMap<DocumentKey, TargetId>(
153155
DocumentKey.comparator
154156
);
157+
/** Keeps track of the information about an active limbo resolution for each active target ID that was started for the purpose of limbo resolution. */
155158
private limboResolutionsByTarget: {
156159
[targetId: number]: LimboResolution;
157160
} = {};
158-
private readonly maxConcurrentLimboResolutions: number = DEFAULT_MAX_CONCURRENT_LIMBO_RESOLUTIONS;
159-
/** The keys of documents whose limbo resolutions are enqueued. */
160-
private limboListenQueue: DocumentKey[] = [];
161161
private limboDocumentRefs = new ReferenceSet();
162162
/** Stores user completion handlers, indexed by User and BatchId. */
163163
private mutationUserCallbacks = {} as {
@@ -179,12 +179,8 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
179179
// PORTING NOTE: Manages state synchronization in multi-tab environments.
180180
private sharedClientState: SharedClientState,
181181
private currentUser: User,
182-
maxConcurrentLimboResolutions?: number
183-
) {
184-
if (maxConcurrentLimboResolutions) {
185-
this.maxConcurrentLimboResolutions = maxConcurrentLimboResolutions;
186-
}
187-
}
182+
private maxConcurrentLimboResolutions: number = 100
183+
) {}
188184

189185
// Only used for testing.
190186
get isPrimaryClient(): boolean {
@@ -495,7 +491,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
495491
// So go ahead and remove it from bookkeeping.
496492
this.limboTargetsByKey = this.limboTargetsByKey.remove(limboKey);
497493
delete this.limboResolutionsByTarget[targetId];
498-
this.startEnqueuedLimboResolutions();
494+
this.pumpLimboResolutionListenQueue();
499495

500496
// TODO(klimt): We really only should do the following on permission
501497
// denied errors, but we don't have the cause code here.
@@ -750,7 +746,7 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
750746
this.remoteStore.unlisten(limboTargetId);
751747
this.limboTargetsByKey = this.limboTargetsByKey.remove(key);
752748
delete this.limboResolutionsByTarget[limboTargetId];
753-
this.startEnqueuedLimboResolutions();
749+
this.pumpLimboResolutionListenQueue();
754750
}
755751

756752
private updateTrackedLimbos(
@@ -782,11 +778,30 @@ export class SyncEngine implements RemoteSyncer, SharedClientStateSyncer {
782778
if (!this.limboTargetsByKey.get(key)) {
783779
log.debug(LOG_TAG, 'New document in limbo: ' + key);
784780
this.limboListenQueue.push(key);
785-
this.startEnqueuedLimboResolutions();
781+
this.pumpLimboResolutionListenQueue();
786782
}
787783
}
788784

789-
private startEnqueuedLimboResolutions(): void {
785+
/**
786+
* Starts listens for documents in limbo that are enqueued for resolution.
787+
*
788+
* When a document goes into limbo it is enqueued for resolution. This method
789+
* repeatedly removes entries from the limbo resolution queue and starts a
790+
* listen for them until either (1) the queue is empty, meaning that all
791+
* documents that were in limbo either have active listens or have been
792+
* resolved, or (2) the maximum number of concurrent limbo resolution listens
793+
* has been reached.
794+
*
795+
* This method is invoked every time an entry is added to the limbo
796+
* resolution queue and every time that a limbo resolution listen completes
797+
* (either successfully or unsuccessfully). This ensures that all documents in
798+
* limbo are eventually resolved.
799+
*
800+
* A maximum number of concurrent limbo resolution listens was implemented to
801+
* prevent an unbounded number of active limbo resolution listens that can
802+
* exhaust server resources and result in "resource exhausted" errors.
803+
*/
804+
private pumpLimboResolutionListenQueue(): void {
790805
while (
791806
this.limboListenQueue.length > 0 &&
792807
this.limboTargetsByKey.size < this.maxConcurrentLimboResolutions

0 commit comments

Comments
 (0)