@naturalcycles/datastore-lib 4.15.0 → 4.16.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.d.ts +2 -2
- package/dist/datastore.db.js +6 -14
- package/dist/datastoreKeyValueDB.d.ts +4 -4
- package/dist/datastoreKeyValueDB.js +3 -9
- package/dist/datastoreStreamReadable.js +15 -2
- package/package.json +2 -2
- package/src/datastore.db.ts +7 -18
- package/src/datastoreKeyValueDB.ts +7 -17
- package/src/datastoreStreamReadable.ts +16 -2
package/dist/datastore.db.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ import { BaseCommonDB } from '@naturalcycles/db-lib';
|
|
|
4
4
|
import type { JsonSchemaObject, JsonSchemaRootObject } from '@naturalcycles/js-lib/json-schema';
|
|
5
5
|
import type { CommonLogger } from '@naturalcycles/js-lib/log';
|
|
6
6
|
import { type ObjectWithId, type StringMap } from '@naturalcycles/js-lib/types';
|
|
7
|
-
import
|
|
7
|
+
import { Pipeline } from '@naturalcycles/nodejs-lib/stream';
|
|
8
8
|
import type { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBReadOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePropertyStats, DatastoreStats } from './datastore.model.js';
|
|
9
9
|
/**
|
|
10
10
|
* Datastore API:
|
|
@@ -31,7 +31,7 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
31
31
|
runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<RunQueryResult<ROW>>;
|
|
32
32
|
runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBReadOptions): Promise<number>;
|
|
33
33
|
private runDatastoreQuery;
|
|
34
|
-
streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBStreamOptions):
|
|
34
|
+
streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBStreamOptions): Pipeline<ROW>;
|
|
35
35
|
/**
|
|
36
36
|
* Returns saved entities with generated id/updated/created (non-mutating!)
|
|
37
37
|
*/
|
package/dist/datastore.db.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Transform } from 'node:stream';
|
|
2
1
|
import { BaseCommonDB, commonDBFullSupport } from '@naturalcycles/db-lib';
|
|
3
2
|
import { _round } from '@naturalcycles/js-lib';
|
|
4
3
|
import { _chunk } from '@naturalcycles/js-lib/array/array.util.js';
|
|
@@ -10,6 +9,7 @@ import { pRetry, pRetryFn } from '@naturalcycles/js-lib/promise/pRetry.js';
|
|
|
10
9
|
import { pTimeout } from '@naturalcycles/js-lib/promise/pTimeout.js';
|
|
11
10
|
import { _stringMapEntries, _stringMapValues, } from '@naturalcycles/js-lib/types';
|
|
12
11
|
import { boldWhite } from '@naturalcycles/nodejs-lib/colors';
|
|
12
|
+
import { Pipeline } from '@naturalcycles/nodejs-lib/stream';
|
|
13
13
|
import { DatastoreType } from './datastore.model.js';
|
|
14
14
|
import { DatastoreStreamReadable } from './datastoreStreamReadable.js';
|
|
15
15
|
import { dbQueryToDatastoreQuery } from './query.util.js';
|
|
@@ -209,26 +209,18 @@ export class DatastoreDB extends BaseCommonDB {
|
|
|
209
209
|
};
|
|
210
210
|
}
|
|
211
211
|
streamQuery(dbQuery, _opt) {
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
transform: (chunk, _, cb) => {
|
|
215
|
-
cb(null, this.mapId(chunk));
|
|
216
|
-
},
|
|
217
|
-
});
|
|
218
|
-
void this.ds().then(async (ds) => {
|
|
212
|
+
return Pipeline.fromAsyncReadable(async () => {
|
|
213
|
+
const ds = await this.ds();
|
|
219
214
|
const q = dbQueryToDatastoreQuery(dbQuery, ds.createQuery(dbQuery.table), await this.getPropertyFilter());
|
|
220
215
|
const opt = {
|
|
221
216
|
logger: this.cfg.logger,
|
|
222
217
|
...this.cfg.streamOptions,
|
|
223
218
|
..._opt,
|
|
224
219
|
};
|
|
225
|
-
|
|
220
|
+
return opt.experimentalCursorStream
|
|
226
221
|
? new DatastoreStreamReadable(q, opt)
|
|
227
|
-
: ds.runQueryStream(q, this.getRunQueryOptions(opt))
|
|
228
|
-
|
|
229
|
-
.pipe(transform);
|
|
230
|
-
});
|
|
231
|
-
return transform;
|
|
222
|
+
: ds.runQueryStream(q, this.getRunQueryOptions(opt));
|
|
223
|
+
}).mapSync(r => this.mapId(r));
|
|
232
224
|
}
|
|
233
225
|
// https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
|
|
234
226
|
/**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { CommonKeyValueDB, IncrementTuple, KeyValueDBTuple } from '@naturalcycles/db-lib/kv';
|
|
2
|
-
import type {
|
|
2
|
+
import type { Pipeline } from '@naturalcycles/nodejs-lib/stream';
|
|
3
3
|
import { DatastoreDB } from './datastore.db.js';
|
|
4
4
|
import type { DatastoreDBCfg } from './datastore.model.js';
|
|
5
5
|
export interface DatastoreKeyValueDBCfg extends DatastoreDBCfg {
|
|
@@ -14,9 +14,9 @@ export declare class DatastoreKeyValueDB implements CommonKeyValueDB {
|
|
|
14
14
|
getByIds(table: string, ids: string[]): Promise<KeyValueDBTuple[]>;
|
|
15
15
|
deleteByIds(table: string, ids: string[]): Promise<void>;
|
|
16
16
|
saveBatch(table: string, entries: KeyValueDBTuple[]): Promise<void>;
|
|
17
|
-
streamIds(table: string, limit?: number):
|
|
18
|
-
streamValues(table: string, limit?: number):
|
|
19
|
-
streamEntries(table: string, limit?: number):
|
|
17
|
+
streamIds(table: string, limit?: number): Pipeline<string>;
|
|
18
|
+
streamValues(table: string, limit?: number): Pipeline<Buffer>;
|
|
19
|
+
streamEntries(table: string, limit?: number): Pipeline<KeyValueDBTuple>;
|
|
20
20
|
count(table: string): Promise<number>;
|
|
21
21
|
incrementBatch(_table: string, _entries: IncrementTuple[]): Promise<IncrementTuple[]>;
|
|
22
22
|
}
|
|
@@ -33,10 +33,7 @@ export class DatastoreKeyValueDB {
|
|
|
33
33
|
const q = DBQuery.create(table)
|
|
34
34
|
.select(['id'])
|
|
35
35
|
.limit(limit || 0);
|
|
36
|
-
return
|
|
37
|
-
.streamQuery(q)
|
|
38
|
-
// .on('error', err => stream.emit('error', err))
|
|
39
|
-
.map(r => r.id));
|
|
36
|
+
return this.db.streamQuery(q).mapSync(r => r.id);
|
|
40
37
|
}
|
|
41
38
|
streamValues(table, limit) {
|
|
42
39
|
// `select v` doesn't work for some reason
|
|
@@ -44,14 +41,11 @@ export class DatastoreKeyValueDB {
|
|
|
44
41
|
return (this.db
|
|
45
42
|
.streamQuery(q)
|
|
46
43
|
// .on('error', err => stream.emit('error', err))
|
|
47
|
-
.
|
|
44
|
+
.mapSync(r => r.v));
|
|
48
45
|
}
|
|
49
46
|
streamEntries(table, limit) {
|
|
50
47
|
const q = DBQuery.create(table).limit(limit || 0);
|
|
51
|
-
return
|
|
52
|
-
.streamQuery(q)
|
|
53
|
-
// .on('error', err => stream.emit('error', err))
|
|
54
|
-
.map(r => [r.id, r.v]));
|
|
48
|
+
return this.db.streamQuery(q).mapSync(r => [r.id, r.v]);
|
|
55
49
|
}
|
|
56
50
|
async count(table) {
|
|
57
51
|
const q = DBQuery.create(table);
|
|
@@ -91,7 +91,7 @@ export class DatastoreStreamReadable extends Readable {
|
|
|
91
91
|
}
|
|
92
92
|
void this.runNextQuery().catch(err => {
|
|
93
93
|
this.logger.error('error in runNextQuery', err);
|
|
94
|
-
this.
|
|
94
|
+
this.destroy(err);
|
|
95
95
|
});
|
|
96
96
|
}
|
|
97
97
|
async runNextQuery() {
|
|
@@ -162,6 +162,7 @@ export class DatastoreStreamReadable extends Readable {
|
|
|
162
162
|
return await q.run(this.dsOpt);
|
|
163
163
|
}, {
|
|
164
164
|
name: `DatastoreStreamReadable.query(${table})`,
|
|
165
|
+
predicate: err => RETRY_ON.some(s => err?.message?.toLowerCase()?.includes(s)),
|
|
165
166
|
maxAttempts: 5,
|
|
166
167
|
delay: 5000,
|
|
167
168
|
delayMultiplier: 2,
|
|
@@ -174,9 +175,21 @@ export class DatastoreStreamReadable extends Readable {
|
|
|
174
175
|
table,
|
|
175
176
|
rowsRetrieved: this.rowsRetrieved,
|
|
176
177
|
}, err);
|
|
177
|
-
this.emit('error', err);
|
|
178
178
|
clearInterval(this.maxWaitInterval);
|
|
179
|
+
this.destroy(err);
|
|
179
180
|
return;
|
|
180
181
|
}
|
|
181
182
|
}
|
|
182
183
|
}
|
|
184
|
+
// Examples of errors:
|
|
185
|
+
// UNKNOWN: Stream removed
|
|
186
|
+
const RETRY_ON = [
|
|
187
|
+
'GOAWAY',
|
|
188
|
+
'UNAVAILABLE',
|
|
189
|
+
'UNKNOWN',
|
|
190
|
+
'DEADLINE_EXCEEDED',
|
|
191
|
+
'ABORTED',
|
|
192
|
+
'much contention',
|
|
193
|
+
'try again',
|
|
194
|
+
'timeout',
|
|
195
|
+
].map(s => s.toLowerCase());
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@naturalcycles/datastore-lib",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "4.
|
|
4
|
+
"version": "4.16.0",
|
|
5
5
|
"description": "Opinionated library to work with Google Datastore, implements CommonDB",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@google-cloud/datastore": "^10",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"@types/node": "^24",
|
|
14
|
-
"@naturalcycles/dev-lib": "
|
|
14
|
+
"@naturalcycles/dev-lib": "19.37.0"
|
|
15
15
|
},
|
|
16
16
|
"exports": {
|
|
17
17
|
".": "./dist/index.js"
|
package/src/datastore.db.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { Transform } from 'node:stream'
|
|
2
1
|
import type { Datastore, Key, PropertyFilter, Query, Transaction } from '@google-cloud/datastore'
|
|
3
2
|
import type { RunQueryOptions } from '@google-cloud/datastore/build/src/query.js'
|
|
4
3
|
import type {
|
|
@@ -41,7 +40,7 @@ import {
|
|
|
41
40
|
type StringMap,
|
|
42
41
|
} from '@naturalcycles/js-lib/types'
|
|
43
42
|
import { boldWhite } from '@naturalcycles/nodejs-lib/colors'
|
|
44
|
-
import
|
|
43
|
+
import { Pipeline } from '@naturalcycles/nodejs-lib/stream'
|
|
45
44
|
import type {
|
|
46
45
|
DatastoreDBCfg,
|
|
47
46
|
DatastoreDBOptions,
|
|
@@ -327,15 +326,10 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
327
326
|
override streamQuery<ROW extends ObjectWithId>(
|
|
328
327
|
dbQuery: DBQuery<ROW>,
|
|
329
328
|
_opt?: DatastoreDBStreamOptions,
|
|
330
|
-
):
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
transform: (chunk, _, cb) => {
|
|
334
|
-
cb(null, this.mapId(chunk))
|
|
335
|
-
},
|
|
336
|
-
})
|
|
329
|
+
): Pipeline<ROW> {
|
|
330
|
+
return Pipeline.fromAsyncReadable<ROW>(async () => {
|
|
331
|
+
const ds = await this.ds()
|
|
337
332
|
|
|
338
|
-
void this.ds().then(async ds => {
|
|
339
333
|
const q = dbQueryToDatastoreQuery(
|
|
340
334
|
dbQuery,
|
|
341
335
|
ds.createQuery(dbQuery.table),
|
|
@@ -348,15 +342,10 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
348
342
|
..._opt,
|
|
349
343
|
}
|
|
350
344
|
|
|
351
|
-
|
|
345
|
+
return opt.experimentalCursorStream
|
|
352
346
|
? new DatastoreStreamReadable<ROW>(q, opt)
|
|
353
|
-
:
|
|
354
|
-
|
|
355
|
-
.on('error', err => transform.emit('error', err))
|
|
356
|
-
.pipe(transform)
|
|
357
|
-
})
|
|
358
|
-
|
|
359
|
-
return transform
|
|
347
|
+
: ds.runQueryStream(q, this.getRunQueryOptions(opt))
|
|
348
|
+
}).mapSync(r => this.mapId<ROW>(r))
|
|
360
349
|
}
|
|
361
350
|
|
|
362
351
|
// https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
|
|
@@ -3,7 +3,7 @@ import type { CommonKeyValueDB, IncrementTuple, KeyValueDBTuple } from '@natural
|
|
|
3
3
|
import { commonKeyValueDBFullSupport } from '@naturalcycles/db-lib/kv'
|
|
4
4
|
import { AppError } from '@naturalcycles/js-lib/error/error.util.js'
|
|
5
5
|
import type { ObjectWithId } from '@naturalcycles/js-lib/types'
|
|
6
|
-
import type {
|
|
6
|
+
import type { Pipeline } from '@naturalcycles/nodejs-lib/stream'
|
|
7
7
|
import { DatastoreDB } from './datastore.db.js'
|
|
8
8
|
import type { DatastoreDBCfg } from './datastore.model.js'
|
|
9
9
|
|
|
@@ -52,20 +52,15 @@ export class DatastoreKeyValueDB implements CommonKeyValueDB {
|
|
|
52
52
|
)
|
|
53
53
|
}
|
|
54
54
|
|
|
55
|
-
streamIds(table: string, limit?: number):
|
|
55
|
+
streamIds(table: string, limit?: number): Pipeline<string> {
|
|
56
56
|
const q = DBQuery.create<ObjectWithId>(table)
|
|
57
57
|
.select(['id'])
|
|
58
58
|
.limit(limit || 0)
|
|
59
59
|
|
|
60
|
-
return (
|
|
61
|
-
this.db
|
|
62
|
-
.streamQuery(q)
|
|
63
|
-
// .on('error', err => stream.emit('error', err))
|
|
64
|
-
.map(r => r.id)
|
|
65
|
-
)
|
|
60
|
+
return this.db.streamQuery(q).mapSync(r => r.id)
|
|
66
61
|
}
|
|
67
62
|
|
|
68
|
-
streamValues(table: string, limit?: number):
|
|
63
|
+
streamValues(table: string, limit?: number): Pipeline<Buffer> {
|
|
69
64
|
// `select v` doesn't work for some reason
|
|
70
65
|
const q = DBQuery.create<KVObject>(table).limit(limit || 0)
|
|
71
66
|
|
|
@@ -73,19 +68,14 @@ export class DatastoreKeyValueDB implements CommonKeyValueDB {
|
|
|
73
68
|
this.db
|
|
74
69
|
.streamQuery(q)
|
|
75
70
|
// .on('error', err => stream.emit('error', err))
|
|
76
|
-
.
|
|
71
|
+
.mapSync(r => r.v)
|
|
77
72
|
)
|
|
78
73
|
}
|
|
79
74
|
|
|
80
|
-
streamEntries(table: string, limit?: number):
|
|
75
|
+
streamEntries(table: string, limit?: number): Pipeline<KeyValueDBTuple> {
|
|
81
76
|
const q = DBQuery.create<KVObject>(table).limit(limit || 0)
|
|
82
77
|
|
|
83
|
-
return (
|
|
84
|
-
this.db
|
|
85
|
-
.streamQuery(q)
|
|
86
|
-
// .on('error', err => stream.emit('error', err))
|
|
87
|
-
.map(r => [r.id, r.v])
|
|
88
|
-
)
|
|
78
|
+
return this.db.streamQuery(q).mapSync(r => [r.id, r.v])
|
|
89
79
|
}
|
|
90
80
|
|
|
91
81
|
async count(table: string): Promise<number> {
|
|
@@ -125,7 +125,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
125
125
|
|
|
126
126
|
void this.runNextQuery().catch(err => {
|
|
127
127
|
this.logger.error('error in runNextQuery', err)
|
|
128
|
-
this.
|
|
128
|
+
this.destroy(err)
|
|
129
129
|
})
|
|
130
130
|
}
|
|
131
131
|
|
|
@@ -217,6 +217,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
217
217
|
},
|
|
218
218
|
{
|
|
219
219
|
name: `DatastoreStreamReadable.query(${table})`,
|
|
220
|
+
predicate: err => RETRY_ON.some(s => err?.message?.toLowerCase()?.includes(s)),
|
|
220
221
|
maxAttempts: 5,
|
|
221
222
|
delay: 5000,
|
|
222
223
|
delayMultiplier: 2,
|
|
@@ -233,9 +234,22 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
233
234
|
},
|
|
234
235
|
err,
|
|
235
236
|
)
|
|
236
|
-
this.emit('error', err)
|
|
237
237
|
clearInterval(this.maxWaitInterval)
|
|
238
|
+
this.destroy(err as Error)
|
|
238
239
|
return
|
|
239
240
|
}
|
|
240
241
|
}
|
|
241
242
|
}
|
|
243
|
+
|
|
244
|
+
// Examples of errors:
|
|
245
|
+
// UNKNOWN: Stream removed
|
|
246
|
+
const RETRY_ON = [
|
|
247
|
+
'GOAWAY',
|
|
248
|
+
'UNAVAILABLE',
|
|
249
|
+
'UNKNOWN',
|
|
250
|
+
'DEADLINE_EXCEEDED',
|
|
251
|
+
'ABORTED',
|
|
252
|
+
'much contention',
|
|
253
|
+
'try again',
|
|
254
|
+
'timeout',
|
|
255
|
+
].map(s => s.toLowerCase())
|