@naturalcycles/datastore-lib 3.28.3 → 3.30.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 +35 -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 +48 -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,7 +12,15 @@ 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,
|
|
@@ -21,6 +29,25 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
21
29
|
this.originalLimit = q.limitVal;
|
|
22
30
|
this.table = q.kinds[0];
|
|
23
31
|
logger.log(`!! using experimentalCursorStream !! ${this.table}, batchSize: ${opt.batchSize}`);
|
|
32
|
+
const { maxWait } = this.opt;
|
|
33
|
+
if (maxWait) {
|
|
34
|
+
this.logger.warn(`!! ${this.table} maxWait ${maxWait}`);
|
|
35
|
+
this.maxWaitInterval = setInterval(() => {
|
|
36
|
+
const millisSinceLastRead = Date.now() - this.lastReadTimestamp;
|
|
37
|
+
if (millisSinceLastRead < maxWait * 1000) {
|
|
38
|
+
this.logger.warn(`!! ${this.table} millisSinceLastRead(${millisSinceLastRead}) < maxWait*1000`);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
const { running, rowsRetrieved } = this;
|
|
42
|
+
this.logger.warn(`maxWait of ${maxWait} seconds reached, force-triggering _read`, {
|
|
43
|
+
running,
|
|
44
|
+
rowsRetrieved,
|
|
45
|
+
});
|
|
46
|
+
// force-trigger _read
|
|
47
|
+
// regardless of `running` status
|
|
48
|
+
this._read();
|
|
49
|
+
}, (maxWait * 1000) / 2);
|
|
50
|
+
}
|
|
24
51
|
}
|
|
25
52
|
async runNextQuery() {
|
|
26
53
|
if (this.done)
|
|
@@ -53,6 +80,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
53
80
|
delay: 5000,
|
|
54
81
|
delayMultiplier: 2,
|
|
55
82
|
logger: this.logger,
|
|
83
|
+
timeout: 120000, // 2 minutes
|
|
56
84
|
});
|
|
57
85
|
}
|
|
58
86
|
catch (err) {
|
|
@@ -61,6 +89,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
61
89
|
rowsRetrieved: this.rowsRetrieved,
|
|
62
90
|
}, err);
|
|
63
91
|
this.emit('error', err);
|
|
92
|
+
clearInterval(this.maxWaitInterval);
|
|
64
93
|
return;
|
|
65
94
|
}
|
|
66
95
|
this.rowsRetrieved += rows.length;
|
|
@@ -75,6 +104,7 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
75
104
|
this.logger.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`);
|
|
76
105
|
this.push(null);
|
|
77
106
|
this.done = true;
|
|
107
|
+
clearInterval(this.maxWaitInterval);
|
|
78
108
|
}
|
|
79
109
|
else if (this.opt.singleBatchBuffer) {
|
|
80
110
|
// here we don't start next query until we're asked (via next _read call)
|
|
@@ -91,11 +121,9 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
91
121
|
}
|
|
92
122
|
}
|
|
93
123
|
_read() {
|
|
124
|
+
this.lastReadTimestamp = Date.now();
|
|
94
125
|
// console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
|
|
95
126
|
this.count++;
|
|
96
|
-
if (this.running) {
|
|
97
|
-
this.logger.log(`_read ${this.count}, wasRunning: true`);
|
|
98
|
-
}
|
|
99
127
|
if (this.done) {
|
|
100
128
|
this.logger.warn(`!!! _read was called, but done==true`);
|
|
101
129
|
return;
|
|
@@ -103,6 +131,9 @@ class DatastoreStreamReadable extends node_stream_1.Readable {
|
|
|
103
131
|
if (!this.running) {
|
|
104
132
|
void this.runNextQuery();
|
|
105
133
|
}
|
|
134
|
+
else {
|
|
135
|
+
this.logger.log(`_read ${this.count}, wasRunning: true`);
|
|
136
|
+
}
|
|
106
137
|
}
|
|
107
138
|
}
|
|
108
139
|
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
|
|
|
@@ -34,6 +39,35 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
34
39
|
this.table = q.kinds[0]!
|
|
35
40
|
|
|
36
41
|
logger.log(`!! using experimentalCursorStream !! ${this.table}, batchSize: ${opt.batchSize}`)
|
|
42
|
+
|
|
43
|
+
const { maxWait } = this.opt
|
|
44
|
+
if (maxWait) {
|
|
45
|
+
this.logger.warn(`!! ${this.table} maxWait ${maxWait}`)
|
|
46
|
+
|
|
47
|
+
this.maxWaitInterval = setInterval(
|
|
48
|
+
() => {
|
|
49
|
+
const millisSinceLastRead = Date.now() - this.lastReadTimestamp
|
|
50
|
+
|
|
51
|
+
if (millisSinceLastRead < maxWait * 1000) {
|
|
52
|
+
this.logger.warn(
|
|
53
|
+
`!! ${this.table} millisSinceLastRead(${millisSinceLastRead}) < maxWait*1000`,
|
|
54
|
+
)
|
|
55
|
+
return
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const { running, rowsRetrieved } = this
|
|
59
|
+
this.logger.warn(`maxWait of ${maxWait} seconds reached, force-triggering _read`, {
|
|
60
|
+
running,
|
|
61
|
+
rowsRetrieved,
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
// force-trigger _read
|
|
65
|
+
// regardless of `running` status
|
|
66
|
+
this._read()
|
|
67
|
+
},
|
|
68
|
+
(maxWait * 1000) / 2,
|
|
69
|
+
)
|
|
70
|
+
}
|
|
37
71
|
}
|
|
38
72
|
|
|
39
73
|
private async runNextQuery(): Promise<void> {
|
|
@@ -75,6 +109,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
75
109
|
delay: 5000,
|
|
76
110
|
delayMultiplier: 2,
|
|
77
111
|
logger: this.logger,
|
|
112
|
+
timeout: 120_000, // 2 minutes
|
|
78
113
|
},
|
|
79
114
|
)
|
|
80
115
|
} catch (err) {
|
|
@@ -87,6 +122,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
87
122
|
err,
|
|
88
123
|
)
|
|
89
124
|
this.emit('error', err)
|
|
125
|
+
clearInterval(this.maxWaitInterval)
|
|
90
126
|
return
|
|
91
127
|
}
|
|
92
128
|
|
|
@@ -114,6 +150,7 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
114
150
|
)
|
|
115
151
|
this.push(null)
|
|
116
152
|
this.done = true
|
|
153
|
+
clearInterval(this.maxWaitInterval)
|
|
117
154
|
} else if (this.opt.singleBatchBuffer) {
|
|
118
155
|
// here we don't start next query until we're asked (via next _read call)
|
|
119
156
|
// do, let's do nothing
|
|
@@ -128,14 +165,17 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
128
165
|
}
|
|
129
166
|
}
|
|
130
167
|
|
|
131
|
-
|
|
168
|
+
/**
|
|
169
|
+
* Counts how many times _read was called.
|
|
170
|
+
* For debugging.
|
|
171
|
+
*/
|
|
172
|
+
count = 0
|
|
132
173
|
|
|
133
174
|
override _read(): void {
|
|
175
|
+
this.lastReadTimestamp = Date.now()
|
|
176
|
+
|
|
134
177
|
// console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
|
|
135
178
|
this.count++
|
|
136
|
-
if (this.running) {
|
|
137
|
-
this.logger.log(`_read ${this.count}, wasRunning: true`)
|
|
138
|
-
}
|
|
139
179
|
|
|
140
180
|
if (this.done) {
|
|
141
181
|
this.logger.warn(`!!! _read was called, but done==true`)
|
|
@@ -144,6 +184,8 @@ export class DatastoreStreamReadable<T = any> extends Readable implements Readab
|
|
|
144
184
|
|
|
145
185
|
if (!this.running) {
|
|
146
186
|
void this.runNextQuery()
|
|
187
|
+
} else {
|
|
188
|
+
this.logger.log(`_read ${this.count}, wasRunning: true`)
|
|
147
189
|
}
|
|
148
190
|
}
|
|
149
191
|
}
|
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 {
|