@naturalcycles/datastore-lib 3.23.2 → 3.24.0

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.
@@ -57,4 +57,5 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
57
57
  getKey(key: Key): string | undefined;
58
58
  getTables(): Promise<string[]>;
59
59
  getTableSchema<ROW extends ObjectWithId>(table: string): Promise<JsonSchemaRootObject<ROW>>;
60
+ private getPRetryOptions;
60
61
  }
@@ -155,19 +155,7 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
155
155
  const method = methodMap[opt.saveMethod || 'upsert'] || 'save';
156
156
  const save = (0, js_lib_1.pRetryFn)(async (batch) => {
157
157
  await (opt.tx || this.ds())[method](batch);
158
- }, {
159
- // Here we retry the GOAWAY errors that are somewhat common for Datastore
160
- // Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
161
- predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
162
- name: `DatastoreLib.saveBatch(${table})`,
163
- maxAttempts: 5,
164
- delay: 5000,
165
- delayMultiplier: 2,
166
- logFirstAttempt: false,
167
- logFailures: true,
168
- // logAll: true,
169
- logger: this.cfg.logger,
170
- });
158
+ }, this.getPRetryOptions(`DatastoreLib.saveBatch(${table})`));
171
159
  try {
172
160
  const chunks = (0, js_lib_1._chunk)(entities, MAX_ITEMS);
173
161
  if (chunks.length === 1) {
@@ -203,27 +191,30 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
203
191
  * https://cloud.google.com/datastore/docs/concepts/transactions#datastore-datastore-transactional-update-nodejs
204
192
  */
205
193
  async commitTransaction(_tx, opt) {
206
- const tx = this.ds().transaction();
207
- try {
208
- await tx.run();
209
- // const ops = mergeDBOperations(_tx.ops)
210
- for await (const op of _tx.ops) {
211
- if (op.type === 'saveBatch') {
212
- await this.saveBatch(op.table, op.rows, { ...opt, tx });
213
- }
214
- else if (op.type === 'deleteByIds') {
215
- await this.deleteByIds(op.table, op.ids, { ...opt, tx });
216
- }
217
- else {
218
- throw new Error(`DBOperation not supported: ${op.type}`);
194
+ // Using Retry, because Datastore can throw errors like "too much contention" here
195
+ await (0, js_lib_1.pRetry)(async () => {
196
+ const tx = this.ds().transaction();
197
+ try {
198
+ await tx.run();
199
+ const ops = (0, db_lib_1.mergeDBOperations)(_tx.ops);
200
+ for await (const op of ops) {
201
+ if (op.type === 'saveBatch') {
202
+ await this.saveBatch(op.table, op.rows, { ...opt, tx });
203
+ }
204
+ else if (op.type === 'deleteByIds') {
205
+ await this.deleteByIds(op.table, op.ids, { ...opt, tx });
206
+ }
207
+ else {
208
+ throw new Error(`DBOperation not supported: ${op.type}`);
209
+ }
219
210
  }
211
+ await tx.commit();
220
212
  }
221
- await tx.commit();
222
- }
223
- catch (err) {
224
- void tx.rollback();
225
- throw err; // rethrow
226
- }
213
+ catch (err) {
214
+ await tx.rollback();
215
+ throw err; // rethrow
216
+ }
217
+ }, this.getPRetryOptions(`DatastoreLib.commitTransaction`));
227
218
  }
228
219
  async getAllStats() {
229
220
  const q = this.ds().createQuery('__Stat_Kind__');
@@ -356,5 +347,18 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
356
347
  });
357
348
  return s;
358
349
  }
350
+ getPRetryOptions(name) {
351
+ return {
352
+ predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
353
+ name,
354
+ maxAttempts: 5,
355
+ delay: 5000,
356
+ delayMultiplier: 2,
357
+ logFirstAttempt: false,
358
+ logFailures: true,
359
+ // logAll: true,
360
+ logger: this.cfg.logger,
361
+ };
362
+ }
359
363
  }
360
364
  exports.DatastoreDB = DatastoreDB;
@@ -1,6 +1,6 @@
1
1
  import type { DatastoreOptions, Key, Transaction } from '@google-cloud/datastore';
2
2
  import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
3
- import { AnyObjectWithId, CommonLogger, ObjectWithId } from '@naturalcycles/js-lib';
3
+ import { CommonLogger, ObjectWithId } from '@naturalcycles/js-lib';
4
4
  export interface DatastorePayload<T = any> {
5
5
  key: Key;
6
6
  data: T;
@@ -99,7 +99,7 @@ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
99
99
  export interface DatastoreDBOptions extends CommonDBOptions {
100
100
  tx?: Transaction;
101
101
  }
102
- export interface DatastoreDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyObjectWithId> extends CommonDBSaveOptions<ROW> {
102
+ export interface DatastoreDBSaveOptions<ROW extends Partial<ObjectWithId> = any> extends CommonDBSaveOptions<ROW> {
103
103
  tx?: Transaction;
104
104
  }
105
105
  export interface DatastoreStats {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/datastore-lib",
3
- "version": "3.23.2",
3
+ "version": "3.24.0",
4
4
  "description": "Opinionated library to work with Google Datastore",
5
5
  "scripts": {
6
6
  "prepare": "husky install"
@@ -6,6 +6,7 @@ import {
6
6
  CommonDBSaveMethod,
7
7
  DBQuery,
8
8
  DBTransaction,
9
+ mergeDBOperations,
9
10
  RunQueryResult,
10
11
  } from '@naturalcycles/db-lib'
11
12
  import {
@@ -25,6 +26,8 @@ import {
25
26
  commonLoggerMinLevel,
26
27
  pTimeout,
27
28
  pRetryFn,
29
+ pRetry,
30
+ PRetryOptions,
28
31
  } from '@naturalcycles/js-lib'
29
32
  import { ReadableTyped } from '@naturalcycles/nodejs-lib'
30
33
  import { boldWhite } from '@naturalcycles/nodejs-lib/dist/colors'
@@ -255,24 +258,9 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
255
258
 
256
259
  const method = methodMap[opt.saveMethod || 'upsert'] || 'save'
257
260
 
258
- const save = pRetryFn(
259
- async (batch: DatastorePayload<ROW>[]) => {
260
- await (opt.tx || this.ds())[method](batch)
261
- },
262
- {
263
- // Here we retry the GOAWAY errors that are somewhat common for Datastore
264
- // Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
265
- predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
266
- name: `DatastoreLib.saveBatch(${table})`,
267
- maxAttempts: 5,
268
- delay: 5000,
269
- delayMultiplier: 2,
270
- logFirstAttempt: false,
271
- logFailures: true,
272
- // logAll: true,
273
- logger: this.cfg.logger,
274
- },
275
- )
261
+ const save = pRetryFn(async (batch: DatastorePayload<ROW>[]) => {
262
+ await (opt.tx || this.ds())[method](batch)
263
+ }, this.getPRetryOptions(`DatastoreLib.saveBatch(${table})`))
276
264
 
277
265
  try {
278
266
  const chunks = _chunk(entities, MAX_ITEMS)
@@ -328,28 +316,31 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
328
316
  _tx: DBTransaction,
329
317
  opt?: DatastoreDBSaveOptions,
330
318
  ): Promise<void> {
331
- const tx = this.ds().transaction()
319
+ // Using Retry, because Datastore can throw errors like "too much contention" here
320
+ await pRetry(async () => {
321
+ const tx = this.ds().transaction()
332
322
 
333
- try {
334
- await tx.run()
323
+ try {
324
+ await tx.run()
335
325
 
336
- // const ops = mergeDBOperations(_tx.ops)
326
+ const ops = mergeDBOperations(_tx.ops)
337
327
 
338
- for await (const op of _tx.ops) {
339
- if (op.type === 'saveBatch') {
340
- await this.saveBatch(op.table, op.rows, { ...opt, tx })
341
- } else if (op.type === 'deleteByIds') {
342
- await this.deleteByIds(op.table, op.ids, { ...opt, tx })
343
- } else {
344
- throw new Error(`DBOperation not supported: ${(op as any).type}`)
328
+ for await (const op of ops) {
329
+ if (op.type === 'saveBatch') {
330
+ await this.saveBatch(op.table, op.rows, { ...opt, tx })
331
+ } else if (op.type === 'deleteByIds') {
332
+ await this.deleteByIds(op.table, op.ids, { ...opt, tx })
333
+ } else {
334
+ throw new Error(`DBOperation not supported: ${(op as any).type}`)
335
+ }
345
336
  }
346
- }
347
337
 
348
- await tx.commit()
349
- } catch (err) {
350
- void tx.rollback()
351
- throw err // rethrow
352
- }
338
+ await tx.commit()
339
+ } catch (err) {
340
+ await tx.rollback()
341
+ throw err // rethrow
342
+ }
343
+ }, this.getPRetryOptions(`DatastoreLib.commitTransaction`))
353
344
  }
354
345
 
355
346
  async getAllStats(): Promise<DatastoreStats[]> {
@@ -496,4 +487,18 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
496
487
 
497
488
  return s
498
489
  }
490
+
491
+ private getPRetryOptions(name: string): PRetryOptions {
492
+ return {
493
+ predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
494
+ name,
495
+ maxAttempts: 5,
496
+ delay: 5000,
497
+ delayMultiplier: 2,
498
+ logFirstAttempt: false,
499
+ logFailures: true,
500
+ // logAll: true,
501
+ logger: this.cfg.logger,
502
+ }
503
+ }
499
504
  }
@@ -1,6 +1,6 @@
1
1
  import type { DatastoreOptions, Key, Transaction } from '@google-cloud/datastore'
2
2
  import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib'
3
- import { AnyObjectWithId, CommonLogger, ObjectWithId } from '@naturalcycles/js-lib'
3
+ import { CommonLogger, ObjectWithId } from '@naturalcycles/js-lib'
4
4
 
5
5
  export interface DatastorePayload<T = any> {
6
6
  key: Key
@@ -114,7 +114,7 @@ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
114
114
  export interface DatastoreDBOptions extends CommonDBOptions {
115
115
  tx?: Transaction
116
116
  }
117
- export interface DatastoreDBSaveOptions<ROW extends Partial<ObjectWithId> = AnyObjectWithId>
117
+ export interface DatastoreDBSaveOptions<ROW extends Partial<ObjectWithId> = any>
118
118
  extends CommonDBSaveOptions<ROW> {
119
119
  tx?: Transaction
120
120
  }