Skip to content

Commit 3203431

Browse files
author
Brian Chen
committed
new attempt of asyncqueue
1 parent 0dc9426 commit 3203431

File tree

7 files changed

+43
-82
lines changed

7 files changed

+43
-82
lines changed

packages/firestore/src/api/database.ts

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -307,8 +307,9 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService {
307307
// are already set to synchronize on the async queue.
308308
private _firestoreClient: FirestoreClient | undefined;
309309

310+
// Public for use in tests.
310311
// TODO(mikelehen): Use modularized initialization instead.
311-
private _queue = new AsyncQueue();
312+
readonly _queue = new AsyncQueue();
312313

313314
_dataConverter: UserDataConverter;
314315

@@ -455,16 +456,6 @@ export class Firestore implements firestore.FirebaseFirestore, FirebaseService {
455456
return this._firestoreClient as FirestoreClient;
456457
}
457458

458-
// Public for use in tests.
459-
getQueue(): AsyncQueue {
460-
return this._queue;
461-
}
462-
463-
// Public for use in tests.
464-
setQueue(newQueue: AsyncQueue): void {
465-
this._queue = newQueue;
466-
}
467-
468459
private makeDatabaseInfo(): DatabaseInfo {
469460
return new DatabaseInfo(
470461
this._config.databaseId,

packages/firestore/src/core/firestore_client.ts

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -71,17 +71,6 @@ const DOM_EXCEPTION_INVALID_STATE = 11;
7171
const DOM_EXCEPTION_ABORTED = 20;
7272
const DOM_EXCEPTION_QUOTA_EXCEEDED = 22;
7373

74-
/**
75-
* Initial backoff time in milliseconds after an error.
76-
* Set to 1s according to https://cloud.google.com/apis/design/errors.
77-
*/
78-
const BACKOFF_INITIAL_DELAY_MS = 1000;
79-
80-
/** Maximum backoff time in milliseconds */
81-
const BACKOFF_MAX_DELAY_MS = 60 * 1000;
82-
83-
const BACKOFF_FACTOR = 1.5;
84-
8574
export class IndexedDbPersistenceSettings {
8675
constructor(
8776
readonly cacheSizeBytes: number,
@@ -628,10 +617,7 @@ export class FirestoreClient {
628617
.then(() => {
629618
const backoff = new ExponentialBackoff(
630619
this.asyncQueue,
631-
TimerId.RetryTransaction,
632-
BACKOFF_INITIAL_DELAY_MS,
633-
BACKOFF_FACTOR,
634-
BACKOFF_MAX_DELAY_MS
620+
TimerId.RetryTransaction
635621
);
636622
return this.syncEngine.runTransaction(updateFunction, backoff);
637623
});

packages/firestore/src/remote/backoff.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,17 @@ import * as log from '../util/log';
2020
import { CancelablePromise } from '../util/promise';
2121
const LOG_TAG = 'ExponentialBackoff';
2222

23+
/**
24+
* Initial backoff time in milliseconds after an error.
25+
* Set to 1s according to https://cloud.google.com/apis/design/errors.
26+
*/
27+
const DEFAULT_BACKOFF_INITIAL_DELAY_MS = 1000;
28+
29+
const DEFAULT_BACKOFF_FACTOR = 1.5;
30+
31+
/** Maximum backoff time in milliseconds */
32+
const DEFAULT_BACKOFF_MAX_DELAY_MS = 60 * 1000;
33+
2334
/**
2435
* A helper for running delayed tasks following an exponential backoff curve
2536
* between attempts.
@@ -49,18 +60,18 @@ export class ExponentialBackoff {
4960
* Note that jitter will still be applied, so the actual delay could be as
5061
* little as 0.5*initialDelayMs.
5162
*/
52-
private readonly initialDelayMs: number,
63+
private readonly initialDelayMs: number = DEFAULT_BACKOFF_INITIAL_DELAY_MS,
5364
/**
5465
* The multiplier to use to determine the extended base delay after each
5566
* attempt.
5667
*/
57-
private readonly backoffFactor: number,
68+
private readonly backoffFactor: number = DEFAULT_BACKOFF_FACTOR,
5869
/**
5970
* The maximum base delay after which no further backoff is performed.
6071
* Note that jitter will still be applied, so the actual delay could be as
6172
* much as 1.5*maxDelayMs.
6273
*/
63-
private readonly maxDelayMs: number
74+
private readonly maxDelayMs: number = DEFAULT_BACKOFF_MAX_DELAY_MS
6475
) {
6576
this.reset();
6677
}

packages/firestore/src/remote/persistent_stream.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -115,17 +115,6 @@ export interface PersistentStreamListener {
115115
onClose: (err?: FirestoreError) => Promise<void>;
116116
}
117117

118-
/**
119-
* Initial backoff time in milliseconds after an error.
120-
* Set to 1s according to https://cloud.google.com/apis/design/errors.
121-
*/
122-
const BACKOFF_INITIAL_DELAY_MS = 1000;
123-
124-
/** Maximum backoff time in milliseconds */
125-
const BACKOFF_MAX_DELAY_MS = 60 * 1000;
126-
127-
const BACKOFF_FACTOR = 1.5;
128-
129118
/** The time a stream stays open after it is marked idle. */
130119
const IDLE_TIMEOUT_MS = 60 * 1000;
131120

@@ -187,13 +176,7 @@ export abstract class PersistentStream<
187176
private credentialsProvider: CredentialsProvider,
188177
protected listener: ListenerType
189178
) {
190-
this.backoff = new ExponentialBackoff(
191-
queue,
192-
connectionTimerId,
193-
BACKOFF_INITIAL_DELAY_MS,
194-
BACKOFF_FACTOR,
195-
BACKOFF_MAX_DELAY_MS
196-
);
179+
this.backoff = new ExponentialBackoff(queue, connectionTimerId);
197180
}
198181

199182
/**

packages/firestore/src/util/async_queue.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,9 @@ export class AsyncQueue {
202202
// assertion sanity-checks.
203203
private operationInProgress = false;
204204

205+
// List of TimerIds to fast-forward delays for.
206+
private timerIdsToSkip: TimerId[] = [];
207+
205208
/**
206209
* Adds a new operation to the queue without waiting for it to complete (i.e.
207210
* we ignore the Promise result).
@@ -275,6 +278,15 @@ export class AsyncQueue {
275278
);
276279
this.delayedOperations.push(delayedOp);
277280

281+
// Fast-forward delays for timerIds that have been overriden.
282+
for (const timerIdToSkip of this.timerIdsToSkip) {
283+
if (this.containsDelayedOperation(timerIdToSkip)) {
284+
this.runDelayedOperationsEarly(timerIdToSkip)
285+
.then(() => {})
286+
.catch(() => 'obligatory catch');
287+
}
288+
}
289+
278290
return delayedOp;
279291
}
280292

@@ -352,6 +364,13 @@ export class AsyncQueue {
352364
});
353365
}
354366

367+
/**
368+
* For Tests: Skip all delays for a timer id.
369+
*/
370+
skipDelaysForTimerId(timerId: TimerId): void {
371+
this.timerIdsToSkip.push(timerId);
372+
}
373+
355374
/** Called once a DelayedOperation is run or canceled. */
356375
private removeDelayedOperation(op: DelayedOperation<unknown>): void {
357376
// NOTE: indexOf / slice are O(n), but delayedOperations is expected to be small.

packages/firestore/test/integration/api/transactions.test.ts

Lines changed: 5 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -20,29 +20,8 @@ import { expect } from 'chai';
2020
import { Deferred } from '../../util/promise';
2121
import firebase from '../util/firebase_export';
2222
import * as integrationHelpers from '../util/helpers';
23-
import { setAsyncQueue } from '../util/internal_helpers';
24-
import { AsyncQueue, TimerId } from '../../../src/util/async_queue';
25-
import { CancelablePromise } from '../../../src/util/promise';
26-
27-
/**
28-
* Version of the AsyncQueue that overrides `enqueueAfterDelay` to fast-forward
29-
* the backoffs used in retrying transactions.
30-
*/
31-
class AsyncQueueWithoutTransactionBackoff extends AsyncQueue {
32-
enqueueAfterDelay<T extends unknown>(
33-
timerId: TimerId,
34-
delayMs: number,
35-
op: () => Promise<T>
36-
): CancelablePromise<T> {
37-
const result = super.enqueueAfterDelay(timerId, delayMs, op);
38-
if (this.containsDelayedOperation(TimerId.RetryTransaction)) {
39-
this.runDelayedOperationsEarly(TimerId.RetryTransaction)
40-
.then(() => {})
41-
.catch(() => 'obligatory catch');
42-
}
43-
return result;
44-
}
45-
}
23+
import { asyncQueue } from '../util/internal_helpers';
24+
import { TimerId } from '../../../src/util/async_queue';
4625

4726
const apiDescribe = integrationHelpers.apiDescribe;
4827
apiDescribe('Database transactions', (persistence: boolean) => {
@@ -482,7 +461,7 @@ apiDescribe('Database transactions', (persistence: boolean) => {
482461
let started = 0;
483462

484463
return integrationHelpers.withTestDb(persistence, db => {
485-
setAsyncQueue(db, new AsyncQueueWithoutTransactionBackoff());
464+
asyncQueue(db).skipDelaysForTimerId(TimerId.RetryTransaction);
486465
const doc = db.collection('counters').doc();
487466
return doc
488467
.set({
@@ -539,7 +518,7 @@ apiDescribe('Database transactions', (persistence: boolean) => {
539518
let counter = 0;
540519

541520
return integrationHelpers.withTestDb(persistence, db => {
542-
setAsyncQueue(db, new AsyncQueueWithoutTransactionBackoff());
521+
asyncQueue(db).skipDelaysForTimerId(TimerId.RetryTransaction);
543522
const doc = db.collection('counters').doc();
544523
return doc
545524
.set({
@@ -678,7 +657,7 @@ apiDescribe('Database transactions', (persistence: boolean) => {
678657

679658
it('handle reading a doc twice with different versions', () => {
680659
return integrationHelpers.withTestDb(persistence, db => {
681-
setAsyncQueue(db, new AsyncQueueWithoutTransactionBackoff());
660+
asyncQueue(db).skipDelaysForTimerId(TimerId.RetryTransaction);
682661
const doc = db.collection('counters').doc();
683662
let counter = 0;
684663
return doc

packages/firestore/test/integration/util/internal_helpers.ts

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,15 +31,7 @@ import { DEFAULT_PROJECT_ID, DEFAULT_SETTINGS } from './helpers';
3131

3232
/** Helper to retrieve the AsyncQueue for a give FirebaseFirestore instance. */
3333
export function asyncQueue(db: firestore.FirebaseFirestore): AsyncQueue {
34-
return (db as Firestore).getQueue();
35-
}
36-
37-
/** Helper to override the AsyncQueue for a give FirebaseFirestore instance. */
38-
export function setAsyncQueue(
39-
db: firestore.FirebaseFirestore,
40-
newQueue: AsyncQueue
41-
): void {
42-
(db as Firestore).setQueue(newQueue);
34+
return (db as Firestore)._queue;
4335
}
4436

4537
export function getDefaultDatabaseInfo(): DatabaseInfo {

0 commit comments

Comments
 (0)