@naturalcycles/datastore-lib 3.18.3 → 3.19.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.
@@ -60,25 +60,31 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
60
60
  return [];
61
61
  const keys = ids.map(id => this.key(table, id));
62
62
  let rows;
63
- try {
64
- if (this.cfg.timeout) {
63
+ if (this.cfg.timeout) {
64
+ // First try
65
+ try {
65
66
  const r = await (0, js_lib_1.pTimeout)(this.ds().get(keys), {
66
67
  timeout: this.cfg.timeout,
67
68
  name: `datastore.getByIds(${table})`,
68
69
  });
69
70
  rows = r[0];
70
71
  }
71
- else {
72
- rows = (await this.ds().get(keys))[0];
72
+ catch {
73
+ this.cfg.logger.log('datastore recreated on error');
74
+ // This is to debug "GCP Datastore Timeout issue"
75
+ const datastoreLib = require('@google-cloud/datastore');
76
+ const DS = datastoreLib.Datastore;
77
+ this.cachedDatastore = new DS(this.cfg);
78
+ // Second try (will throw)
79
+ const r = await (0, js_lib_1.pTimeout)(this.ds().get(keys), {
80
+ timeout: this.cfg.timeout,
81
+ name: `datastore.getByIds(${table}) second try`,
82
+ });
83
+ rows = r[0];
73
84
  }
74
85
  }
75
- catch (err) {
76
- this.cfg.logger.log('datastore recreated on error');
77
- // This is to debug "GCP Datastore Timeout issue"
78
- const datastoreLib = require('@google-cloud/datastore');
79
- const DS = datastoreLib.Datastore;
80
- this.cachedDatastore = new DS(this.cfg);
81
- throw err;
86
+ else {
87
+ rows = (await this.ds().get(keys))[0];
82
88
  }
83
89
  return (rows
84
90
  .map(r => this.mapId(r))
@@ -137,12 +143,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
137
143
  */
138
144
  async saveBatch(table, rows, opt = {}) {
139
145
  const entities = rows.map(obj => this.toDatastoreEntity(table, obj, opt.excludeFromIndexes));
140
- const save = (0, js_lib_1.pRetry)(async (batch) => {
146
+ const save = (0, js_lib_1.pRetryFn)(async (batch) => {
141
147
  await (opt.tx || this.ds()).save(batch);
142
148
  }, {
143
149
  // Here we retry the GOAWAY errors that are somewhat common for Datastore
144
150
  // Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
145
- predicate: err => RETRY_ON.some(s => err?.message.includes(s)),
151
+ predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
146
152
  name: `DatastoreLib.saveBatch(${table})`,
147
153
  maxAttempts: 5,
148
154
  delay: 5000,
@@ -153,13 +159,19 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
153
159
  logger: this.cfg.logger,
154
160
  });
155
161
  try {
156
- await (0, js_lib_1.pMap)((0, js_lib_1._chunk)(entities, MAX_ITEMS), async (batch) => await save(batch));
162
+ const chunks = (0, js_lib_1._chunk)(entities, MAX_ITEMS);
163
+ if (chunks.length === 1) {
164
+ // Not using pMap in hope to preserve stack trace
165
+ await save(chunks[0]);
166
+ }
167
+ else {
168
+ await (0, js_lib_1.pMap)(chunks, async (batch) => await save(batch));
169
+ }
157
170
  }
158
171
  catch (err) {
159
172
  // console.log(`datastore.save ${kind}`, { obj, entity })
160
173
  this.cfg.logger.error(`error in DatastoreLib.saveBatch for ${table} (${rows.length} rows)`, err);
161
- // don't throw, because datastore SDK makes it in separate thread, so exception will be unhandled otherwise
162
- return await Promise.reject(err);
174
+ throw err;
163
175
  }
164
176
  }
165
177
  async deleteByQuery(q, opt) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/datastore-lib",
3
- "version": "3.18.3",
3
+ "version": "3.19.0",
4
4
  "description": "Opinionated library to work with Google Datastore",
5
5
  "scripts": {
6
6
  "prepare": "husky install"
@@ -17,7 +17,6 @@ import {
17
17
  JsonSchemaObject,
18
18
  JsonSchemaString,
19
19
  pMap,
20
- pRetry,
21
20
  _assert,
22
21
  _chunk,
23
22
  _omit,
@@ -25,6 +24,7 @@ import {
25
24
  CommonLogger,
26
25
  commonLoggerMinLevel,
27
26
  pTimeout,
27
+ pRetryFn,
28
28
  } from '@naturalcycles/js-lib'
29
29
  import { ReadableTyped } from '@naturalcycles/nodejs-lib'
30
30
  import { boldWhite } from '@naturalcycles/nodejs-lib/dist/colors'
@@ -118,25 +118,31 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
118
118
  const keys = ids.map(id => this.key(table, id))
119
119
  let rows: any[]
120
120
 
121
- try {
122
- if (this.cfg.timeout) {
121
+ if (this.cfg.timeout) {
122
+ // First try
123
+ try {
123
124
  const r = await pTimeout(this.ds().get(keys), {
124
125
  timeout: this.cfg.timeout,
125
126
  name: `datastore.getByIds(${table})`,
126
127
  })
127
128
  rows = r[0]
128
- } else {
129
- rows = (await this.ds().get(keys))[0]
130
- }
131
- } catch (err) {
132
- this.cfg.logger.log('datastore recreated on error')
129
+ } catch {
130
+ this.cfg.logger.log('datastore recreated on error')
133
131
 
134
- // This is to debug "GCP Datastore Timeout issue"
135
- const datastoreLib = require('@google-cloud/datastore')
136
- const DS = datastoreLib.Datastore as typeof Datastore
137
- this.cachedDatastore = new DS(this.cfg)
132
+ // This is to debug "GCP Datastore Timeout issue"
133
+ const datastoreLib = require('@google-cloud/datastore')
134
+ const DS = datastoreLib.Datastore as typeof Datastore
135
+ this.cachedDatastore = new DS(this.cfg)
138
136
 
139
- throw err
137
+ // Second try (will throw)
138
+ const r = await pTimeout(this.ds().get(keys), {
139
+ timeout: this.cfg.timeout,
140
+ name: `datastore.getByIds(${table}) second try`,
141
+ })
142
+ rows = r[0]
143
+ }
144
+ } else {
145
+ rows = (await this.ds().get(keys))[0]
140
146
  }
141
147
 
142
148
  return (
@@ -237,14 +243,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
237
243
  this.toDatastoreEntity(table, obj, opt.excludeFromIndexes as string[]),
238
244
  )
239
245
 
240
- const save = pRetry(
246
+ const save = pRetryFn(
241
247
  async (batch: DatastorePayload<ROW>[]) => {
242
248
  await (opt.tx || this.ds()).save(batch)
243
249
  },
244
250
  {
245
251
  // Here we retry the GOAWAY errors that are somewhat common for Datastore
246
252
  // Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
247
- predicate: err => RETRY_ON.some(s => (err as Error)?.message.includes(s)),
253
+ predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
248
254
  name: `DatastoreLib.saveBatch(${table})`,
249
255
  maxAttempts: 5,
250
256
  delay: 5000,
@@ -257,15 +263,21 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
257
263
  )
258
264
 
259
265
  try {
260
- await pMap(_chunk(entities, MAX_ITEMS), async batch => await save(batch))
266
+ const chunks = _chunk(entities, MAX_ITEMS)
267
+ if (chunks.length === 1) {
268
+ // Not using pMap in hope to preserve stack trace
269
+ await save(chunks[0]!)
270
+ } else {
271
+ await pMap(chunks, async batch => await save(batch))
272
+ }
261
273
  } catch (err) {
262
274
  // console.log(`datastore.save ${kind}`, { obj, entity })
263
275
  this.cfg.logger.error(
264
276
  `error in DatastoreLib.saveBatch for ${table} (${rows.length} rows)`,
265
277
  err,
266
278
  )
267
- // don't throw, because datastore SDK makes it in separate thread, so exception will be unhandled otherwise
268
- return await Promise.reject(err)
279
+
280
+ throw err
269
281
  }
270
282
  }
271
283