@naturalcycles/datastore-lib 3.14.1 → 3.16.2

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/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## [3.16.2](https://github.com/NaturalCycles/datastore-lib/compare/v3.16.1...v3.16.2) (2021-10-17)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * adapt to latest db-lib with typed queries ([21d31ea](https://github.com/NaturalCycles/datastore-lib/commit/21d31ea7343ca0b2590950c067f6ce472dc82501))
7
+
8
+ ## [3.16.1](https://github.com/NaturalCycles/datastore-lib/compare/v3.16.0...v3.16.1) (2021-10-07)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * logging ([961b485](https://github.com/NaturalCycles/datastore-lib/commit/961b485a0ff1c0268bfc491e9f7a608a1b2b77da))
14
+
15
+ # [3.16.0](https://github.com/NaturalCycles/datastore-lib/compare/v3.15.0...v3.16.0) (2021-10-07)
16
+
17
+
18
+ ### Features
19
+
20
+ * allow to set streamOptions in DatastoreCfg ([c9fb5f0](https://github.com/NaturalCycles/datastore-lib/commit/c9fb5f08760f113d3b563ce80579df189d06b7b1))
21
+
22
+ # [3.15.0](https://github.com/NaturalCycles/datastore-lib/compare/v3.14.1...v3.15.0) (2021-10-07)
23
+
24
+
25
+ ### Features
26
+
27
+ * experimentalCursorStream mode to fix backpressure ([419f8e1](https://github.com/NaturalCycles/datastore-lib/commit/419f8e16ccb9722771a01b90001ae19f2a7a8595))
28
+
1
29
  ## [3.14.1](https://github.com/NaturalCycles/datastore-lib/compare/v3.14.0...v3.14.1) (2021-10-04)
2
30
 
3
31
 
@@ -0,0 +1,21 @@
1
+ /// <reference types="node" />
2
+ import { Readable } from 'stream';
3
+ import { Query } from '@google-cloud/datastore';
4
+ import type { ReadableTyped } from '@naturalcycles/nodejs-lib';
5
+ import type { DatastoreDBStreamOptions } from './datastore.model';
6
+ export declare class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
7
+ private q;
8
+ private originalLimit;
9
+ private rowsRetrieved;
10
+ private endCursor?;
11
+ private running;
12
+ private done;
13
+ private lastQueryDone?;
14
+ private totalWait;
15
+ private log;
16
+ private opt;
17
+ constructor(q: Query, opt: DatastoreDBStreamOptions);
18
+ private runNextQuery;
19
+ count: number;
20
+ _read(): void;
21
+ }
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatastoreStreamReadable = void 0;
4
+ const stream_1 = require("stream");
5
+ const js_lib_1 = require("@naturalcycles/js-lib");
6
+ class DatastoreStreamReadable extends stream_1.Readable {
7
+ constructor(q, opt) {
8
+ super({ objectMode: true });
9
+ this.q = q;
10
+ this.rowsRetrieved = 0;
11
+ this.running = false;
12
+ this.done = false;
13
+ this.totalWait = 0;
14
+ // private log = (...args: any[]): void => console.log(...args)
15
+ this.log = console.log.bind(console);
16
+ this.count = 0; // use for debugging
17
+ this.opt = {
18
+ rssLimitMB: 1000,
19
+ batchSize: 1000,
20
+ ...opt,
21
+ };
22
+ if (!opt.debug)
23
+ this.log = () => { };
24
+ this.originalLimit = q.limitVal;
25
+ console.log(`!! using experimentalCursorStream !! batchSize: ${opt.batchSize}`);
26
+ }
27
+ async runNextQuery() {
28
+ if (this.done)
29
+ return;
30
+ if (this.lastQueryDone) {
31
+ const now = Date.now();
32
+ this.totalWait += now - this.lastQueryDone;
33
+ }
34
+ this.running = true;
35
+ // console.log('running query...')
36
+ let limit = this.opt.batchSize;
37
+ if (this.originalLimit) {
38
+ limit = Math.min(this.opt.batchSize, this.originalLimit - this.rowsRetrieved);
39
+ }
40
+ // console.log(`limit: ${limit}`)
41
+ let q = this.q.limit(limit);
42
+ if (this.endCursor) {
43
+ q = q.start(this.endCursor);
44
+ }
45
+ try {
46
+ const [rows, info] = await q.run();
47
+ this.rowsRetrieved += rows.length;
48
+ this.log(`got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`, info.moreResults);
49
+ this.endCursor = info.endCursor;
50
+ this.running = false; // ready to take more _reads
51
+ this.lastQueryDone = Date.now();
52
+ rows.forEach(row => this.push(row));
53
+ if (!info.endCursor ||
54
+ info.moreResults === 'NO_MORE_RESULTS' ||
55
+ (this.originalLimit && this.rowsRetrieved >= this.originalLimit)) {
56
+ this.log(`!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${(0, js_lib_1._ms)(this.totalWait)}`);
57
+ this.push(null);
58
+ this.done = true;
59
+ }
60
+ else if (this.opt.rssLimitMB) {
61
+ const rssMB = Math.round(process.memoryUsage().rss / 1024 / 1024);
62
+ if (rssMB <= this.opt.rssLimitMB) {
63
+ void this.runNextQuery();
64
+ }
65
+ else {
66
+ this.log(`rssLimitMB reached ${rssMB} > ${this.opt.rssLimitMB}, pausing stream`);
67
+ }
68
+ }
69
+ }
70
+ catch (err) {
71
+ console.error('DatastoreStreamReadable error!\n', err);
72
+ this.emit('error', err);
73
+ }
74
+ }
75
+ _read() {
76
+ // console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
77
+ this.count++;
78
+ if (this.running) {
79
+ this.log(`_read ${this.count}, wasRunning: true`);
80
+ }
81
+ if (this.done) {
82
+ console.warn(`!!! _read was called, but done==true`);
83
+ return;
84
+ }
85
+ if (!this.running) {
86
+ void this.runNextQuery();
87
+ }
88
+ }
89
+ }
90
+ exports.DatastoreStreamReadable = DatastoreStreamReadable;
@@ -2,7 +2,7 @@ import type { Datastore, Key, Query } from '@google-cloud/datastore';
2
2
  import { BaseCommonDB, CommonDB, DBQuery, DBTransaction, ObjectWithId, RunQueryResult } from '@naturalcycles/db-lib';
3
3
  import { JsonSchemaRootObject } from '@naturalcycles/js-lib';
4
4
  import { ReadableTyped } from '@naturalcycles/nodejs-lib';
5
- import { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBSaveOptions, DatastorePayload, DatastorePropertyStats, DatastoreStats } from './datastore.model';
5
+ import { DatastoreDBCfg, DatastoreDBOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePayload, DatastorePropertyStats, DatastoreStats } from './datastore.model';
6
6
  /**
7
7
  * Datastore API:
8
8
  * https://googlecloudplatform.github.io/google-cloud-node/#/docs/datastore/1.0.3/datastore
@@ -23,12 +23,12 @@ export declare class DatastoreDB extends BaseCommonDB implements CommonDB {
23
23
  runQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBOptions): Promise<RunQueryResult<ROW>>;
24
24
  runQueryCount<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBOptions): Promise<number>;
25
25
  runDatastoreQuery<ROW extends ObjectWithId>(q: Query): Promise<RunQueryResult<ROW>>;
26
- runQueryStream<ROW extends ObjectWithId>(q: Query): ReadableTyped<ROW>;
27
- streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, _opt?: DatastoreDBOptions): ReadableTyped<ROW>;
26
+ private runQueryStream;
27
+ streamQuery<ROW extends ObjectWithId>(dbQuery: DBQuery<ROW>, opt?: DatastoreDBStreamOptions): ReadableTyped<ROW>;
28
28
  /**
29
29
  * Returns saved entities with generated id/updated/created (non-mutating!)
30
30
  */
31
- saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: DatastoreDBSaveOptions): Promise<void>;
31
+ saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: DatastoreDBSaveOptions<ROW>): Promise<void>;
32
32
  deleteByQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: DatastoreDBOptions): Promise<number>;
33
33
  /**
34
34
  * Limitation: Datastore's delete returns void, so we always return all ids here as "deleted"
@@ -6,6 +6,7 @@ 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");
8
8
  const datastore_model_1 = require("./datastore.model");
9
+ const DatastoreStreamReadable_1 = require("./DatastoreStreamReadable");
9
10
  const query_util_1 = require("./query.util");
10
11
  // Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
11
12
  const MAX_ITEMS = 500;
@@ -88,21 +89,23 @@ class DatastoreDB extends db_lib_1.BaseCommonDB {
88
89
  rows,
89
90
  };
90
91
  }
91
- runQueryStream(q) {
92
- return (this.ds()
93
- .runQueryStream(q)
94
- // Important that new instance of Transform should be created every time!
95
- // Otherwise they share shate and affect each other
96
- .pipe(new stream_1.Transform({
92
+ runQueryStream(q, _opt) {
93
+ const opt = {
94
+ ...this.cfg.streamOptions,
95
+ ..._opt,
96
+ };
97
+ return (opt.experimentalCursorStream
98
+ ? new DatastoreStreamReadable_1.DatastoreStreamReadable(q, opt)
99
+ : this.ds().runQueryStream(q)).pipe(new stream_1.Transform({
97
100
  objectMode: true,
98
101
  transform: (chunk, _enc, cb) => {
99
102
  cb(null, this.mapId(chunk));
100
103
  },
101
- })));
104
+ }));
102
105
  }
103
- streamQuery(dbQuery, _opt) {
106
+ streamQuery(dbQuery, opt) {
104
107
  const q = (0, query_util_1.dbQueryToDatastoreQuery)(dbQuery, this.ds().createQuery(dbQuery.table));
105
- return this.runQueryStream(q);
108
+ return this.runQueryStream(q, opt);
106
109
  }
107
110
  // https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
108
111
  /**
@@ -1,5 +1,6 @@
1
1
  import type { DatastoreOptions, Key, Transaction } from '@google-cloud/datastore';
2
2
  import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib';
3
+ import { AnyObjectWithId, ObjectWithId } from '@naturalcycles/db-lib/src/db.model';
3
4
  export interface DatastorePayload<T = any> {
4
5
  key: Key;
5
6
  data: T;
@@ -24,6 +25,11 @@ export interface DatastoreDBCfg extends DatastoreOptions {
24
25
  * Allows to set the old native library here, e.g `grpc: require('grpc')`
25
26
  */
26
27
  grpc?: any;
28
+ /**
29
+ * Use it to set default options to stream operations,
30
+ * e.g you can globally enable `experimentalCursorStream` here, set the batchSize, etc.
31
+ */
32
+ streamOptions?: DatastoreDBStreamOptions;
27
33
  }
28
34
  export interface DatastoreCredentials {
29
35
  type?: string;
@@ -35,10 +41,43 @@ export interface DatastoreCredentials {
35
41
  client_secret?: string;
36
42
  refresh_token?: string;
37
43
  }
44
+ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
45
+ /**
46
+ * Set to `true` to stream via experimental "cursor-query based stream".
47
+ *
48
+ * @default false
49
+ */
50
+ experimentalCursorStream?: boolean;
51
+ /**
52
+ * Applicable to `experimentalCursorStream`
53
+ *
54
+ * @default 1000
55
+ */
56
+ batchSize?: number;
57
+ /**
58
+ * Applicable to `experimentalCursorStream`
59
+ *
60
+ * Set to a value (number of Megabytes) to control the peak RSS size.
61
+ * If limit is reached - streaming will pause until the stream keeps up, and then
62
+ * resumes.
63
+ *
64
+ * Set to 0/undefined to disable. Stream will get "slow" then, cause it'll only run the query
65
+ * when _read is called.
66
+ *
67
+ * @default 1000
68
+ */
69
+ rssLimitMB?: number;
70
+ /**
71
+ * Set to `true` to log additional debug info, when using experimentalCursorStream.
72
+ *
73
+ * @default false
74
+ */
75
+ debug?: boolean;
76
+ }
38
77
  export interface DatastoreDBOptions extends CommonDBOptions {
39
78
  tx?: Transaction;
40
79
  }
41
- export interface DatastoreDBSaveOptions extends CommonDBSaveOptions {
80
+ export interface DatastoreDBSaveOptions<ROW extends ObjectWithId = AnyObjectWithId> extends CommonDBSaveOptions<ROW> {
42
81
  tx?: Transaction;
43
82
  }
44
83
  export interface DatastoreStats {
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { DatastoreDB } from './datastore.db';
2
- import { DatastoreCredentials, DatastoreDBCfg, DatastorePayload, DatastorePropertyStats, DatastoreStats, DatastoreType } from './datastore.model';
2
+ import { DatastoreCredentials, DatastoreDBCfg, DatastoreDBOptions, DatastoreDBSaveOptions, DatastoreDBStreamOptions, DatastorePayload, DatastorePropertyStats, DatastoreStats, DatastoreType } from './datastore.model';
3
3
  import { DatastoreKeyValueDB, DatastoreKeyValueDBCfg } from './datastoreKeyValueDB';
4
4
  import { getDBAdapter } from './dbAdapter';
5
- export type { DatastorePayload, DatastoreDBCfg, DatastoreKeyValueDBCfg, DatastoreStats, DatastoreCredentials, DatastorePropertyStats, };
5
+ export type { DatastorePayload, DatastoreDBCfg, DatastoreKeyValueDBCfg, DatastoreStats, DatastoreCredentials, DatastorePropertyStats, DatastoreDBStreamOptions, DatastoreDBOptions, DatastoreDBSaveOptions, };
6
6
  export { DatastoreDB, DatastoreType, getDBAdapter, DatastoreKeyValueDB };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@naturalcycles/datastore-lib",
3
- "version": "3.14.1",
3
+ "version": "3.16.2",
4
4
  "description": "Opinionated library to work with Google Datastore",
5
5
  "scripts": {
6
6
  "prepare": "husky install"
@@ -0,0 +1,120 @@
1
+ import { Readable } from 'stream'
2
+ import { Query } from '@google-cloud/datastore'
3
+ import { _ms } from '@naturalcycles/js-lib'
4
+ import type { ReadableTyped } from '@naturalcycles/nodejs-lib'
5
+ import type { DatastoreDBStreamOptions } from './datastore.model'
6
+
7
+ export class DatastoreStreamReadable<T = any> extends Readable implements ReadableTyped<T> {
8
+ private originalLimit: number
9
+ private rowsRetrieved = 0
10
+ private endCursor?: string
11
+ private running = false
12
+ private done = false
13
+ private lastQueryDone?: number
14
+ private totalWait = 0
15
+
16
+ // private log = (...args: any[]): void => console.log(...args)
17
+ private log = console.log.bind(console)
18
+
19
+ private opt: DatastoreDBStreamOptions & { batchSize: number }
20
+
21
+ constructor(private q: Query, opt: DatastoreDBStreamOptions) {
22
+ super({ objectMode: true })
23
+
24
+ this.opt = {
25
+ rssLimitMB: 1000,
26
+ batchSize: 1000,
27
+ ...opt,
28
+ }
29
+
30
+ if (!opt.debug) this.log = () => {}
31
+
32
+ this.originalLimit = q.limitVal
33
+
34
+ console.log(`!! using experimentalCursorStream !! batchSize: ${opt.batchSize}`)
35
+ }
36
+
37
+ private async runNextQuery(): Promise<void> {
38
+ if (this.done) return
39
+
40
+ if (this.lastQueryDone) {
41
+ const now = Date.now()
42
+ this.totalWait += now - this.lastQueryDone
43
+ }
44
+
45
+ this.running = true
46
+ // console.log('running query...')
47
+
48
+ let limit = this.opt.batchSize
49
+
50
+ if (this.originalLimit) {
51
+ limit = Math.min(this.opt.batchSize, this.originalLimit - this.rowsRetrieved)
52
+ }
53
+
54
+ // console.log(`limit: ${limit}`)
55
+ let q = this.q.limit(limit)
56
+ if (this.endCursor) {
57
+ q = q.start(this.endCursor)
58
+ }
59
+
60
+ try {
61
+ const [rows, info] = await q.run()
62
+
63
+ this.rowsRetrieved += rows.length
64
+ this.log(
65
+ `got ${rows.length} rows, ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(
66
+ this.totalWait,
67
+ )}`,
68
+ info.moreResults,
69
+ )
70
+
71
+ this.endCursor = info.endCursor
72
+ this.running = false // ready to take more _reads
73
+ this.lastQueryDone = Date.now()
74
+
75
+ rows.forEach(row => this.push(row))
76
+
77
+ if (
78
+ !info.endCursor ||
79
+ info.moreResults === 'NO_MORE_RESULTS' ||
80
+ (this.originalLimit && this.rowsRetrieved >= this.originalLimit)
81
+ ) {
82
+ this.log(
83
+ `!!!! DONE! ${this.rowsRetrieved} rowsRetrieved, totalWait: ${_ms(this.totalWait)}`,
84
+ )
85
+ this.push(null)
86
+ this.done = true
87
+ } else if (this.opt.rssLimitMB) {
88
+ const rssMB = Math.round(process.memoryUsage().rss / 1024 / 1024)
89
+
90
+ if (rssMB <= this.opt.rssLimitMB) {
91
+ void this.runNextQuery()
92
+ } else {
93
+ this.log(`rssLimitMB reached ${rssMB} > ${this.opt.rssLimitMB}, pausing stream`)
94
+ }
95
+ }
96
+ } catch (err) {
97
+ console.error('DatastoreStreamReadable error!\n', err)
98
+ this.emit('error', err)
99
+ }
100
+ }
101
+
102
+ count = 0 // use for debugging
103
+
104
+ override _read(): void {
105
+ // console.log(`_read called ${++this.count}, wasRunning: ${this.running}`) // debugging
106
+ this.count++
107
+ if (this.running) {
108
+ this.log(`_read ${this.count}, wasRunning: true`)
109
+ }
110
+
111
+ if (this.done) {
112
+ console.warn(`!!! _read was called, but done==true`)
113
+ return
114
+ }
115
+
116
+ if (!this.running) {
117
+ void this.runNextQuery()
118
+ }
119
+ }
120
+ }
@@ -29,11 +29,13 @@ import {
29
29
  DatastoreDBCfg,
30
30
  DatastoreDBOptions,
31
31
  DatastoreDBSaveOptions,
32
+ DatastoreDBStreamOptions,
32
33
  DatastorePayload,
33
34
  DatastorePropertyStats,
34
35
  DatastoreStats,
35
36
  DatastoreType,
36
37
  } from './datastore.model'
38
+ import { DatastoreStreamReadable } from './DatastoreStreamReadable'
37
39
  import { dbQueryToDatastoreQuery } from './query.util'
38
40
 
39
41
  // Datastore (also Firestore and other Google APIs) supports max 500 of items when saving/deleting, etc.
@@ -154,29 +156,35 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
154
156
  }
155
157
  }
156
158
 
157
- runQueryStream<ROW extends ObjectWithId>(q: Query): ReadableTyped<ROW> {
159
+ private runQueryStream<ROW extends ObjectWithId>(
160
+ q: Query,
161
+ _opt?: DatastoreDBStreamOptions,
162
+ ): ReadableTyped<ROW> {
163
+ const opt = {
164
+ ...this.cfg.streamOptions,
165
+ ..._opt,
166
+ }
167
+
158
168
  return (
159
- this.ds()
160
- .runQueryStream(q)
161
- // Important that new instance of Transform should be created every time!
162
- // Otherwise they share shate and affect each other
163
- .pipe(
164
- new Transform({
165
- objectMode: true,
166
- transform: (chunk, _enc, cb) => {
167
- cb(null, this.mapId(chunk))
168
- },
169
- }),
170
- )
169
+ opt.experimentalCursorStream
170
+ ? new DatastoreStreamReadable(q, opt)
171
+ : this.ds().runQueryStream(q)
172
+ ).pipe(
173
+ new Transform({
174
+ objectMode: true,
175
+ transform: (chunk, _enc, cb) => {
176
+ cb(null, this.mapId(chunk))
177
+ },
178
+ }),
171
179
  )
172
180
  }
173
181
 
174
182
  override streamQuery<ROW extends ObjectWithId>(
175
183
  dbQuery: DBQuery<ROW>,
176
- _opt?: DatastoreDBOptions,
184
+ opt?: DatastoreDBStreamOptions,
177
185
  ): ReadableTyped<ROW> {
178
186
  const q = dbQueryToDatastoreQuery(dbQuery, this.ds().createQuery(dbQuery.table))
179
- return this.runQueryStream(q)
187
+ return this.runQueryStream(q, opt)
180
188
  }
181
189
 
182
190
  // https://github.com/GoogleCloudPlatform/nodejs-getting-started/blob/master/2-structured-data/books/model-datastore.js
@@ -187,9 +195,11 @@ export class DatastoreDB extends BaseCommonDB implements CommonDB {
187
195
  override async saveBatch<ROW extends ObjectWithId>(
188
196
  table: string,
189
197
  rows: ROW[],
190
- opt: DatastoreDBSaveOptions = {},
198
+ opt: DatastoreDBSaveOptions<ROW> = {},
191
199
  ): Promise<void> {
192
- const entities = rows.map(obj => this.toDatastoreEntity(table, obj, opt.excludeFromIndexes))
200
+ const entities = rows.map(obj =>
201
+ this.toDatastoreEntity(table, obj, opt.excludeFromIndexes as string[]),
202
+ )
193
203
 
194
204
  const save = pRetry(
195
205
  async (batch: DatastorePayload<ROW>[]) => {
@@ -1,5 +1,6 @@
1
1
  import type { DatastoreOptions, Key, Transaction } from '@google-cloud/datastore'
2
2
  import { CommonDBOptions, CommonDBSaveOptions } from '@naturalcycles/db-lib'
3
+ import { AnyObjectWithId, ObjectWithId } from '@naturalcycles/db-lib/src/db.model'
3
4
 
4
5
  export interface DatastorePayload<T = any> {
5
6
  key: Key
@@ -29,6 +30,12 @@ export interface DatastoreDBCfg extends DatastoreOptions {
29
30
  * Allows to set the old native library here, e.g `grpc: require('grpc')`
30
31
  */
31
32
  grpc?: any
33
+
34
+ /**
35
+ * Use it to set default options to stream operations,
36
+ * e.g you can globally enable `experimentalCursorStream` here, set the batchSize, etc.
37
+ */
38
+ streamOptions?: DatastoreDBStreamOptions
32
39
  }
33
40
 
34
41
  export interface DatastoreCredentials {
@@ -42,10 +49,48 @@ export interface DatastoreCredentials {
42
49
  refresh_token?: string
43
50
  }
44
51
 
52
+ export interface DatastoreDBStreamOptions extends DatastoreDBOptions {
53
+ /**
54
+ * Set to `true` to stream via experimental "cursor-query based stream".
55
+ *
56
+ * @default false
57
+ */
58
+ experimentalCursorStream?: boolean
59
+
60
+ /**
61
+ * Applicable to `experimentalCursorStream`
62
+ *
63
+ * @default 1000
64
+ */
65
+ batchSize?: number
66
+
67
+ /**
68
+ * Applicable to `experimentalCursorStream`
69
+ *
70
+ * Set to a value (number of Megabytes) to control the peak RSS size.
71
+ * If limit is reached - streaming will pause until the stream keeps up, and then
72
+ * resumes.
73
+ *
74
+ * Set to 0/undefined to disable. Stream will get "slow" then, cause it'll only run the query
75
+ * when _read is called.
76
+ *
77
+ * @default 1000
78
+ */
79
+ rssLimitMB?: number
80
+
81
+ /**
82
+ * Set to `true` to log additional debug info, when using experimentalCursorStream.
83
+ *
84
+ * @default false
85
+ */
86
+ debug?: boolean
87
+ }
88
+
45
89
  export interface DatastoreDBOptions extends CommonDBOptions {
46
90
  tx?: Transaction
47
91
  }
48
- export interface DatastoreDBSaveOptions extends CommonDBSaveOptions {
92
+ export interface DatastoreDBSaveOptions<ROW extends ObjectWithId = AnyObjectWithId>
93
+ extends CommonDBSaveOptions<ROW> {
49
94
  tx?: Transaction
50
95
  }
51
96
 
@@ -9,7 +9,7 @@ interface KVObject {
9
9
  v: Buffer
10
10
  }
11
11
 
12
- const excludeFromIndexes = ['v']
12
+ const excludeFromIndexes: (keyof KVObject)[] = ['v']
13
13
 
14
14
  export interface DatastoreKeyValueDBCfg extends DatastoreDBCfg {}
15
15
 
package/src/index.ts CHANGED
@@ -2,6 +2,9 @@ import { DatastoreDB } from './datastore.db'
2
2
  import {
3
3
  DatastoreCredentials,
4
4
  DatastoreDBCfg,
5
+ DatastoreDBOptions,
6
+ DatastoreDBSaveOptions,
7
+ DatastoreDBStreamOptions,
5
8
  DatastorePayload,
6
9
  DatastorePropertyStats,
7
10
  DatastoreStats,
@@ -17,6 +20,9 @@ export type {
17
20
  DatastoreStats,
18
21
  DatastoreCredentials,
19
22
  DatastorePropertyStats,
23
+ DatastoreDBStreamOptions,
24
+ DatastoreDBOptions,
25
+ DatastoreDBSaveOptions,
20
26
  }
21
27
 
22
28
  export { DatastoreDB, DatastoreType, getDBAdapter, DatastoreKeyValueDB }
package/src/query.util.ts CHANGED
@@ -19,18 +19,24 @@ export function dbQueryToDatastoreQuery<ROW extends ObjectWithId>(
19
19
 
20
20
  // filter
21
21
  // eslint-disable-next-line unicorn/no-array-reduce
22
- q = dbQuery._filters.reduce((q, f) => q.filter(f.name, OP_MAP[f.op] || (f.op as any), f.val), q)
22
+ q = dbQuery._filters.reduce(
23
+ (q, f) => q.filter(f.name as string, OP_MAP[f.op] || (f.op as any), f.val),
24
+ q,
25
+ )
23
26
 
24
27
  // limit
25
28
  q = q.limit(dbQuery._limitValue || 0)
26
29
 
27
30
  // order
28
31
  // eslint-disable-next-line unicorn/no-array-reduce
29
- q = dbQuery._orders.reduce((q, ord) => q.order(ord.name, { descending: ord.descending }), q)
32
+ q = dbQuery._orders.reduce(
33
+ (q, ord) => q.order(ord.name as string, { descending: ord.descending }),
34
+ q,
35
+ )
30
36
 
31
37
  // select
32
38
  if (dbQuery._selectedFieldNames) {
33
- const fields = dbQuery._selectedFieldNames.map(f => FNAME_MAP[f] || f)
39
+ const fields = (dbQuery._selectedFieldNames as string[]).map(f => FNAME_MAP[f] || f)
34
40
 
35
41
  // Datastore requires you to specify at least one column, so if empty array is passed - it'll include __key__ at least
36
42
  if (!fields.length) {