@naturalcycles/datastore-lib 3.18.1 → 3.19.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.
- package/dist/datastore.db.js +38 -18
- package/package.json +1 -1
- package/src/datastore.db.ts +39 -21
package/dist/datastore.db.js
CHANGED
|
@@ -35,8 +35,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
35
35
|
const datastoreLib = require('@google-cloud/datastore');
|
|
36
36
|
const DS = datastoreLib.Datastore;
|
|
37
37
|
(_a = this.cfg).projectId || (_a.projectId = this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT']);
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
if (this.cfg.projectId) {
|
|
39
|
+
this.cfg.logger.log(`DatastoreDB connected to ${(0, colors_1.boldWhite)(this.cfg.projectId)}`);
|
|
40
|
+
}
|
|
41
|
+
else if (process.env['GOOGLE_APPLICATION_CREDENTIALS']) {
|
|
42
|
+
this.cfg.logger.log(`DatastoreDB connected via GOOGLE_APPLICATION_CREDENTIALS`);
|
|
43
|
+
}
|
|
40
44
|
if (this.cfg.useLegacyGRPC) {
|
|
41
45
|
this.cfg.grpc = require('grpc');
|
|
42
46
|
}
|
|
@@ -56,25 +60,35 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
56
60
|
return [];
|
|
57
61
|
const keys = ids.map(id => this.key(table, id));
|
|
58
62
|
let rows;
|
|
59
|
-
|
|
60
|
-
|
|
63
|
+
if (this.cfg.timeout) {
|
|
64
|
+
// First try
|
|
65
|
+
try {
|
|
61
66
|
const r = await (0, js_lib_1.pTimeout)(this.ds().get(keys), {
|
|
62
67
|
timeout: this.cfg.timeout,
|
|
63
68
|
name: `datastore.getByIds(${table})`,
|
|
64
69
|
});
|
|
65
70
|
rows = r[0];
|
|
66
71
|
}
|
|
67
|
-
|
|
68
|
-
|
|
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
|
+
errorData: {
|
|
83
|
+
// This error will be grouped ACROSS all endpoints and usages
|
|
84
|
+
fingerprint: ['DATASTORE_TIMEOUT'],
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
rows = r[0];
|
|
69
88
|
}
|
|
70
89
|
}
|
|
71
|
-
|
|
72
|
-
this.
|
|
73
|
-
// This is to debug "GCP Datastore Timeout issue"
|
|
74
|
-
const datastoreLib = require('@google-cloud/datastore');
|
|
75
|
-
const DS = datastoreLib.Datastore;
|
|
76
|
-
this.cachedDatastore = new DS(this.cfg);
|
|
77
|
-
throw err;
|
|
90
|
+
else {
|
|
91
|
+
rows = (await this.ds().get(keys))[0];
|
|
78
92
|
}
|
|
79
93
|
return (rows
|
|
80
94
|
.map(r => this.mapId(r))
|
|
@@ -133,12 +147,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
133
147
|
*/
|
|
134
148
|
async saveBatch(table, rows, opt = {}) {
|
|
135
149
|
const entities = rows.map(obj => this.toDatastoreEntity(table, obj, opt.excludeFromIndexes));
|
|
136
|
-
const save = (0, js_lib_1.
|
|
150
|
+
const save = (0, js_lib_1.pRetryFn)(async (batch) => {
|
|
137
151
|
await (opt.tx || this.ds()).save(batch);
|
|
138
152
|
}, {
|
|
139
153
|
// Here we retry the GOAWAY errors that are somewhat common for Datastore
|
|
140
154
|
// Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
|
|
141
|
-
predicate: err => RETRY_ON.some(s => err?.message
|
|
155
|
+
predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
|
|
142
156
|
name: `DatastoreLib.saveBatch(${table})`,
|
|
143
157
|
maxAttempts: 5,
|
|
144
158
|
delay: 5000,
|
|
@@ -149,13 +163,19 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
149
163
|
logger: this.cfg.logger,
|
|
150
164
|
});
|
|
151
165
|
try {
|
|
152
|
-
|
|
166
|
+
const chunks = (0, js_lib_1._chunk)(entities, MAX_ITEMS);
|
|
167
|
+
if (chunks.length === 1) {
|
|
168
|
+
// Not using pMap in hope to preserve stack trace
|
|
169
|
+
await save(chunks[0]);
|
|
170
|
+
}
|
|
171
|
+
else {
|
|
172
|
+
await (0, js_lib_1.pMap)(chunks, async (batch) => await save(batch));
|
|
173
|
+
}
|
|
153
174
|
}
|
|
154
175
|
catch (err) {
|
|
155
176
|
// console.log(`datastore.save ${kind}`, { obj, entity })
|
|
156
177
|
this.cfg.logger.error(`error in DatastoreLib.saveBatch for ${table} (${rows.length} rows)`, err);
|
|
157
|
-
|
|
158
|
-
return await Promise.reject(err);
|
|
178
|
+
throw err;
|
|
159
179
|
}
|
|
160
180
|
}
|
|
161
181
|
async deleteByQuery(q, opt) {
|
package/package.json
CHANGED
package/src/datastore.db.ts
CHANGED
|
@@ -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'
|
|
@@ -84,9 +84,11 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
84
84
|
const DS = datastoreLib.Datastore as typeof Datastore
|
|
85
85
|
this.cfg.projectId ||= this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT']
|
|
86
86
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
87
|
+
if (this.cfg.projectId) {
|
|
88
|
+
this.cfg.logger.log(`DatastoreDB connected to ${boldWhite(this.cfg.projectId)}`)
|
|
89
|
+
} else if (process.env['GOOGLE_APPLICATION_CREDENTIALS']) {
|
|
90
|
+
this.cfg.logger.log(`DatastoreDB connected via GOOGLE_APPLICATION_CREDENTIALS`)
|
|
91
|
+
}
|
|
90
92
|
|
|
91
93
|
if (this.cfg.useLegacyGRPC) {
|
|
92
94
|
this.cfg.grpc = require('grpc')
|
|
@@ -116,25 +118,35 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
116
118
|
const keys = ids.map(id => this.key(table, id))
|
|
117
119
|
let rows: any[]
|
|
118
120
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
+
if (this.cfg.timeout) {
|
|
122
|
+
// First try
|
|
123
|
+
try {
|
|
121
124
|
const r = await pTimeout(this.ds().get(keys), {
|
|
122
125
|
timeout: this.cfg.timeout,
|
|
123
126
|
name: `datastore.getByIds(${table})`,
|
|
124
127
|
})
|
|
125
128
|
rows = r[0]
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
}
|
|
129
|
-
} catch (err) {
|
|
130
|
-
this.cfg.logger.log('datastore recreated on error')
|
|
129
|
+
} catch {
|
|
130
|
+
this.cfg.logger.log('datastore recreated on error')
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
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)
|
|
136
136
|
|
|
137
|
-
|
|
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
|
+
errorData: {
|
|
142
|
+
// This error will be grouped ACROSS all endpoints and usages
|
|
143
|
+
fingerprint: ['DATASTORE_TIMEOUT'],
|
|
144
|
+
},
|
|
145
|
+
})
|
|
146
|
+
rows = r[0]
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
rows = (await this.ds().get(keys))[0]
|
|
138
150
|
}
|
|
139
151
|
|
|
140
152
|
return (
|
|
@@ -235,14 +247,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
235
247
|
this.toDatastoreEntity(table, obj, opt.excludeFromIndexes as string[]),
|
|
236
248
|
)
|
|
237
249
|
|
|
238
|
-
const save =
|
|
250
|
+
const save = pRetryFn(
|
|
239
251
|
async (batch: DatastorePayload<ROW>[]) => {
|
|
240
252
|
await (opt.tx || this.ds()).save(batch)
|
|
241
253
|
},
|
|
242
254
|
{
|
|
243
255
|
// Here we retry the GOAWAY errors that are somewhat common for Datastore
|
|
244
256
|
// Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
|
|
245
|
-
predicate: err => RETRY_ON.some(s =>
|
|
257
|
+
predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
|
|
246
258
|
name: `DatastoreLib.saveBatch(${table})`,
|
|
247
259
|
maxAttempts: 5,
|
|
248
260
|
delay: 5000,
|
|
@@ -255,15 +267,21 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
255
267
|
)
|
|
256
268
|
|
|
257
269
|
try {
|
|
258
|
-
|
|
270
|
+
const chunks = _chunk(entities, MAX_ITEMS)
|
|
271
|
+
if (chunks.length === 1) {
|
|
272
|
+
// Not using pMap in hope to preserve stack trace
|
|
273
|
+
await save(chunks[0]!)
|
|
274
|
+
} else {
|
|
275
|
+
await pMap(chunks, async batch => await save(batch))
|
|
276
|
+
}
|
|
259
277
|
} catch (err) {
|
|
260
278
|
// console.log(`datastore.save ${kind}`, { obj, entity })
|
|
261
279
|
this.cfg.logger.error(
|
|
262
280
|
`error in DatastoreLib.saveBatch for ${table} (${rows.length} rows)`,
|
|
263
281
|
err,
|
|
264
282
|
)
|
|
265
|
-
|
|
266
|
-
|
|
283
|
+
|
|
284
|
+
throw err
|
|
267
285
|
}
|
|
268
286
|
}
|
|
269
287
|
|