@naturalcycles/datastore-lib 3.18.0 → 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.
- package/dist/datastore.db.js +41 -16
- package/dist/datastore.model.d.ts +7 -0
- package/package.json +1 -1
- package/src/datastore.db.ts +42 -19
- package/src/datastore.model.ts +8 -0
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,16 +60,31 @@ 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 {
|
|
66
|
+
const r = await (0, js_lib_1.pTimeout)(this.ds().get(keys), {
|
|
67
|
+
timeout: this.cfg.timeout,
|
|
68
|
+
name: `datastore.getByIds(${table})`,
|
|
69
|
+
});
|
|
70
|
+
rows = r[0];
|
|
71
|
+
}
|
|
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];
|
|
84
|
+
}
|
|
61
85
|
}
|
|
62
|
-
|
|
63
|
-
this.
|
|
64
|
-
// This is to debug "GCP Datastore Timeout issue"
|
|
65
|
-
const datastoreLib = require('@google-cloud/datastore');
|
|
66
|
-
const DS = datastoreLib.Datastore;
|
|
67
|
-
this.cachedDatastore = new DS(this.cfg);
|
|
68
|
-
throw err;
|
|
86
|
+
else {
|
|
87
|
+
rows = (await this.ds().get(keys))[0];
|
|
69
88
|
}
|
|
70
89
|
return (rows
|
|
71
90
|
.map(r => this.mapId(r))
|
|
@@ -124,12 +143,12 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
124
143
|
*/
|
|
125
144
|
async saveBatch(table, rows, opt = {}) {
|
|
126
145
|
const entities = rows.map(obj => this.toDatastoreEntity(table, obj, opt.excludeFromIndexes));
|
|
127
|
-
const save = (0, js_lib_1.
|
|
146
|
+
const save = (0, js_lib_1.pRetryFn)(async (batch) => {
|
|
128
147
|
await (opt.tx || this.ds()).save(batch);
|
|
129
148
|
}, {
|
|
130
149
|
// Here we retry the GOAWAY errors that are somewhat common for Datastore
|
|
131
150
|
// Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
|
|
132
|
-
predicate: err => RETRY_ON.some(s => err?.message
|
|
151
|
+
predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
|
|
133
152
|
name: `DatastoreLib.saveBatch(${table})`,
|
|
134
153
|
maxAttempts: 5,
|
|
135
154
|
delay: 5000,
|
|
@@ -140,13 +159,19 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
140
159
|
logger: this.cfg.logger,
|
|
141
160
|
});
|
|
142
161
|
try {
|
|
143
|
-
|
|
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
|
+
}
|
|
144
170
|
}
|
|
145
171
|
catch (err) {
|
|
146
172
|
// console.log(`datastore.save ${kind}`, { obj, entity })
|
|
147
173
|
this.cfg.logger.error(`error in DatastoreLib.saveBatch for ${table} (${rows.length} rows)`, err);
|
|
148
|
-
|
|
149
|
-
return await Promise.reject(err);
|
|
174
|
+
throw err;
|
|
150
175
|
}
|
|
151
176
|
}
|
|
152
177
|
async deleteByQuery(q, opt) {
|
|
@@ -34,6 +34,13 @@ export interface DatastoreDBCfg extends DatastoreOptions {
|
|
|
34
34
|
* Default to `console`
|
|
35
35
|
*/
|
|
36
36
|
logger?: CommonLogger;
|
|
37
|
+
/**
|
|
38
|
+
* Experimental option, currently only applies to `getByIds`.
|
|
39
|
+
* Applies pTimeout to Datastore operation, re-creates Datastore on any error.
|
|
40
|
+
*
|
|
41
|
+
* @experimental
|
|
42
|
+
*/
|
|
43
|
+
timeout?: number;
|
|
37
44
|
}
|
|
38
45
|
export interface DatastoreCredentials {
|
|
39
46
|
type?: string;
|
package/package.json
CHANGED
package/src/datastore.db.ts
CHANGED
|
@@ -17,13 +17,14 @@ import {
|
|
|
17
17
|
JsonSchemaObject,
|
|
18
18
|
JsonSchemaString,
|
|
19
19
|
pMap,
|
|
20
|
-
pRetry,
|
|
21
20
|
_assert,
|
|
22
21
|
_chunk,
|
|
23
22
|
_omit,
|
|
24
23
|
JsonSchemaRootObject,
|
|
25
24
|
CommonLogger,
|
|
26
25
|
commonLoggerMinLevel,
|
|
26
|
+
pTimeout,
|
|
27
|
+
pRetryFn,
|
|
27
28
|
} from '@naturalcycles/js-lib'
|
|
28
29
|
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
29
30
|
import { boldWhite } from '@naturalcycles/nodejs-lib/dist/colors'
|
|
@@ -83,9 +84,11 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
83
84
|
const DS = datastoreLib.Datastore as typeof Datastore
|
|
84
85
|
this.cfg.projectId ||= this.cfg.credentials?.project_id || process.env['GOOGLE_CLOUD_PROJECT']
|
|
85
86
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
+
}
|
|
89
92
|
|
|
90
93
|
if (this.cfg.useLegacyGRPC) {
|
|
91
94
|
this.cfg.grpc = require('grpc')
|
|
@@ -115,17 +118,31 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
115
118
|
const keys = ids.map(id => this.key(table, id))
|
|
116
119
|
let rows: any[]
|
|
117
120
|
|
|
118
|
-
|
|
121
|
+
if (this.cfg.timeout) {
|
|
122
|
+
// First try
|
|
123
|
+
try {
|
|
124
|
+
const r = await pTimeout(this.ds().get(keys), {
|
|
125
|
+
timeout: this.cfg.timeout,
|
|
126
|
+
name: `datastore.getByIds(${table})`,
|
|
127
|
+
})
|
|
128
|
+
rows = r[0]
|
|
129
|
+
} catch {
|
|
130
|
+
this.cfg.logger.log('datastore recreated on error')
|
|
131
|
+
|
|
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
|
+
|
|
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 {
|
|
119
145
|
rows = (await this.ds().get(keys))[0]
|
|
120
|
-
} catch (err) {
|
|
121
|
-
this.cfg.logger.log('datastore recreated on error')
|
|
122
|
-
|
|
123
|
-
// This is to debug "GCP Datastore Timeout issue"
|
|
124
|
-
const datastoreLib = require('@google-cloud/datastore')
|
|
125
|
-
const DS = datastoreLib.Datastore as typeof Datastore
|
|
126
|
-
this.cachedDatastore = new DS(this.cfg)
|
|
127
|
-
|
|
128
|
-
throw err
|
|
129
146
|
}
|
|
130
147
|
|
|
131
148
|
return (
|
|
@@ -226,14 +243,14 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
226
243
|
this.toDatastoreEntity(table, obj, opt.excludeFromIndexes as string[]),
|
|
227
244
|
)
|
|
228
245
|
|
|
229
|
-
const save =
|
|
246
|
+
const save = pRetryFn(
|
|
230
247
|
async (batch: DatastorePayload<ROW>[]) => {
|
|
231
248
|
await (opt.tx || this.ds()).save(batch)
|
|
232
249
|
},
|
|
233
250
|
{
|
|
234
251
|
// Here we retry the GOAWAY errors that are somewhat common for Datastore
|
|
235
252
|
// Currently only retrying them here in .saveBatch(), cause probably they're only thrown when saving
|
|
236
|
-
predicate: err => RETRY_ON.some(s =>
|
|
253
|
+
predicate: err => RETRY_ON.some(s => err?.message?.includes(s)),
|
|
237
254
|
name: `DatastoreLib.saveBatch(${table})`,
|
|
238
255
|
maxAttempts: 5,
|
|
239
256
|
delay: 5000,
|
|
@@ -246,15 +263,21 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
246
263
|
)
|
|
247
264
|
|
|
248
265
|
try {
|
|
249
|
-
|
|
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
|
+
}
|
|
250
273
|
} catch (err) {
|
|
251
274
|
// console.log(`datastore.save ${kind}`, { obj, entity })
|
|
252
275
|
this.cfg.logger.error(
|
|
253
276
|
`error in DatastoreLib.saveBatch for ${table} (${rows.length} rows)`,
|
|
254
277
|
err,
|
|
255
278
|
)
|
|
256
|
-
|
|
257
|
-
|
|
279
|
+
|
|
280
|
+
throw err
|
|
258
281
|
}
|
|
259
282
|
}
|
|
260
283
|
|
package/src/datastore.model.ts
CHANGED
|
@@ -41,6 +41,14 @@ export interface DatastoreDBCfg extends DatastoreOptions {
|
|
|
41
41
|
* Default to `console`
|
|
42
42
|
*/
|
|
43
43
|
logger?: CommonLogger
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Experimental option, currently only applies to `getByIds`.
|
|
47
|
+
* Applies pTimeout to Datastore operation, re-creates Datastore on any error.
|
|
48
|
+
*
|
|
49
|
+
* @experimental
|
|
50
|
+
*/
|
|
51
|
+
timeout?: number
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export interface DatastoreCredentials {
|