@naturalcycles/datastore-lib 3.24.1 → 3.25.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/DatastoreStreamReadable.d.ts +2 -1
- package/dist/DatastoreStreamReadable.js +48 -31
- package/dist/datastore.db.js +6 -3
- package/dist/datastoreKeyValueDB.js +15 -3
- package/package.json +1 -1
- package/src/DatastoreStreamReadable.ts +67 -40
- package/src/datastore.db.ts +13 -9
- package/src/datastoreKeyValueDB.ts +30 -15
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
|
-
import { Readable } from 'stream';
|
|
2
|
+
import { Readable } from 'node:stream';
|
|
3
3
|
import { Query } from '@google-cloud/datastore';
|
|
4
4
|
import { CommonLogger } from '@naturalcycles/js-lib';
|
|
5
5
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
|
|
@@ -14,6 +14,7 @@ export declare class DatastoreStreamReadable<T = any> extends Readable implement
|
|
|
14
14
|
private done;
|
|
15
15
|
private lastQueryDone?;
|
|
16
16
|
private totalWait;
|
|
17
|
+
private table;
|
|
17
18
|
private opt;
|
|
18
19
|
constructor(q: Query, opt: DatastoreDBStreamOptions, logger: CommonLogger);
|
|
19
20
|
private runNextQuery;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DatastoreStreamReadable = void 0;
|
|
4
|
-
const
|
|
4
|
+
const node_stream_1 = require("node:stream");
|
|
5
5
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
6
|
-
class DatastoreStreamReadable extends
|
|
6
|
+
class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
7
7
|
constructor(q, opt, logger) {
|
|
8
8
|
super({ objectMode: true });
|
|
9
9
|
this.q = q;
|
|
@@ -19,7 +19,8 @@ class DatastoreStreamReadable extends stream_1.Readable {
|
|
|
19
19
|
...opt,
|
|
20
20
|
};
|
|
21
21
|
this.originalLimit = q.limitVal;
|
|
22
|
-
|
|
22
|
+
this.table = q.kinds[0];
|
|
23
|
+
logger.log(`!! using experimentalCursorStream !! ${this.table}, batchSize: ${opt.batchSize}`);
|
|
23
24
|
}
|
|
24
25
|
async runNextQuery() {
|
|
25
26
|
if (this.done)
|
|
@@ -39,38 +40,54 @@ class DatastoreStreamReadable extends stream_1.Readable {
|
|
|
39
40
|
if (this.endCursor) {
|
|
40
41
|
q = q.start(this.endCursor);
|
|
41
42
|
}
|
|
43
|
+
let rows = [];
|
|
44
|
+
let info = {};
|
|
42
45
|
try {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
this.push(null);
|
|
55
|
-
this.done = true;
|
|
56
|
-
}
|
|
57
|
-
else if (this.opt.singleBatchBuffer) {
|
|
58
|
-
// here we don't start next query until we're asked (via next _read call)
|
|
59
|
-
// do, let's do nothing
|
|
60
|
-
}
|
|
61
|
-
else if (this.opt.rssLimitMB) {
|
|
62
|
-
const rssMB = Math.round(process.memoryUsage().rss / 1024 / 1024);
|
|
63
|
-
if (rssMB <= this.opt.rssLimitMB) {
|
|
64
|
-
void this.runNextQuery();
|
|
65
|
-
}
|
|
66
|
-
else {
|
|
67
|
-
this.logger.log(`rssLimitMB reached ${rssMB} > ${this.opt.rssLimitMB}, pausing stream`);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
46
|
+
await (0, js_lib_1.pRetry)(async () => {
|
|
47
|
+
const res = await q.run();
|
|
48
|
+
rows = res[0];
|
|
49
|
+
info = res[1];
|
|
50
|
+
}, {
|
|
51
|
+
name: `DatastoreStreamReadable.query(${this.table})`,
|
|
52
|
+
maxAttempts: 5,
|
|
53
|
+
delay: 5000,
|
|
54
|
+
delayMultiplier: 2,
|
|
55
|
+
logger: this.logger,
|
|
56
|
+
});
|
|
70
57
|
}
|
|
71
58
|
catch (err) {
|
|
72
|
-
console.error(
|
|
59
|
+
console.error(`DatastoreStreamReadable error!\n`, {
|
|
60
|
+
table: this.table,
|
|
61
|
+
rowsRetrieved: this.rowsRetrieved,
|
|
62
|
+
}, err);
|
|
73
63
|
this.emit('error', err);
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
this.rowsRetrieved += rows.length;
|
|
67
|
+
this.logger.log(`got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`, info.moreResults);
|
|
68
|
+
this.endCursor = info.endCursor;
|
|
69
|
+
this.running = false; // ready to take more _reads
|
|
70
|
+
this.lastQueryDone = Date.now();
|
|
71
|
+
rows.forEach(row => this.push(row));
|
|
72
|
+
if (!info.endCursor ||
|
|
73
|
+
info.moreResults === 'NO_MORE_RESULTS' ||
|
|
74
|
+
(this.originalLimit && this.rowsRetrieved >= this.originalLimit)) {
|
|
75
|
+
this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`);
|
|
76
|
+
this.push(null);
|
|
77
|
+
this.done = true;
|
|
78
|
+
}
|
|
79
|
+
else if (this.opt.singleBatchBuffer) {
|
|
80
|
+
// here we don't start next query until we're asked (via next _read call)
|
|
81
|
+
// do, let's do nothing
|
|
82
|
+
}
|
|
83
|
+
else if (this.opt.rssLimitMB) {
|
|
84
|
+
const rssMB = Math.round(process.memoryUsage().rss / 1024 / 1024);
|
|
85
|
+
if (rssMB <= this.opt.rssLimitMB) {
|
|
86
|
+
void this.runNextQuery();
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
this.logger.log(`rssLimitMB reached ${rssMB} > ${this.opt.rssLimitMB}, pausing stream`);
|
|
90
|
+
}
|
|
74
91
|
}
|
|
75
92
|
}
|
|
76
93
|
_read() {
|
package/dist/datastore.db.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DatastoreDB = void 0;
|
|
4
|
-
const
|
|
4
|
+
const node_stream_1 = require("node:stream");
|
|
5
5
|
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
6
6
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
7
7
|
const colors_1 = require("@naturalcycles/nodejs-lib/dist/colors");
|
|
@@ -133,14 +133,17 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
|
|
|
133
133
|
...this.cfg.streamOptions,
|
|
134
134
|
..._opt,
|
|
135
135
|
};
|
|
136
|
-
|
|
136
|
+
const stream = (opt.experimentalCursorStream
|
|
137
137
|
? new DatastoreStreamReadable_1.DatastoreStreamReadable(q, opt, (0, js_lib_1.commonLoggerMinLevel)(this.cfg.logger, opt.debug ? 'log' : 'warn'))
|
|
138
|
-
: this.ds().runQueryStream(q))
|
|
138
|
+
: this.ds().runQueryStream(q))
|
|
139
|
+
.on('error', err => stream.emit('error', err))
|
|
140
|
+
.pipe(new node_stream_1.Transform({
|
|
139
141
|
objectMode: true,
|
|
140
142
|
transform: (chunk, _enc, cb) => {
|
|
141
143
|
cb(null, this.mapId(chunk));
|
|
142
144
|
},
|
|
143
145
|
}));
|
|
146
|
+
return stream;
|
|
144
147
|
}
|
|
145
148
|
streamQuery(dbQuery, opt) {
|
|
146
149
|
const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table));
|
|
@@ -30,22 +30,34 @@ class DatastoreKeyValueDB {
|
|
|
30
30
|
const q = db_lib_1.DBQuery.create(table)
|
|
31
31
|
.select(['id'])
|
|
32
32
|
.limit(limit || 0);
|
|
33
|
-
|
|
33
|
+
const stream = this.db
|
|
34
|
+
.streamQuery(q)
|
|
35
|
+
.on('error', err => stream.emit('error', err))
|
|
36
|
+
.pipe((0, nodejs_lib_1.transformMapSimple)(objectWithId => objectWithId.id, {
|
|
34
37
|
errorMode: js_lib_1.ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
35
38
|
}));
|
|
39
|
+
return stream;
|
|
36
40
|
}
|
|
37
41
|
streamValues(table, limit) {
|
|
38
42
|
// `select v` doesn't work for some reason
|
|
39
43
|
const q = db_lib_1.DBQuery.create(table).limit(limit || 0);
|
|
40
|
-
|
|
44
|
+
const stream = this.db
|
|
45
|
+
.streamQuery(q)
|
|
46
|
+
.on('error', err => stream.emit('error', err))
|
|
47
|
+
.pipe((0, nodejs_lib_1.transformMapSimple)(obj => obj.v, {
|
|
41
48
|
errorMode: js_lib_1.ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
42
49
|
}));
|
|
50
|
+
return stream;
|
|
43
51
|
}
|
|
44
52
|
streamEntries(table, limit) {
|
|
45
53
|
const q = db_lib_1.DBQuery.create(table).limit(limit || 0);
|
|
46
|
-
|
|
54
|
+
const stream = this.db
|
|
55
|
+
.streamQuery(q)
|
|
56
|
+
.on('error', err => stream.emit('error', err))
|
|
57
|
+
.pipe((0, nodejs_lib_1.transformMapSimple)(obj => [obj.id, obj.v], {
|
|
47
58
|
errorMode: js_lib_1.ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
48
59
|
}));
|
|
60
|
+
return stream;
|
|
49
61
|
}
|
|
50
62
|
async count(_table) {
|
|
51
63
|
this.db.cfg.logger.warn(`DatastoreKeyValueDB.count is not supported`);
|
package/package.json
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Readable } from 'stream'
|
|
1
|
+
import { Readable } from 'node:stream'
|
|
2
|
+
import type { RunQueryInfo } from '@google-cloud/datastore/build/src/query'
|
|
2
3
|
import { Query } from '@google-cloud/datastore'
|
|
3
|
-
import { _ms, CommonLogger } from '@naturalcycles/js-lib'
|
|
4
|
+
import { _ms, CommonLogger, pRetry } from '@naturalcycles/js-lib'
|
|
4
5
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
5
6
|
import type { DatastoreDBStreamOptions } from './datastore.model'
|
|
6
7
|
|
|
@@ -12,6 +13,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
12
13
|
private done = false
|
|
13
14
|
private lastQueryDone?: number
|
|
14
15
|
private totalWait = 0
|
|
16
|
+
private table: string
|
|
15
17
|
|
|
16
18
|
private opt: DatastoreDBStreamOptions & { batchSize: number }
|
|
17
19
|
|
|
@@ -25,8 +27,9 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
this.originalLimit = q.limitVal
|
|
30
|
+
this.table = q.kinds[0]!
|
|
28
31
|
|
|
29
|
-
logger.log(`!! using experimentalCursorStream !! batchSize: ${opt.batchSize}`)
|
|
32
|
+
logger.log(`!! using experimentalCursorStream !! ${this.table}, batchSize: ${opt.batchSize}`)
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
private async runNextQuery(): Promise<void> {
|
|
@@ -52,48 +55,72 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
52
55
|
q = q.start(this.endCursor)
|
|
53
56
|
}
|
|
54
57
|
|
|
58
|
+
let rows: T[] = []
|
|
59
|
+
let info: RunQueryInfo = {}
|
|
60
|
+
|
|
55
61
|
try {
|
|
56
|
-
|
|
62
|
+
await pRetry(
|
|
63
|
+
async () => {
|
|
64
|
+
const res = await q.run()
|
|
65
|
+
rows = res[0]
|
|
66
|
+
info = res[1]
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
name: `DatastoreStreamReadable.query(${this.table})`,
|
|
70
|
+
maxAttempts: 5,
|
|
71
|
+
delay: 5000,
|
|
72
|
+
delayMultiplier: 2,
|
|
73
|
+
logger: this.logger,
|
|
74
|
+
},
|
|
75
|
+
)
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.error(
|
|
78
|
+
`DatastoreStreamReadable error!\n`,
|
|
79
|
+
{
|
|
80
|
+
table: this.table,
|
|
81
|
+
rowsRetrieved: this.rowsRetrieved,
|
|
82
|
+
},
|
|
83
|
+
err,
|
|
84
|
+
)
|
|
85
|
+
this.emit('error', err)
|
|
86
|
+
return
|
|
87
|
+
}
|
|
57
88
|
|
|
58
|
-
|
|
89
|
+
this.rowsRetrieved += rows.length
|
|
90
|
+
this.logger.log(
|
|
91
|
+
`got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(
|
|
92
|
+
this.totalWait,
|
|
93
|
+
)}`,
|
|
94
|
+
info.moreResults,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
this.endCursor = info.endCursor
|
|
98
|
+
this.running = false // ready to take more _reads
|
|
99
|
+
this.lastQueryDone = Date.now()
|
|
100
|
+
|
|
101
|
+
rows.forEach(row => this.push(row))
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
!info.endCursor ||
|
|
105
|
+
info.moreResults === 'NO_MORE_RESULTS' ||
|
|
106
|
+
(this.originalLimit && this.rowsRetrieved >= this.originalLimit)
|
|
107
|
+
) {
|
|
59
108
|
this.logger.log(
|
|
60
|
-
|
|
61
|
-
this.totalWait,
|
|
62
|
-
)}`,
|
|
63
|
-
info.moreResults,
|
|
109
|
+
`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`,
|
|
64
110
|
)
|
|
65
|
-
|
|
66
|
-
this.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
this.logger.log(
|
|
78
|
-
`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`,
|
|
79
|
-
)
|
|
80
|
-
this.push(null)
|
|
81
|
-
this.done = true
|
|
82
|
-
} else if (this.opt.singleBatchBuffer) {
|
|
83
|
-
// here we don't start next query until we're asked (via next _read call)
|
|
84
|
-
// do, let's do nothing
|
|
85
|
-
} else if (this.opt.rssLimitMB) {
|
|
86
|
-
const rssMB = Math.round(process.memoryUsage().rss / 1024 / 1024)
|
|
87
|
-
|
|
88
|
-
if (rssMB <= this.opt.rssLimitMB) {
|
|
89
|
-
void this.runNextQuery()
|
|
90
|
-
} else {
|
|
91
|
-
this.logger.log(`rssLimitMB reached ${rssMB} > ${this.opt.rssLimitMB}, pausing stream`)
|
|
92
|
-
}
|
|
111
|
+
this.push(null)
|
|
112
|
+
this.done = true
|
|
113
|
+
} else if (this.opt.singleBatchBuffer) {
|
|
114
|
+
// here we don't start next query until we're asked (via next _read call)
|
|
115
|
+
// do, let's do nothing
|
|
116
|
+
} else if (this.opt.rssLimitMB) {
|
|
117
|
+
const rssMB = Math.round(process.memoryUsage().rss / 1024 / 1024)
|
|
118
|
+
|
|
119
|
+
if (rssMB <= this.opt.rssLimitMB) {
|
|
120
|
+
void this.runNextQuery()
|
|
121
|
+
} else {
|
|
122
|
+
this.logger.log(`rssLimitMB reached ${rssMB} > ${this.opt.rssLimitMB}, pausing stream`)
|
|
93
123
|
}
|
|
94
|
-
} catch (err) {
|
|
95
|
-
console.error('DatastoreStreamReadable error!\n', err)
|
|
96
|
-
this.emit('error', err)
|
|
97
124
|
}
|
|
98
125
|
}
|
|
99
126
|
|
package/src/datastore.db.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Transform } from 'stream'
|
|
1
|
+
import { Transform } from 'node:stream'
|
|
2
2
|
import type { Datastore, Key, Query } from '@google-cloud/datastore'
|
|
3
3
|
import {
|
|
4
4
|
BaseCommonDB,
|
|
@@ -216,7 +216,7 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
216
216
|
..._opt,
|
|
217
217
|
}
|
|
218
218
|
|
|
219
|
-
|
|
219
|
+
const stream: ReadableTyped<ROW> = (
|
|
220
220
|
opt.experimentalCursorStream
|
|
221
221
|
? new DatastoreStreamReadable(
|
|
222
222
|
q,
|
|
@@ -224,14 +224,18 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
|
|
|
224
224
|
commonLoggerMinLevel(this.cfg.logger, opt.debug ? 'log' : 'warn'),
|
|
225
225
|
)
|
|
226
226
|
: this.ds().runQueryStream(q)
|
|
227
|
-
).pipe(
|
|
228
|
-
new Transform({
|
|
229
|
-
objectMode: true,
|
|
230
|
-
transform: (chunk, _enc, cb) => {
|
|
231
|
-
cb(null, this.mapId(chunk))
|
|
232
|
-
},
|
|
233
|
-
}),
|
|
234
227
|
)
|
|
228
|
+
.on('error', err => stream.emit('error', err))
|
|
229
|
+
.pipe(
|
|
230
|
+
new Transform({
|
|
231
|
+
objectMode: true,
|
|
232
|
+
transform: (chunk, _enc, cb) => {
|
|
233
|
+
cb(null, this.mapId(chunk))
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
return stream
|
|
235
239
|
}
|
|
236
240
|
|
|
237
241
|
override streamQuery<ROW extends ObjectWithId>(
|
|
@@ -49,32 +49,47 @@ export class DatastoreKeyValueDB implements CommonKeyValueDB {
|
|
|
49
49
|
.select(['id'])
|
|
50
50
|
.limit(limit || 0)
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
52
|
+
const stream: ReadableTyped<string> = this.db
|
|
53
|
+
.streamQuery<KVObject>(q)
|
|
54
|
+
.on('error', err => stream.emit('error', err))
|
|
55
|
+
.pipe(
|
|
56
|
+
transformMapSimple<ObjectWithId<string>, string>(objectWithId => objectWithId.id, {
|
|
57
|
+
errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
58
|
+
}),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
return stream
|
|
57
62
|
}
|
|
58
63
|
|
|
59
64
|
streamValues(table: string, limit?: number): ReadableTyped<Buffer> {
|
|
60
65
|
// `select v` doesn't work for some reason
|
|
61
66
|
const q = DBQuery.create(table).limit(limit || 0)
|
|
62
67
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
+
const stream: ReadableTyped<string> = this.db
|
|
69
|
+
.streamQuery<KVObject>(q)
|
|
70
|
+
.on('error', err => stream.emit('error', err))
|
|
71
|
+
.pipe(
|
|
72
|
+
transformMapSimple<{ v: Buffer }, Buffer>(obj => obj.v, {
|
|
73
|
+
errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
74
|
+
}),
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return stream
|
|
68
78
|
}
|
|
69
79
|
|
|
70
80
|
streamEntries(table: string, limit?: number): ReadableTyped<KeyValueDBTuple> {
|
|
71
81
|
const q = DBQuery.create(table).limit(limit || 0)
|
|
72
82
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
83
|
+
const stream: ReadableTyped<string> = this.db
|
|
84
|
+
.streamQuery<KVObject>(q)
|
|
85
|
+
.on('error', err => stream.emit('error', err))
|
|
86
|
+
.pipe(
|
|
87
|
+
transformMapSimple<KVObject, KeyValueDBTuple>(obj => [obj.id, obj.v], {
|
|
88
|
+
errorMode: ErrorMode.SUPPRESS, // cause .pipe() cannot propagate errors
|
|
89
|
+
}),
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return stream
|
|
78
93
|
}
|
|
79
94
|
|
|
80
95
|
async count(_table: string): Promise<number> {
|