shelving 1.276.0 → 1.276.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -108,7 +108,7 @@ export declare class FirestoreProvider<I extends string = string, T extends Data
108
108
  deleteQuery<II extends I, TT extends T>(collection: Collection<string, II, TT>, query: Query<Item<II, TT>>): Promise<void>;
109
109
  /**
110
110
  * Runs the callback in a Firestore transaction: begin → reads with the transaction id → buffered writes committed atomically.
111
- * - Retries the whole callback (up to 5 attempts) when the commit is aborted by contention, so the callback must have no side effects other than through its provider.
111
+ * - Retries the whole callback (up to 5 attempts, with jittered exponential backoff) when the commit is aborted by contention, so the callback must have no side effects other than through its provider.
112
112
  * - Rolls back and rethrows if the callback throws.
113
113
  * - Reads see a consistent snapshot and never the transaction's own buffered writes.
114
114
  */
@@ -1,12 +1,13 @@
1
1
  import { DBProvider } from "../db/provider/DBProvider.js";
2
2
  import { ResponseError } from "../error/ResponseError.js";
3
3
  import { UnsupportedError } from "../error/UnsupportedError.js";
4
+ import { getDelay } from "../util/async.js";
4
5
  import { joinDataPath } from "../util/data.js";
5
6
  import { BLACKHOLE } from "../util/function.js";
6
7
  import { getItem } from "../util/item.js";
7
8
  import { isPlainObject } from "../util/object.js";
8
9
  import { getQueryFilters, getQueryLimit, getQueryOrders } from "../util/query.js";
9
- import { getRandomKey } from "../util/random.js";
10
+ import { getRandom, getRandomKey } from "../util/random.js";
10
11
  import { getUpdates } from "../util/update.js";
11
12
  import { toData, toFirestoreFields, toFirestoreValue } from "./value.js";
12
13
  // Constants.
@@ -260,7 +261,7 @@ export class FirestoreProvider extends DBProvider {
260
261
  }
261
262
  /**
262
263
  * Runs the callback in a Firestore transaction: begin → reads with the transaction id → buffered writes committed atomically.
263
- * - Retries the whole callback (up to 5 attempts) when the commit is aborted by contention, so the callback must have no side effects other than through its provider.
264
+ * - Retries the whole callback (up to 5 attempts, with jittered exponential backoff) when the commit is aborted by contention, so the callback must have no side effects other than through its provider.
264
265
  * - Rolls back and rethrows if the callback throws.
265
266
  * - Reads see a consistent snapshot and never the transaction's own buffered writes.
266
267
  */
@@ -268,6 +269,9 @@ export class FirestoreProvider extends DBProvider {
268
269
  let retryTransaction;
269
270
  let aborted;
270
271
  for (let attempt = 0; attempt < TRANSACTION_ATTEMPTS; attempt++) {
272
+ // Back off with jitter before each retry so contending transactions de-synchronise instead of re-aborting each other in lockstep.
273
+ if (attempt)
274
+ await getDelay(getRandom(0, 100 * 2 ** attempt));
271
275
  const { transaction } = (await this._request("beginTransaction", {
272
276
  options: { readWrite: retryTransaction ? { retryTransaction } : {} },
273
277
  }));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shelving",
3
- "version": "1.276.0",
3
+ "version": "1.276.1",
4
4
  "author": "Dave Houlbrooke <dave@shax.com>",
5
5
  "repository": {
6
6
  "type": "git",