@naturalcycles/firestore-lib 2.12.0 → 2.13.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/firestore.db.d.ts +2 -2
- package/dist/firestore.db.js +5 -4
- package/dist/firestoreShardedReadable.js +2 -2
- package/dist/firestoreStreamReadable.js +20 -6
- package/package.json +2 -2
- package/src/firestore.db.ts +12 -10
- package/src/firestoreShardedReadable.ts +2 -2
- package/src/firestoreStreamReadable.ts +28 -7
package/dist/firestore.db.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ import type { CommonDB, CommonDBOptions, CommonDBReadOptions, CommonDBSaveOption
|
|
|
3
3
|
import { BaseCommonDB } from '@naturalcycles/db-lib';
|
|
4
4
|
import type { CommonLogger, CommonLogLevel } from '@naturalcycles/js-lib/log';
|
|
5
5
|
import type { ObjectWithId, PositiveInteger, StringMap } from '@naturalcycles/js-lib/types';
|
|
6
|
-
import
|
|
6
|
+
import { Pipeline } from '@naturalcycles/nodejs-lib/stream';
|
|
7
7
|
export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
8
8
|
constructor(cfg: FirestoreDBCfg);
|
|
9
9
|
cfg: FirestoreDBCfg & {
|
|
@@ -15,7 +15,7 @@ export declare class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
15
15
|
runQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt?: FirestoreDBOptions): Promise<RunQueryResult<ROW>>;
|
|
16
16
|
runFirestoreQuery<ROW extends ObjectWithId>(q: Query): Promise<ROW[]>;
|
|
17
17
|
runQueryCount<ROW extends ObjectWithId>(q: DBQuery<ROW>, _opt?: FirestoreDBOptions): Promise<number>;
|
|
18
|
-
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt_?: FirestoreDBStreamOptions):
|
|
18
|
+
streamQuery<ROW extends ObjectWithId>(q: DBQuery<ROW>, opt_?: FirestoreDBStreamOptions): Pipeline<ROW>;
|
|
19
19
|
saveBatch<ROW extends ObjectWithId>(table: string, rows: ROW[], opt?: FirestoreDBSaveOptions<ROW>): Promise<void>;
|
|
20
20
|
multiSave<ROW extends ObjectWithId>(map: StringMap<ROW[]>, opt?: FirestoreDBSaveOptions<ROW>): Promise<void>;
|
|
21
21
|
patchById<ROW extends ObjectWithId>(table: string, id: string, patch: Partial<ROW>, opt?: FirestoreDBOptions): Promise<void>;
|
package/dist/firestore.db.js
CHANGED
|
@@ -6,6 +6,7 @@ import { _assert } from '@naturalcycles/js-lib/error/assert.js';
|
|
|
6
6
|
import { _filterUndefinedValues, _omit } from '@naturalcycles/js-lib/object/object.util.js';
|
|
7
7
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js';
|
|
8
8
|
import { _stringMapEntries } from '@naturalcycles/js-lib/types';
|
|
9
|
+
import { Pipeline } from '@naturalcycles/nodejs-lib/stream';
|
|
9
10
|
import { escapeDocId, unescapeDocId } from './firestore.util.js';
|
|
10
11
|
import { FirestoreShardedReadable } from './firestoreShardedReadable.js';
|
|
11
12
|
import { FirestoreStreamReadable } from './firestoreStreamReadable.js';
|
|
@@ -101,17 +102,17 @@ export class FirestoreDB extends BaseCommonDB {
|
|
|
101
102
|
...opt_,
|
|
102
103
|
};
|
|
103
104
|
if (opt.experimentalCursorStream) {
|
|
104
|
-
return new FirestoreStreamReadable(firestoreQuery, q, opt);
|
|
105
|
+
return Pipeline.from(new FirestoreStreamReadable(firestoreQuery, q, opt));
|
|
105
106
|
}
|
|
106
107
|
if (opt.experimentalShardedStream) {
|
|
107
|
-
return new FirestoreShardedReadable(firestoreQuery, q, opt);
|
|
108
|
+
return Pipeline.from(new FirestoreShardedReadable(firestoreQuery, q, opt));
|
|
108
109
|
}
|
|
109
|
-
return firestoreQuery.stream().map(doc => {
|
|
110
|
+
return Pipeline.from(firestoreQuery.stream().map(doc => {
|
|
110
111
|
return {
|
|
111
112
|
id: unescapeDocId(doc.id),
|
|
112
113
|
...doc.data(),
|
|
113
114
|
};
|
|
114
|
-
});
|
|
115
|
+
}));
|
|
115
116
|
}
|
|
116
117
|
// SAVE
|
|
117
118
|
async saveBatch(table, rows, opt = {}) {
|
|
@@ -65,7 +65,7 @@ export class FirestoreShardedReadable extends Readable {
|
|
|
65
65
|
}
|
|
66
66
|
void this.runNextQuery(shard).catch(err => {
|
|
67
67
|
this.logger.error('error in runNextQuery', err);
|
|
68
|
-
this.
|
|
68
|
+
this.destroy(err);
|
|
69
69
|
});
|
|
70
70
|
}
|
|
71
71
|
async runNextQuery(shard) {
|
|
@@ -155,7 +155,7 @@ export class FirestoreShardedReadable extends Readable {
|
|
|
155
155
|
table,
|
|
156
156
|
rowsRetrieved: this.rowsRetrieved,
|
|
157
157
|
}, err);
|
|
158
|
-
this.
|
|
158
|
+
this.destroy(err);
|
|
159
159
|
return;
|
|
160
160
|
}
|
|
161
161
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Readable } from 'node:stream';
|
|
2
|
-
import { FieldPath } from '@google-cloud/firestore';
|
|
2
|
+
import { FieldPath, } from '@google-cloud/firestore';
|
|
3
3
|
import { localTime } from '@naturalcycles/js-lib/datetime/localTime.js';
|
|
4
4
|
import { _ms } from '@naturalcycles/js-lib/datetime/time.util.js';
|
|
5
5
|
import { createCommonLoggerAtLevel } from '@naturalcycles/js-lib/log';
|
|
@@ -62,7 +62,7 @@ export class FirestoreStreamReadable extends Readable {
|
|
|
62
62
|
}
|
|
63
63
|
void this.runNextQuery().catch(err => {
|
|
64
64
|
this.logger.error('error in runNextQuery', err);
|
|
65
|
-
this.
|
|
65
|
+
this.destroy(err);
|
|
66
66
|
});
|
|
67
67
|
}
|
|
68
68
|
async runNextQuery() {
|
|
@@ -91,9 +91,9 @@ export class FirestoreStreamReadable extends Readable {
|
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
const rows = [];
|
|
94
|
-
let
|
|
94
|
+
let lastDoc;
|
|
95
95
|
for (const doc of qs.docs) {
|
|
96
|
-
|
|
96
|
+
lastDoc = doc;
|
|
97
97
|
rows.push({
|
|
98
98
|
id: unescapeDocId(doc.id),
|
|
99
99
|
...doc.data(),
|
|
@@ -101,7 +101,7 @@ export class FirestoreStreamReadable extends Readable {
|
|
|
101
101
|
}
|
|
102
102
|
this.rowsRetrieved += rows.length;
|
|
103
103
|
logger.debug(`${table} got ${rows.length} rows in ${_ms(queryTook)}, ${this.rowsRetrieved} rowsRetrieved`);
|
|
104
|
-
this.endCursor =
|
|
104
|
+
this.endCursor = lastDoc;
|
|
105
105
|
this.queryIsRunning = false; // ready to take more _reads
|
|
106
106
|
let shouldContinue = false;
|
|
107
107
|
for (const row of rows) {
|
|
@@ -137,6 +137,7 @@ export class FirestoreStreamReadable extends Readable {
|
|
|
137
137
|
return await q.get();
|
|
138
138
|
}, {
|
|
139
139
|
name: `FirestoreStreamReadable.query(${table})`,
|
|
140
|
+
predicate: err => RETRY_ON.some(s => err?.message?.toLowerCase()?.includes(s)),
|
|
140
141
|
maxAttempts: 5,
|
|
141
142
|
delay: 5000,
|
|
142
143
|
delayMultiplier: 2,
|
|
@@ -145,12 +146,25 @@ export class FirestoreStreamReadable extends Readable {
|
|
|
145
146
|
});
|
|
146
147
|
}
|
|
147
148
|
catch (err) {
|
|
149
|
+
console.log(q._queryOptions);
|
|
148
150
|
logger.error(`FirestoreStreamReadable error!\n`, {
|
|
149
151
|
table,
|
|
150
152
|
rowsRetrieved: this.rowsRetrieved,
|
|
151
153
|
}, err);
|
|
152
|
-
this.
|
|
154
|
+
this.destroy(err);
|
|
153
155
|
return;
|
|
154
156
|
}
|
|
155
157
|
}
|
|
156
158
|
}
|
|
159
|
+
// Examples of errors:
|
|
160
|
+
// UNKNOWN: Stream removed
|
|
161
|
+
const RETRY_ON = [
|
|
162
|
+
'GOAWAY',
|
|
163
|
+
'UNAVAILABLE',
|
|
164
|
+
'UNKNOWN',
|
|
165
|
+
'DEADLINE_EXCEEDED',
|
|
166
|
+
'ABORTED',
|
|
167
|
+
'much contention',
|
|
168
|
+
'try again',
|
|
169
|
+
'timeout',
|
|
170
|
+
].map(s => s.toLowerCase());
|
package/package.json
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
"@types/node": "^24",
|
|
13
13
|
"dotenv": "^17",
|
|
14
14
|
"firebase-admin": "^13",
|
|
15
|
-
"@naturalcycles/dev-lib": "
|
|
15
|
+
"@naturalcycles/dev-lib": "19.37.0"
|
|
16
16
|
},
|
|
17
17
|
"exports": {
|
|
18
18
|
".": "./dist/index.js"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"engines": {
|
|
39
39
|
"node": ">=22.12.0"
|
|
40
40
|
},
|
|
41
|
-
"version": "2.
|
|
41
|
+
"version": "2.13.0",
|
|
42
42
|
"description": "Firestore implementation of CommonDB interface",
|
|
43
43
|
"author": "Natural Cycles Team",
|
|
44
44
|
"license": "MIT",
|
package/src/firestore.db.ts
CHANGED
|
@@ -30,7 +30,7 @@ import { _filterUndefinedValues, _omit } from '@naturalcycles/js-lib/object/obje
|
|
|
30
30
|
import { pMap } from '@naturalcycles/js-lib/promise/pMap.js'
|
|
31
31
|
import type { ObjectWithId, PositiveInteger, StringMap } from '@naturalcycles/js-lib/types'
|
|
32
32
|
import { _stringMapEntries } from '@naturalcycles/js-lib/types'
|
|
33
|
-
import type
|
|
33
|
+
import { Pipeline, type ReadableTyped } from '@naturalcycles/nodejs-lib/stream'
|
|
34
34
|
import { escapeDocId, unescapeDocId } from './firestore.util.js'
|
|
35
35
|
import { FirestoreShardedReadable } from './firestoreShardedReadable.js'
|
|
36
36
|
import { FirestoreStreamReadable } from './firestoreStreamReadable.js'
|
|
@@ -152,7 +152,7 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
152
152
|
override streamQuery<ROW extends ObjectWithId>(
|
|
153
153
|
q: DBQuery<ROW>,
|
|
154
154
|
opt_?: FirestoreDBStreamOptions,
|
|
155
|
-
):
|
|
155
|
+
): Pipeline<ROW> {
|
|
156
156
|
const firestoreQuery = dbQueryToFirestoreQuery(q, this.cfg.firestore.collection(q.table))
|
|
157
157
|
|
|
158
158
|
const opt: FirestoreDBStreamOptions = {
|
|
@@ -163,19 +163,21 @@ export class FirestoreDB extends BaseCommonDB implements CommonDB {
|
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
if (opt.experimentalCursorStream) {
|
|
166
|
-
return new FirestoreStreamReadable(firestoreQuery, q, opt)
|
|
166
|
+
return Pipeline.from(new FirestoreStreamReadable(firestoreQuery, q, opt))
|
|
167
167
|
}
|
|
168
168
|
|
|
169
169
|
if (opt.experimentalShardedStream) {
|
|
170
|
-
return new FirestoreShardedReadable(firestoreQuery, q, opt)
|
|
170
|
+
return Pipeline.from(new FirestoreShardedReadable(firestoreQuery, q, opt))
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
return
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
173
|
+
return Pipeline.from(
|
|
174
|
+
(firestoreQuery.stream() as ReadableTyped<QueryDocumentSnapshot<any>>).map(doc => {
|
|
175
|
+
return {
|
|
176
|
+
id: unescapeDocId(doc.id),
|
|
177
|
+
...doc.data(),
|
|
178
|
+
} as ROW
|
|
179
|
+
}),
|
|
180
|
+
)
|
|
179
181
|
}
|
|
180
182
|
|
|
181
183
|
// SAVE
|
|
@@ -91,7 +91,7 @@ export class FirestoreShardedReadable<T extends ObjectWithId = any>
|
|
|
91
91
|
}
|
|
92
92
|
void this.runNextQuery(shard).catch(err => {
|
|
93
93
|
this.logger.error('error in runNextQuery', err)
|
|
94
|
-
this.
|
|
94
|
+
this.destroy(err)
|
|
95
95
|
})
|
|
96
96
|
}
|
|
97
97
|
|
|
@@ -213,7 +213,7 @@ export class FirestoreShardedReadable<T extends ObjectWithId = any>
|
|
|
213
213
|
},
|
|
214
214
|
err,
|
|
215
215
|
)
|
|
216
|
-
this.
|
|
216
|
+
this.destroy(err as Error)
|
|
217
217
|
return
|
|
218
218
|
}
|
|
219
219
|
}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { Readable } from 'node:stream'
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
FieldPath,
|
|
4
|
+
type Query,
|
|
5
|
+
type QueryDocumentSnapshot,
|
|
6
|
+
type QuerySnapshot,
|
|
7
|
+
} from '@google-cloud/firestore'
|
|
3
8
|
import type { DBQuery } from '@naturalcycles/db-lib'
|
|
4
9
|
import { localTime } from '@naturalcycles/js-lib/datetime/localTime.js'
|
|
5
10
|
import { _ms } from '@naturalcycles/js-lib/datetime/time.util.js'
|
|
@@ -17,7 +22,7 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
17
22
|
private readonly table: string
|
|
18
23
|
private readonly originalLimit: number
|
|
19
24
|
private rowsRetrieved = 0
|
|
20
|
-
private endCursor?:
|
|
25
|
+
private endCursor?: QueryDocumentSnapshot
|
|
21
26
|
private queryIsRunning = false
|
|
22
27
|
private paused = false
|
|
23
28
|
private done = false
|
|
@@ -85,7 +90,7 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
85
90
|
|
|
86
91
|
void this.runNextQuery().catch(err => {
|
|
87
92
|
this.logger.error('error in runNextQuery', err)
|
|
88
|
-
this.
|
|
93
|
+
this.destroy(err)
|
|
89
94
|
})
|
|
90
95
|
}
|
|
91
96
|
|
|
@@ -121,10 +126,10 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
121
126
|
}
|
|
122
127
|
|
|
123
128
|
const rows: T[] = []
|
|
124
|
-
let
|
|
129
|
+
let lastDoc: QueryDocumentSnapshot | undefined
|
|
125
130
|
|
|
126
131
|
for (const doc of qs.docs) {
|
|
127
|
-
|
|
132
|
+
lastDoc = doc
|
|
128
133
|
rows.push({
|
|
129
134
|
id: unescapeDocId(doc.id),
|
|
130
135
|
...doc.data(),
|
|
@@ -136,7 +141,7 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
136
141
|
`${table} got ${rows.length} rows in ${_ms(queryTook)}, ${this.rowsRetrieved} rowsRetrieved`,
|
|
137
142
|
)
|
|
138
143
|
|
|
139
|
-
this.endCursor =
|
|
144
|
+
this.endCursor = lastDoc
|
|
140
145
|
this.queryIsRunning = false // ready to take more _reads
|
|
141
146
|
let shouldContinue = false
|
|
142
147
|
|
|
@@ -177,6 +182,7 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
177
182
|
},
|
|
178
183
|
{
|
|
179
184
|
name: `FirestoreStreamReadable.query(${table})`,
|
|
185
|
+
predicate: err => RETRY_ON.some(s => err?.message?.toLowerCase()?.includes(s)),
|
|
180
186
|
maxAttempts: 5,
|
|
181
187
|
delay: 5000,
|
|
182
188
|
delayMultiplier: 2,
|
|
@@ -185,6 +191,8 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
185
191
|
},
|
|
186
192
|
)
|
|
187
193
|
} catch (err) {
|
|
194
|
+
console.log((q as any)._queryOptions)
|
|
195
|
+
|
|
188
196
|
logger.error(
|
|
189
197
|
`FirestoreStreamReadable error!\n`,
|
|
190
198
|
{
|
|
@@ -193,8 +201,21 @@ export class FirestoreStreamReadable<T extends ObjectWithId = any>
|
|
|
193
201
|
},
|
|
194
202
|
err,
|
|
195
203
|
)
|
|
196
|
-
this.
|
|
204
|
+
this.destroy(err as Error)
|
|
197
205
|
return
|
|
198
206
|
}
|
|
199
207
|
}
|
|
200
208
|
}
|
|
209
|
+
|
|
210
|
+
// Examples of errors:
|
|
211
|
+
// UNKNOWN: Stream removed
|
|
212
|
+
const RETRY_ON = [
|
|
213
|
+
'GOAWAY',
|
|
214
|
+
'UNAVAILABLE',
|
|
215
|
+
'UNKNOWN',
|
|
216
|
+
'DEADLINE_EXCEEDED',
|
|
217
|
+
'ABORTED',
|
|
218
|
+
'much contention',
|
|
219
|
+
'try again',
|
|
220
|
+
'timeout',
|
|
221
|
+
].map(s => s.toLowerCase())
|