@naturalcycles/datastore-lib 3.28.3 → 3.29.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 +9 -0
- package/dist/DatastoreStreamReadable.js +30 -4
- package/dist/datastore.db.js +1 -1
- package/dist/datastore.model.d.ts +9 -1
- package/package.json +1 -1
- package/src/DatastoreStreamReadable.ts +42 -6
- package/src/datastore.db.ts +2 -3
- package/src/datastore.model.ts +10 -1
|
@@ -15,9 +15,18 @@ export declare class DatastoreStreamReadable<T = any> extends Readable implement
|
|
|
15
15
|
private lastQueryDone?;
|
|
16
16
|
private totalWait;
|
|
17
17
|
private table;
|
|
18
|
+
/**
|
|
19
|
+
* Used to support maxWait
|
|
20
|
+
*/
|
|
21
|
+
private lastReadTimestamp;
|
|
22
|
+
private maxWaitInterval;
|
|
18
23
|
private opt;
|
|
19
24
|
constructor(q: Query, opt: DatastoreDBStreamOptions, logger: CommonLogger);
|
|
20
25
|
private runNextQuery;
|
|
26
|
+
/**
|
|
27
|
+
* Counts how many times _read was called.
|
|
28
|
+
* For debugging.
|
|
29
|
+
*/
|
|
21
30
|
count: number;
|
|
22
31
|
_read(): void;
|
|
23
32
|
}
|
|
@@ -12,12 +12,35 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
12
12
|
this.running = false;
|
|
13
13
|
this.done = false;
|
|
14
14
|
this.totalWait = 0;
|
|
15
|
-
|
|
15
|
+
/**
|
|
16
|
+
* Used to support maxWait
|
|
17
|
+
*/
|
|
18
|
+
this.lastReadTimestamp = 0;
|
|
19
|
+
/**
|
|
20
|
+
* Counts how many times _read was called.
|
|
21
|
+
* For debugging.
|
|
22
|
+
*/
|
|
23
|
+
this.count = 0;
|
|
16
24
|
this.opt = {
|
|
17
25
|
rssLimitMB: 1000,
|
|
18
26
|
batchSize: 1000,
|
|
19
27
|
...opt,
|
|
20
28
|
};
|
|
29
|
+
if (this.opt.maxWait) {
|
|
30
|
+
this.maxWaitInterval = setInterval(() => {
|
|
31
|
+
if (Date.now() - this.lastReadTimestamp < this.opt.maxWait * 1000) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const { running, rowsRetrieved } = this;
|
|
35
|
+
this.logger.warn(`maxWait of ${this.opt.maxWait} seconds reached, force-triggering _read`, {
|
|
36
|
+
running,
|
|
37
|
+
rowsRetrieved,
|
|
38
|
+
});
|
|
39
|
+
// force-trigger _read
|
|
40
|
+
// regardless of `running` status
|
|
41
|
+
this._read();
|
|
42
|
+
}, (this.opt.maxWait * 1000) / 2);
|
|
43
|
+
}
|
|
21
44
|
this.originalLimit = q.limitVal;
|
|
22
45
|
this.table = q.kinds[0];
|
|
23
46
|
logger.log(`!! using experimentalCursorStream !! ${this.table}, batchSize: ${opt.batchSize}`);
|
|
@@ -53,6 +76,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
53
76
|
delay: 5000,
|
|
54
77
|
delayMultiplier: 2,
|
|
55
78
|
logger: this.logger,
|
|
79
|
+
timeout: 120000, // 2 minutes
|
|
56
80
|
});
|
|
57
81
|
}
|
|
58
82
|
catch (err) {
|
|
@@ -75,6 +99,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
75
99
|
this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`);
|
|
76
100
|
this.push(null);
|
|
77
101
|
this.done = true;
|
|
102
|
+
clearInterval(this.maxWaitInterval);
|
|
78
103
|
}
|
|
79
104
|
else if (this.opt.singleBatchBuffer) {
|
|
80
105
|
// here we don't start next query until we're asked (via next _read call)
|
|
@@ -91,11 +116,9 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
91
116
|
}
|
|
92
117
|
}
|
|
93
118
|
_read() {
|
|
119
|
+
this.lastReadTimestamp = Date.now();
|
|
94
120
|
// console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
|
|
95
121
|
this.count++;
|
|
96
|
-
if (this.running) {
|
|
97
|
-
this.logger.log(`_read ${this.count}, wasRunning: true`);
|
|
98
|
-
}
|
|
99
122
|
if (this.done) {
|
|
100
123
|
this.logger.warn(`!!! _read was called, but done==true`);
|
|
101
124
|
return;
|
|
@@ -103,6 +126,9 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
103
126
|
if (!this.running) {
|
|
104
127
|
void this.runNextQuery();
|
|
105
128
|
}
|
|
129
|
+
else {
|
|
130
|
+
this.logger.log(`_read ${this.count}, wasRunning: true`);
|
|
131
|
+
}
|
|
106
132
|
}
|
|
107
133
|
}
|
|
108
134
|
exports.DatastoreStreamReadable = DatastoreStreamReadable;
|
package/dist/datastore.db.js
CHANGED
|
@@ -6,8 +6,8 @@ const datastore_1 = require("@google-cloud/datastore");
|
|
|
6
6
|
const db_lib_1 = require("@naturalcycles/db-lib");
|
|
7
7
|
const js_lib_1 = require("@naturalcycles/js-lib");
|
|
8
8
|
const nodejs_lib_1 = require("@naturalcycles/nodejs-lib");
|
|
9
|
-
const datastore_model_1 = require("./datastore.model");
|
|
10
9
|
const DatastoreStreamReadable_1 = require("./DatastoreStreamReadable");
|
|
10
|
+
const datastore_model_1 = require("./datastore.model");
|
|
11
11
|
const query_util_1 = require("./query.util");
|
|
12
12
|
// Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
|
|
13
13
|
const MAX_ITEMS = 500;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DatastoreOptions, Key, Transaction } from '@google-cloud/datastore';
|
|
2
2
|
import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
|
|
3
|
-
import { CommonLogger, ObjectWithId } from '@naturalcycles/js-lib';
|
|
3
|
+
import { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib';
|
|
4
4
|
export interface DatastorePayload<T = any> {
|
|
5
5
|
key: Key;
|
|
6
6
|
data: T;
|
|
@@ -90,6 +90,14 @@ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
|
|
|
90
90
|
* @default false
|
|
91
91
|
*/
|
|
92
92
|
debug?: boolean;
|
|
93
|
+
/**
|
|
94
|
+
* Default is undefined.
|
|
95
|
+
* If set - sets a "safety timer", which will force call _read after the specified number of seconds.
|
|
96
|
+
* This is to prevent possible "dead-lock"/race-condition that would make the stream "hang".
|
|
97
|
+
*
|
|
98
|
+
* @experimental
|
|
99
|
+
*/
|
|
100
|
+
maxWait?: NumberOfSeconds;
|
|
93
101
|
}
|
|
94
102
|
export interface DatastoreDBOptions extends CommonDBOptions {
|
|
95
103
|
tx?: Transaction;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
|
-
import type { RunQueryInfo } from '@google-cloud/datastore/build/src/query'
|
|
3
2
|
import { Query } from '@google-cloud/datastore'
|
|
4
|
-
import {
|
|
3
|
+
import type { RunQueryInfo } from '@google-cloud/datastore/build/src/query'
|
|
4
|
+
import { _ms, CommonLogger, pRetry, UnixTimestampMillisNumber } from '@naturalcycles/js-lib'
|
|
5
5
|
import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
6
6
|
import type { DatastoreDBStreamOptions } from './datastore.model'
|
|
7
7
|
|
|
@@ -14,6 +14,11 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
14
14
|
private lastQueryDone?: number
|
|
15
15
|
private totalWait = 0
|
|
16
16
|
private table: string
|
|
17
|
+
/**
|
|
18
|
+
* Used to support maxWait
|
|
19
|
+
*/
|
|
20
|
+
private lastReadTimestamp: UnixTimestampMillisNumber = 0
|
|
21
|
+
private maxWaitInterval: NodeJS.Timeout | undefined
|
|
17
22
|
|
|
18
23
|
private opt: DatastoreDBStreamOptions & { batchSize: number }
|
|
19
24
|
|
|
@@ -30,6 +35,30 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
30
35
|
...opt,
|
|
31
36
|
}
|
|
32
37
|
|
|
38
|
+
if (this.opt.maxWait) {
|
|
39
|
+
this.maxWaitInterval = setInterval(
|
|
40
|
+
() => {
|
|
41
|
+
if (Date.now() - this.lastReadTimestamp < this.opt.maxWait! * 1000) {
|
|
42
|
+
return
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const { running, rowsRetrieved } = this
|
|
46
|
+
this.logger.warn(
|
|
47
|
+
`maxWait of ${this.opt.maxWait} seconds reached, force-triggering _read`,
|
|
48
|
+
{
|
|
49
|
+
running,
|
|
50
|
+
rowsRetrieved,
|
|
51
|
+
},
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
// force-trigger _read
|
|
55
|
+
// regardless of `running` status
|
|
56
|
+
this._read()
|
|
57
|
+
},
|
|
58
|
+
(this.opt.maxWait * 1000) / 2,
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
33
62
|
this.originalLimit = q.limitVal
|
|
34
63
|
this.table = q.kinds[0]!
|
|
35
64
|
|
|
@@ -75,6 +104,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
75
104
|
delay: 5000,
|
|
76
105
|
delayMultiplier: 2,
|
|
77
106
|
logger: this.logger,
|
|
107
|
+
timeout: 120_000, // 2 minutes
|
|
78
108
|
},
|
|
79
109
|
)
|
|
80
110
|
} catch (err) {
|
|
@@ -114,6 +144,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
114
144
|
)
|
|
115
145
|
this.push(null)
|
|
116
146
|
this.done = true
|
|
147
|
+
clearInterval(this.maxWaitInterval)
|
|
117
148
|
} else if (this.opt.singleBatchBuffer) {
|
|
118
149
|
// here we don't start next query until we're asked (via next _read call)
|
|
119
150
|
// do, let's do nothing
|
|
@@ -128,14 +159,17 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
128
159
|
}
|
|
129
160
|
}
|
|
130
161
|
|
|
131
|
-
|
|
162
|
+
/**
|
|
163
|
+
* Counts how many times _read was called.
|
|
164
|
+
* For debugging.
|
|
165
|
+
*/
|
|
166
|
+
count = 0
|
|
132
167
|
|
|
133
168
|
override _read(): void {
|
|
169
|
+
this.lastReadTimestamp = Date.now()
|
|
170
|
+
|
|
134
171
|
// console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
|
|
135
172
|
this.count++
|
|
136
|
-
if (this.running) {
|
|
137
|
-
this.logger.log(`_read ${this.count}, wasRunning: true`)
|
|
138
|
-
}
|
|
139
173
|
|
|
140
174
|
if (this.done) {
|
|
141
175
|
this.logger.warn(`!!! _read was called, but done==true`)
|
|
@@ -144,6 +178,8 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
144
178
|
|
|
145
179
|
if (!this.running) {
|
|
146
180
|
void this.runNextQuery()
|
|
181
|
+
} else {
|
|
182
|
+
this.logger.log(`_read ${this.count}, wasRunning: true`)
|
|
147
183
|
}
|
|
148
184
|
}
|
|
149
185
|
}
|
package/src/datastore.db.ts
CHANGED
|
@@ -30,8 +30,8 @@ import {
|
|
|
30
30
|
pRetry,
|
|
31
31
|
PRetryOptions,
|
|
32
32
|
} from '@naturalcycles/js-lib'
|
|
33
|
-
import { ReadableTyped } from '@naturalcycles/nodejs-lib'
|
|
34
|
-
import {
|
|
33
|
+
import { ReadableTyped, boldWhite } from '@naturalcycles/nodejs-lib'
|
|
34
|
+
import { DatastoreStreamReadable } from './DatastoreStreamReadable'
|
|
35
35
|
import {
|
|
36
36
|
DatastoreDBCfg,
|
|
37
37
|
DatastoreDBOptions,
|
|
@@ -42,7 +42,6 @@ import {
|
|
|
42
42
|
DatastoreStats,
|
|
43
43
|
DatastoreType,
|
|
44
44
|
} from './datastore.model'
|
|
45
|
-
import { DatastoreStreamReadable } from './DatastoreStreamReadable'
|
|
46
45
|
import { dbQueryToDatastoreQuery } from './query.util'
|
|
47
46
|
|
|
48
47
|
// Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
|
package/src/datastore.model.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DatastoreOptions, Key, Transaction } from '@google-cloud/datastore'
|
|
2
2
|
import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib'
|
|
3
|
-
import { CommonLogger, ObjectWithId } from '@naturalcycles/js-lib'
|
|
3
|
+
import { CommonLogger, NumberOfSeconds, ObjectWithId } from '@naturalcycles/js-lib'
|
|
4
4
|
|
|
5
5
|
export interface DatastorePayload<T = any> {
|
|
6
6
|
key: Key
|
|
@@ -103,6 +103,15 @@ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
|
|
|
103
103
|
* @default false
|
|
104
104
|
*/
|
|
105
105
|
debug?: boolean
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Default is undefined.
|
|
109
|
+
* If set - sets a "safety timer", which will force call _read after the specified number of seconds.
|
|
110
|
+
* This is to prevent possible "dead-lock"/race-condition that would make the stream "hang".
|
|
111
|
+
*
|
|
112
|
+
* @experimental
|
|
113
|
+
*/
|
|
114
|
+
maxWait?: NumberOfSeconds
|
|
106
115
|
}
|
|
107
116
|
|
|
108
117
|
export interface DatastoreDBOptions extends CommonDBOptions {
|