@lossless.org/client 1.22.2 → 1.23.1
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_ts/00_commitinfo_data.js +1 -1
- package/dist_ts/nosqldb/classes.bootstraprecord.js +4 -4
- package/dist_ts/nosqldb/classes.connection.d.ts +3 -0
- package/dist_ts/nosqldb/classes.connection.js +3 -2
- package/dist_ts/nosqldb/classes.db.d.ts +31 -0
- package/dist_ts/nosqldb/classes.db.js +41 -1
- package/dist_ts/nosqldb/classes.doc.js +5 -5
- package/dist_ts/nosqldb/classes.exactpersistence.d.ts +3 -2
- package/dist_ts/nosqldb/classes.exactpersistence.js +8 -7
- package/dist_ts/nosqldb/classes.session.d.ts +7 -6
- package/dist_ts/nosqldb/classes.session.js +88 -36
- package/dist_ts/nosqldb/classes.writelog.d.ts +129 -0
- package/dist_ts/nosqldb/classes.writelog.js +497 -0
- package/dist_ts/nosqldb/classes.writeobserver.d.ts +13 -0
- package/dist_ts/nosqldb/classes.writeobserver.js +47 -25
- package/dist_ts/nosqldb/classes.writerevisions.d.ts +40 -4
- package/dist_ts/nosqldb/classes.writerevisions.js +174 -37
- package/dist_ts/nosqldb/engineidentity.d.ts +6 -0
- package/dist_ts/nosqldb/engineidentity.js +14 -1
- package/package.json +2 -2
- package/readme.md +11 -10
- package/ts/00_commitinfo_data.ts +1 -1
- package/ts/nosqldb/classes.bootstraprecord.ts +3 -3
- package/ts/nosqldb/classes.connection.ts +5 -1
- package/ts/nosqldb/classes.db.ts +64 -0
- package/ts/nosqldb/classes.doc.ts +4 -4
- package/ts/nosqldb/classes.exactpersistence.ts +8 -7
- package/ts/nosqldb/classes.session.ts +107 -38
- package/ts/nosqldb/classes.writelog.ts +701 -0
- package/ts/nosqldb/classes.writeobserver.ts +62 -28
- package/ts/nosqldb/classes.writerevisions.ts +225 -41
- package/ts/nosqldb/engineidentity.ts +16 -0
|
@@ -0,0 +1,497 @@
|
|
|
1
|
+
import * as plugins from './plugins.js';
|
|
2
|
+
import { assertDataOptions } from './classes.operationbudget.js';
|
|
3
|
+
import { SmartdataPersistenceError } from './classes.persistence.js';
|
|
4
|
+
import { narrowSmartdataWriteReport, normalizeObservedFields, reportOfSmartdataWrite, } from './classes.writeobserver.js';
|
|
5
|
+
import { isSmartdataWriteRevisionsActive, writeRevisionsCollectionName } from './classes.writerevisions.js';
|
|
6
|
+
import { smartdataCommitJournal } from './engineidentity.js';
|
|
7
|
+
import { logger } from './logging.js';
|
|
8
|
+
/** The collection that holds the entries. */
|
|
9
|
+
export const writeLogCollectionName = '__smartdata_write_log';
|
|
10
|
+
/** How long an entry is kept. The engine keeps a commit in its journal at most as long. */
|
|
11
|
+
export const writeLogRetentionSeconds = 15 * 60;
|
|
12
|
+
const expiryIndexName = 'smartdata_write_log_expiry';
|
|
13
|
+
const activePollMs = 250;
|
|
14
|
+
const idlePollMs = 1_000;
|
|
15
|
+
/** A follower that saw a commit this recently polls at the active pace. */
|
|
16
|
+
const activeWindowMs = 60_000;
|
|
17
|
+
const pageLimit = 128;
|
|
18
|
+
/** The engine's largest page; a single commit record larger than a page is refused. */
|
|
19
|
+
const pageBytes = 4 * 1024 * 1024;
|
|
20
|
+
const readTimeoutMs = 5_000;
|
|
21
|
+
/** Beyond these bounds, an entry states its largest collections as unplaced instead. */
|
|
22
|
+
const maximumEntryDocuments = 4_096;
|
|
23
|
+
const maximumEntryBytes = 1024 * 1024;
|
|
24
|
+
/** How long a follower remembers a local delivery whose commit the journal has not listed yet. */
|
|
25
|
+
const localDeliveryMemoryMs = 2 * writeLogRetentionSeconds * 1_000;
|
|
26
|
+
/**
|
|
27
|
+
* Namespaces whose changes only carry the bookkeeping of model writes, and the
|
|
28
|
+
* database's own namespace, which a catalog change publishes beside the
|
|
29
|
+
* collection it changed.
|
|
30
|
+
*/
|
|
31
|
+
const bookkeepingCollections = new Set([
|
|
32
|
+
'',
|
|
33
|
+
writeLogCollectionName,
|
|
34
|
+
writeRevisionsCollectionName,
|
|
35
|
+
]);
|
|
36
|
+
const writeKinds = new Set(['insert', 'update', 'replace', 'delete']);
|
|
37
|
+
const logFields = new WeakMap();
|
|
38
|
+
const logStates = new WeakMap();
|
|
39
|
+
const stateOf = (dbArg) => {
|
|
40
|
+
let state = logStates.get(dbArg);
|
|
41
|
+
if (!state) {
|
|
42
|
+
state = { followers: new Set(), pending: new Map() };
|
|
43
|
+
logStates.set(dbArg, state);
|
|
44
|
+
}
|
|
45
|
+
return state;
|
|
46
|
+
};
|
|
47
|
+
/** @internal Validates and records the option; called once by the database's constructor. */
|
|
48
|
+
export const configureSmartdataWriteLog = (dbArg, optionsArg) => {
|
|
49
|
+
if (optionsArg === undefined)
|
|
50
|
+
return;
|
|
51
|
+
const { fields } = assertDataOptions(optionsArg, ['fields']);
|
|
52
|
+
logFields.set(dbArg, normalizeObservedFields(fields, 'A SmartData write log'));
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* @internal The write log is configured and the engine journals its commits
|
|
56
|
+
* in commit order; elsewhere the option is inert.
|
|
57
|
+
*/
|
|
58
|
+
export const isSmartdataWriteLogActive = (dbArg) => logFields.has(dbArg)
|
|
59
|
+
&& dbArg.capabilities?.transactions === 'available'
|
|
60
|
+
&& smartdataCommitJournal(dbArg.engineIdentity);
|
|
61
|
+
/**
|
|
62
|
+
* @internal Every model write runs in an owned transaction, which carries its
|
|
63
|
+
* write revisions and its write-log entry.
|
|
64
|
+
*/
|
|
65
|
+
export const requiresSmartdataWriteTransactions = (dbArg) => isSmartdataWriteRevisionsActive(dbArg) || isSmartdataWriteLogActive(dbArg);
|
|
66
|
+
/** @internal The report a write adds to its transaction's entry, or `undefined` without an active log. */
|
|
67
|
+
export const writeLogReportOf = (dbArg, writeArg) => {
|
|
68
|
+
const fields = logFields.get(dbArg);
|
|
69
|
+
return fields !== undefined && isSmartdataWriteLogActive(dbArg)
|
|
70
|
+
? reportOfSmartdataWrite(writeArg, fields)
|
|
71
|
+
: undefined;
|
|
72
|
+
};
|
|
73
|
+
const logCollection = (mongoDbArg) => mongoDbArg.collection(writeLogCollectionName);
|
|
74
|
+
/**
|
|
75
|
+
* @internal Creates the index that expires entries, before `init()` publishes
|
|
76
|
+
* the connection. Another process creating it at the same time is fine.
|
|
77
|
+
*/
|
|
78
|
+
export const ensureSmartdataWriteLog = async (mongoDbArg, optionsArg = {}) => {
|
|
79
|
+
await logCollection(mongoDbArg).createIndex({ at: 1 }, {
|
|
80
|
+
name: expiryIndexName,
|
|
81
|
+
expireAfterSeconds: writeLogRetentionSeconds,
|
|
82
|
+
...optionsArg,
|
|
83
|
+
});
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* The receipt the engine journals a transaction's commit with. The driver
|
|
87
|
+
* numbers a transaction when it sends the transaction's first command.
|
|
88
|
+
*/
|
|
89
|
+
const receiptOfSession = (rawSessionArg) => {
|
|
90
|
+
const id = rawSessionArg.id;
|
|
91
|
+
if (id === undefined) {
|
|
92
|
+
throw new SmartdataPersistenceError('unsupported_operation', 'A SmartData transaction has no server session to key its write-log entry by.');
|
|
93
|
+
}
|
|
94
|
+
return `${id.id.toUUID().toHexString()}:${rawSessionArg.serverSession.txnNumber}`;
|
|
95
|
+
};
|
|
96
|
+
const receiptOfJournal = (sessionArg, transactionArg) => `${sessionArg}:${transactionArg}`;
|
|
97
|
+
/**
|
|
98
|
+
* The entry as written: a collection whose writes cannot be placed changed as
|
|
99
|
+
* a whole, so its reports add nothing, and the largest collections become
|
|
100
|
+
* unplaced until the entry fits its bounds.
|
|
101
|
+
*/
|
|
102
|
+
const boundWriteLogEntry = (reportsArg, unplacedArg) => {
|
|
103
|
+
const unplaced = new Set(unplacedArg);
|
|
104
|
+
const sizes = new Map();
|
|
105
|
+
let documents = 0;
|
|
106
|
+
let bytes = plugins.mongodb.BSON.calculateObjectSize({ unplaced: [...unplaced] });
|
|
107
|
+
for (const report of reportsArg) {
|
|
108
|
+
if (unplaced.has(report.collection))
|
|
109
|
+
continue;
|
|
110
|
+
const size = sizes.get(report.collection) ?? { documents: 0, bytes: 0 };
|
|
111
|
+
const reportDocuments = report.documents?.length ?? 0;
|
|
112
|
+
const reportBytes = plugins.mongodb.BSON.calculateObjectSize(report);
|
|
113
|
+
size.documents += reportDocuments;
|
|
114
|
+
size.bytes += reportBytes;
|
|
115
|
+
documents += reportDocuments;
|
|
116
|
+
bytes += reportBytes;
|
|
117
|
+
sizes.set(report.collection, size);
|
|
118
|
+
}
|
|
119
|
+
const bySize = [...sizes].sort(([, aArg], [, bArg]) => bArg.bytes - aArg.bytes);
|
|
120
|
+
for (const [collection, size] of bySize) {
|
|
121
|
+
if (documents <= maximumEntryDocuments && bytes <= maximumEntryBytes)
|
|
122
|
+
break;
|
|
123
|
+
unplaced.add(collection);
|
|
124
|
+
documents -= size.documents;
|
|
125
|
+
bytes -= size.bytes;
|
|
126
|
+
}
|
|
127
|
+
return Object.freeze({
|
|
128
|
+
reports: Object.freeze(reportsArg.filter((reportArg) => !unplaced.has(reportArg.collection))),
|
|
129
|
+
unplaced: Object.freeze([...unplaced].sort()),
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* @internal Writes a transaction's entry inside that transaction, right
|
|
134
|
+
* before its commit, and registers it until its outcome settled. `undefined`
|
|
135
|
+
* when the transaction wrote nothing through the models.
|
|
136
|
+
*/
|
|
137
|
+
export const flushSmartdataWriteLog = async (dbArg, mongoDbArg, rawSessionArg, reportsArg, unplacedArg, signalArg) => {
|
|
138
|
+
const fields = logFields.get(dbArg);
|
|
139
|
+
if (fields === undefined || (reportsArg.length === 0 && unplacedArg.size === 0))
|
|
140
|
+
return undefined;
|
|
141
|
+
const collection = logCollection(mongoDbArg);
|
|
142
|
+
const signalOptions = signalArg === undefined ? {} : { signal: signalArg };
|
|
143
|
+
if (reportsArg.length === 0) {
|
|
144
|
+
// No write of the attempt was acknowledged, so none may have numbered the transaction yet.
|
|
145
|
+
await collection.findOne({ _id: '' }, { projection: { _id: 1 }, session: rawSessionArg, ...signalOptions });
|
|
146
|
+
}
|
|
147
|
+
const receipt = receiptOfSession(rawSessionArg);
|
|
148
|
+
const entry = boundWriteLogEntry(reportsArg, unplacedArg);
|
|
149
|
+
await collection.insertOne({
|
|
150
|
+
_id: receipt,
|
|
151
|
+
at: new Date(),
|
|
152
|
+
fields: [...fields],
|
|
153
|
+
reports: [...entry.reports],
|
|
154
|
+
unplaced: [...entry.unplaced],
|
|
155
|
+
}, { session: rawSessionArg, ...signalOptions });
|
|
156
|
+
const { pending } = stateOf(dbArg);
|
|
157
|
+
const settled = Promise.withResolvers();
|
|
158
|
+
pending.set(receipt, settled.promise);
|
|
159
|
+
let open = true;
|
|
160
|
+
return {
|
|
161
|
+
receipt,
|
|
162
|
+
entry,
|
|
163
|
+
settle: () => {
|
|
164
|
+
if (!open)
|
|
165
|
+
return;
|
|
166
|
+
open = false;
|
|
167
|
+
if (pending.get(receipt) === settled.promise)
|
|
168
|
+
pending.delete(receipt);
|
|
169
|
+
settled.resolve();
|
|
170
|
+
},
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* @internal Delivers a confirmed commit of this database instance to its
|
|
175
|
+
* followers at once; each then skips the commit when the journal lists it.
|
|
176
|
+
*/
|
|
177
|
+
export const deliverSmartdataWriteLogCommit = (dbArg, commitArg) => {
|
|
178
|
+
const followers = logStates.get(dbArg)?.followers;
|
|
179
|
+
if (!followers)
|
|
180
|
+
return;
|
|
181
|
+
for (const follower of [...followers]) {
|
|
182
|
+
follower.deliverLocal(commitArg);
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
const isPlainRecord = (valueArg) => {
|
|
186
|
+
if (typeof valueArg !== 'object' || valueArg === null || Array.isArray(valueArg))
|
|
187
|
+
return false;
|
|
188
|
+
const prototype = Object.getPrototypeOf(valueArg);
|
|
189
|
+
return prototype === Object.prototype || prototype === null;
|
|
190
|
+
};
|
|
191
|
+
const unexpectedPage = () => new SmartdataPersistenceError('unsupported_operation', 'The engine answered getCommittedWrites with an unexpected page.');
|
|
192
|
+
const unsignedDecimal = /^(?:0|[1-9][0-9]*)$/;
|
|
193
|
+
const stampOf = (valueArg) => {
|
|
194
|
+
if (!isPlainRecord(valueArg)
|
|
195
|
+
|| typeof valueArg.timeline !== 'string'
|
|
196
|
+
|| typeof valueArg.sequence !== 'string'
|
|
197
|
+
|| !unsignedDecimal.test(valueArg.sequence)) {
|
|
198
|
+
throw unexpectedPage();
|
|
199
|
+
}
|
|
200
|
+
return Object.freeze({ timeline: valueArg.timeline, sequence: valueArg.sequence });
|
|
201
|
+
};
|
|
202
|
+
const committedWriteOf = (valueArg, databaseArg) => {
|
|
203
|
+
if (!isPlainRecord(valueArg) || !Array.isArray(valueArg.namespaces) || typeof valueArg.committedAtMs !== 'number') {
|
|
204
|
+
throw unexpectedPage();
|
|
205
|
+
}
|
|
206
|
+
const collections = new Set();
|
|
207
|
+
let logged = false;
|
|
208
|
+
for (const namespace of valueArg.namespaces) {
|
|
209
|
+
if (!isPlainRecord(namespace) || typeof namespace.database !== 'string' || typeof namespace.collection !== 'string') {
|
|
210
|
+
throw unexpectedPage();
|
|
211
|
+
}
|
|
212
|
+
if (namespace.database !== databaseArg)
|
|
213
|
+
continue;
|
|
214
|
+
if (namespace.collection === writeLogCollectionName)
|
|
215
|
+
logged = true;
|
|
216
|
+
if (!bookkeepingCollections.has(namespace.collection))
|
|
217
|
+
collections.add(namespace.collection);
|
|
218
|
+
}
|
|
219
|
+
const receipt = valueArg.receipt;
|
|
220
|
+
if (receipt !== null && receipt !== undefined && (!isPlainRecord(receipt) || typeof receipt.session !== 'string' || typeof receipt.transaction !== 'string')) {
|
|
221
|
+
throw unexpectedPage();
|
|
222
|
+
}
|
|
223
|
+
return {
|
|
224
|
+
stamp: stampOf(valueArg.stamp),
|
|
225
|
+
committedAtMs: valueArg.committedAtMs,
|
|
226
|
+
collections: [...collections].sort(),
|
|
227
|
+
// A retryable statement outside a transaction carries its number; only a
|
|
228
|
+
// transaction can have written an entry.
|
|
229
|
+
receipt: isPlainRecord(receipt) && (receipt.statement === null || receipt.statement === undefined)
|
|
230
|
+
? receiptOfJournal(receipt.session, receipt.transaction)
|
|
231
|
+
: undefined,
|
|
232
|
+
logged,
|
|
233
|
+
};
|
|
234
|
+
};
|
|
235
|
+
/** One page of the database's commit journal after `afterArg`, or its head without `afterArg`. */
|
|
236
|
+
const readCommittedWrites = async (mongoDbArg, afterArg, signalArg) => {
|
|
237
|
+
const page = await mongoDbArg.command({
|
|
238
|
+
getCommittedWrites: 1,
|
|
239
|
+
...(afterArg === undefined ? {} : { after: { timeline: afterArg.timeline, sequence: afterArg.sequence } }),
|
|
240
|
+
limit: pageLimit,
|
|
241
|
+
maximumBytes: pageBytes,
|
|
242
|
+
}, { timeoutMS: readTimeoutMs, ...(signalArg === undefined ? {} : { signal: signalArg }) });
|
|
243
|
+
if (!Array.isArray(page.writes) || typeof page.gap !== 'boolean' || typeof page.hasMore !== 'boolean') {
|
|
244
|
+
throw unexpectedPage();
|
|
245
|
+
}
|
|
246
|
+
return {
|
|
247
|
+
writes: page.writes.map((writeArg) => committedWriteOf(writeArg, mongoDbArg.databaseName)),
|
|
248
|
+
cursor: stampOf(page.cursor),
|
|
249
|
+
head: stampOf(page.head),
|
|
250
|
+
gap: page.gap,
|
|
251
|
+
hasMore: page.hasMore,
|
|
252
|
+
};
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* The entry a stored document states for a follower of `fieldsArg`, or
|
|
256
|
+
* `undefined` when it states none: a document another version wrote or one
|
|
257
|
+
* that does not record every field followed.
|
|
258
|
+
*/
|
|
259
|
+
const entryOfDocument = (documentArg, fieldsArg) => {
|
|
260
|
+
if (!isPlainRecord(documentArg))
|
|
261
|
+
return undefined;
|
|
262
|
+
const { fields, reports, unplaced } = documentArg;
|
|
263
|
+
if (!Array.isArray(fields)
|
|
264
|
+
|| !fieldsArg.every((fieldArg) => fields.includes(fieldArg))
|
|
265
|
+
|| !Array.isArray(unplaced)
|
|
266
|
+
|| !unplaced.every((collectionArg) => typeof collectionArg === 'string')
|
|
267
|
+
|| !Array.isArray(reports)
|
|
268
|
+
|| !reports.every((reportArg) => isPlainRecord(reportArg)
|
|
269
|
+
&& typeof reportArg.collection === 'string'
|
|
270
|
+
&& writeKinds.has(reportArg.kind)
|
|
271
|
+
&& isPlainRecord(reportArg.anchors)
|
|
272
|
+
&& (reportArg.documents === null
|
|
273
|
+
|| (Array.isArray(reportArg.documents) && reportArg.documents.every(isPlainRecord))))) {
|
|
274
|
+
return undefined;
|
|
275
|
+
}
|
|
276
|
+
return { reports: reports, unplaced: unplaced };
|
|
277
|
+
};
|
|
278
|
+
const describeError = (errorArg) => errorArg instanceof Error ? errorArg.message : String(errorArg);
|
|
279
|
+
/** Resolves after `millisecondsArg`, or at once when `signalArg` aborts. */
|
|
280
|
+
const pause = (millisecondsArg, signalArg) => new Promise((resolveArg) => {
|
|
281
|
+
if (signalArg.aborted)
|
|
282
|
+
return resolveArg();
|
|
283
|
+
const done = () => {
|
|
284
|
+
clearTimeout(timer);
|
|
285
|
+
signalArg.removeEventListener('abort', done);
|
|
286
|
+
resolveArg();
|
|
287
|
+
};
|
|
288
|
+
const timer = setTimeout(done, millisecondsArg);
|
|
289
|
+
signalArg.addEventListener('abort', done, { once: true });
|
|
290
|
+
});
|
|
291
|
+
/** Resolves when `promiseArg` settles, or at once when `signalArg` aborts. */
|
|
292
|
+
const settledOrAborted = (promiseArg, signalArg) => new Promise((resolveArg) => {
|
|
293
|
+
if (signalArg.aborted)
|
|
294
|
+
return resolveArg();
|
|
295
|
+
const done = () => {
|
|
296
|
+
signalArg.removeEventListener('abort', done);
|
|
297
|
+
resolveArg();
|
|
298
|
+
};
|
|
299
|
+
signalArg.addEventListener('abort', done, { once: true });
|
|
300
|
+
void promiseArg.finally(done);
|
|
301
|
+
});
|
|
302
|
+
class SmartdataWriteFollower {
|
|
303
|
+
state;
|
|
304
|
+
fields;
|
|
305
|
+
onWrite;
|
|
306
|
+
onGap;
|
|
307
|
+
/** The receipts of local deliveries, by when they were delivered, until the journal lists them. */
|
|
308
|
+
delivered = new Map();
|
|
309
|
+
controller = new AbortController();
|
|
310
|
+
running = Promise.resolve();
|
|
311
|
+
stopping;
|
|
312
|
+
constructor(state, fields, onWrite, onGap) {
|
|
313
|
+
this.state = state;
|
|
314
|
+
this.fields = fields;
|
|
315
|
+
this.onWrite = onWrite;
|
|
316
|
+
this.onGap = onGap;
|
|
317
|
+
}
|
|
318
|
+
start(mongoDbArg, cursorArg) {
|
|
319
|
+
this.state.followers.add(this);
|
|
320
|
+
this.running = this.follow(mongoDbArg, cursorArg);
|
|
321
|
+
}
|
|
322
|
+
stop() {
|
|
323
|
+
this.stopping ??= (async () => {
|
|
324
|
+
this.state.followers.delete(this);
|
|
325
|
+
this.controller.abort();
|
|
326
|
+
await this.running;
|
|
327
|
+
})();
|
|
328
|
+
return this.stopping;
|
|
329
|
+
}
|
|
330
|
+
deliverLocal(commitArg) {
|
|
331
|
+
if (this.controller.signal.aborted)
|
|
332
|
+
return;
|
|
333
|
+
this.delivered.set(commitArg.receipt, Date.now());
|
|
334
|
+
this.deliver('local', null, null, commitArg.entry.reports, commitArg.entry.unplaced);
|
|
335
|
+
}
|
|
336
|
+
deliver(originArg, stampArg, committedAtMsArg, reportsArg, unplacedArg) {
|
|
337
|
+
const write = Object.freeze({
|
|
338
|
+
origin: originArg,
|
|
339
|
+
stamp: stampArg,
|
|
340
|
+
committedAtMs: committedAtMsArg,
|
|
341
|
+
reports: Object.freeze(reportsArg.map((reportArg) => narrowSmartdataWriteReport(reportArg, this.fields))),
|
|
342
|
+
unplaced: Object.freeze([...unplacedArg]),
|
|
343
|
+
});
|
|
344
|
+
try {
|
|
345
|
+
this.onWrite(write);
|
|
346
|
+
}
|
|
347
|
+
catch (errorArg) {
|
|
348
|
+
logger.log('error', `A SmartData write follower threw on a commit: ${describeError(errorArg)}`);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
reportGap(reasonArg) {
|
|
352
|
+
try {
|
|
353
|
+
this.onGap(Object.freeze({ reason: reasonArg }));
|
|
354
|
+
}
|
|
355
|
+
catch (errorArg) {
|
|
356
|
+
logger.log('error', `A SmartData write follower threw on a gap: ${describeError(errorArg)}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Reads the journal from `cursorArg` on until stopped. A failed read is
|
|
361
|
+
* retried from the same position; a position the journal lost reports a gap
|
|
362
|
+
* and continues from its head.
|
|
363
|
+
*/
|
|
364
|
+
async follow(mongoDbArg, cursorArg) {
|
|
365
|
+
const signal = this.controller.signal;
|
|
366
|
+
let cursor = cursorArg;
|
|
367
|
+
let lastCommitAt = Number.NEGATIVE_INFINITY;
|
|
368
|
+
let failing = false;
|
|
369
|
+
while (!signal.aborted) {
|
|
370
|
+
try {
|
|
371
|
+
let page;
|
|
372
|
+
do {
|
|
373
|
+
page = await readCommittedWrites(mongoDbArg, cursor, signal);
|
|
374
|
+
if (page.gap) {
|
|
375
|
+
// A position beyond the head belongs to a history the journal no longer continues.
|
|
376
|
+
const reason = page.head.timeline !== cursor.timeline
|
|
377
|
+
|| BigInt(cursor.sequence) > BigInt(page.head.sequence) ? 'timeline' : 'retention';
|
|
378
|
+
cursor = (await readCommittedWrites(mongoDbArg, undefined, signal)).cursor;
|
|
379
|
+
this.reportGap(reason);
|
|
380
|
+
break;
|
|
381
|
+
}
|
|
382
|
+
const committed = await this.deliverPage(mongoDbArg, page.writes, signal);
|
|
383
|
+
if (signal.aborted)
|
|
384
|
+
return;
|
|
385
|
+
if (committed)
|
|
386
|
+
lastCommitAt = performance.now();
|
|
387
|
+
cursor = page.cursor;
|
|
388
|
+
} while (page.hasMore && !signal.aborted);
|
|
389
|
+
if (failing) {
|
|
390
|
+
failing = false;
|
|
391
|
+
logger.log('info', `A SmartData write follower of "${mongoDbArg.databaseName}" reads the commit journal again.`);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
catch (errorArg) {
|
|
395
|
+
if (signal.aborted)
|
|
396
|
+
return;
|
|
397
|
+
if (!failing) {
|
|
398
|
+
failing = true;
|
|
399
|
+
logger.log('warn', `A SmartData write follower of "${mongoDbArg.databaseName}" failed to read the commit journal and retries: ${describeError(errorArg)}`);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
this.forgetOldDeliveries();
|
|
403
|
+
await pause(performance.now() - lastCommitAt < activeWindowMs ? activePollMs : idlePollMs, signal);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* Delivers one page's commits in order and returns whether it held any.
|
|
408
|
+
* A commit of this database instance still settling is waited for, so the
|
|
409
|
+
* follower knows whether it was delivered locally. Every read happens before
|
|
410
|
+
* the first delivery, so a failed read delivers nothing and is retried with
|
|
411
|
+
* the page.
|
|
412
|
+
*/
|
|
413
|
+
async deliverPage(mongoDbArg, writesArg, signalArg) {
|
|
414
|
+
const writes = writesArg.filter((writeArg) => writeArg.collections.length > 0 || (writeArg.logged && writeArg.receipt !== undefined));
|
|
415
|
+
if (writes.length === 0)
|
|
416
|
+
return false;
|
|
417
|
+
const receipts = writes.flatMap((writeArg) => writeArg.receipt === undefined ? [] : [writeArg.receipt]);
|
|
418
|
+
for (const receipt of receipts) {
|
|
419
|
+
const pending = this.state.pending.get(receipt);
|
|
420
|
+
if (pending)
|
|
421
|
+
await settledOrAborted(pending, signalArg);
|
|
422
|
+
}
|
|
423
|
+
if (signalArg.aborted)
|
|
424
|
+
return true;
|
|
425
|
+
const lookups = receipts.filter((receiptArg) => !this.delivered.has(receiptArg));
|
|
426
|
+
const documents = new Map();
|
|
427
|
+
if (lookups.length > 0) {
|
|
428
|
+
const found = await logCollection(mongoDbArg).find({ _id: { $in: lookups } }, { projection: { at: 0 }, timeoutMS: readTimeoutMs, signal: signalArg }).toArray();
|
|
429
|
+
for (const document of found)
|
|
430
|
+
documents.set(document._id, document);
|
|
431
|
+
}
|
|
432
|
+
for (const write of writes) {
|
|
433
|
+
if (signalArg.aborted)
|
|
434
|
+
return true;
|
|
435
|
+
if (write.receipt !== undefined && this.delivered.delete(write.receipt))
|
|
436
|
+
continue;
|
|
437
|
+
const entry = write.receipt === undefined
|
|
438
|
+
? undefined
|
|
439
|
+
: entryOfDocument(documents.get(write.receipt), this.fields);
|
|
440
|
+
const unplaced = new Set(entry?.unplaced);
|
|
441
|
+
const covered = new Set([...unplaced, ...(entry?.reports ?? []).map((reportArg) => reportArg.collection)]);
|
|
442
|
+
for (const collection of write.collections) {
|
|
443
|
+
if (!covered.has(collection))
|
|
444
|
+
unplaced.add(collection);
|
|
445
|
+
}
|
|
446
|
+
this.deliver('journal', write.stamp, write.committedAtMs, entry?.reports ?? [], [...unplaced].sort());
|
|
447
|
+
}
|
|
448
|
+
return true;
|
|
449
|
+
}
|
|
450
|
+
forgetOldDeliveries() {
|
|
451
|
+
const oldest = Date.now() - localDeliveryMemoryMs;
|
|
452
|
+
for (const [receipt, deliveredAt] of this.delivered) {
|
|
453
|
+
if (deliveredAt < oldest)
|
|
454
|
+
this.delivered.delete(receipt);
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
/**
|
|
459
|
+
* @internal Starts a follower at the journal's head; `db.followWrites()`
|
|
460
|
+
* documents it.
|
|
461
|
+
*/
|
|
462
|
+
export const followSmartdataWrites = async (dbArg, optionsArg) => {
|
|
463
|
+
const { fields, onWrite, onGap } = assertDataOptions(optionsArg, ['fields', 'onWrite', 'onGap']);
|
|
464
|
+
const followed = normalizeObservedFields(fields, 'A SmartData write follower');
|
|
465
|
+
if (typeof onWrite !== 'function' || typeof onGap !== 'function') {
|
|
466
|
+
throw new SmartdataPersistenceError('invalid_argument', 'A SmartData write follower requires onWrite and onGap functions.');
|
|
467
|
+
}
|
|
468
|
+
const logged = logFields.get(dbArg);
|
|
469
|
+
if (logged === undefined) {
|
|
470
|
+
throw new SmartdataPersistenceError('unsupported_operation', 'Following writes requires the writeLog option of the SmartdataDb.');
|
|
471
|
+
}
|
|
472
|
+
if (!followed.every((fieldArg) => logged.includes(fieldArg))) {
|
|
473
|
+
throw new SmartdataPersistenceError('invalid_argument', 'A SmartData write follower follows fields of the write log only.');
|
|
474
|
+
}
|
|
475
|
+
const mongoDb = dbArg.mongoDb;
|
|
476
|
+
if (dbArg.status !== 'connected' || !mongoDb) {
|
|
477
|
+
throw new SmartdataPersistenceError('unsupported_operation', 'Following writes requires a connected SmartdataDb.');
|
|
478
|
+
}
|
|
479
|
+
if (!isSmartdataWriteLogActive(dbArg)) {
|
|
480
|
+
throw new SmartdataPersistenceError('unsupported_operation', 'Following writes requires NoSQLDB 10.9.0 or newer, whose commit journal orders the commits.');
|
|
481
|
+
}
|
|
482
|
+
const tail = await readCommittedWrites(mongoDb, undefined);
|
|
483
|
+
if (dbArg.status !== 'connected' || dbArg.mongoDb !== mongoDb) {
|
|
484
|
+
throw new SmartdataPersistenceError('unsupported_operation', 'The SmartdataDb closed while the write follower started.');
|
|
485
|
+
}
|
|
486
|
+
const follower = new SmartdataWriteFollower(stateOf(dbArg), followed, onWrite, onGap);
|
|
487
|
+
follower.start(mongoDb, tail.cursor);
|
|
488
|
+
return Object.freeze({ stop: () => follower.stop() });
|
|
489
|
+
};
|
|
490
|
+
/** @internal Stops every follower of the database; `close()` calls it. */
|
|
491
|
+
export const stopSmartdataWriteFollowers = async (dbArg) => {
|
|
492
|
+
const followers = logStates.get(dbArg)?.followers;
|
|
493
|
+
if (!followers)
|
|
494
|
+
return;
|
|
495
|
+
await Promise.all([...followers].map((followerArg) => followerArg.stop()));
|
|
496
|
+
};
|
|
497
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy53cml0ZWxvZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3RzL25vc3FsZGIvY2xhc3Nlcy53cml0ZWxvZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEtBQUssT0FBTyxNQUFNLGNBQWMsQ0FBQztBQUd4QyxPQUFPLEVBQUUsaUJBQWlCLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUNqRSxPQUFPLEVBQUUseUJBQXlCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUNyRSxPQUFPLEVBQ0wsMEJBQTBCLEVBQzFCLHVCQUF1QixFQUN2QixzQkFBc0IsR0FHdkIsTUFBTSw0QkFBNEIsQ0FBQztBQUNwQyxPQUFPLEVBQUUsK0JBQStCLEVBQUUsNEJBQTRCLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUM1RyxPQUFPLEVBQUUsc0JBQXNCLEVBQUUsTUFBTSxxQkFBcUIsQ0FBQztBQUM3RCxPQUFPLEVBQUUsTUFBTSxFQUFFLE1BQU0sY0FBYyxDQUFDO0FBK0V0Qyw2Q0FBNkM7QUFDN0MsTUFBTSxDQUFDLE1BQU0sc0JBQXNCLEdBQUcsdUJBQXVCLENBQUM7QUFFOUQsMkZBQTJGO0FBQzNGLE1BQU0sQ0FBQyxNQUFNLHdCQUF3QixHQUFHLEVBQUUsR0FBRyxFQUFFLENBQUM7QUFDaEQsTUFBTSxlQUFlLEdBQUcsNEJBQTRCLENBQUM7QUFDckQsTUFBTSxZQUFZLEdBQUcsR0FBRyxDQUFDO0FBQ3pCLE1BQU0sVUFBVSxHQUFHLEtBQUssQ0FBQztBQUN6QiwyRUFBMkU7QUFDM0UsTUFBTSxjQUFjLEdBQUcsTUFBTSxDQUFDO0FBQzlCLE1BQU0sU0FBUyxHQUFHLEdBQUcsQ0FBQztBQUN0Qix1RkFBdUY7QUFDdkYsTUFBTSxTQUFTLEdBQUcsQ0FBQyxHQUFHLElBQUksR0FBRyxJQUFJLENBQUM7QUFDbEMsTUFBTSxhQUFhLEdBQUcsS0FBSyxDQUFDO0FBQzVCLHdGQUF3RjtBQUN4RixNQUFNLHFCQUFxQixHQUFHLEtB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
|
|
@@ -59,10 +59,23 @@ export interface ISmartdataWrite {
|
|
|
59
59
|
*/
|
|
60
60
|
previousPartitionsRead?: true;
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* @internal Validates the top-level field names a report carries: 1 to 64
|
|
64
|
+
* distinct names, no `.` and no leading `$`. `subjectArg` names what requires
|
|
65
|
+
* them in the refusal.
|
|
66
|
+
*/
|
|
67
|
+
export declare const normalizeObservedFields: (fieldsArg: unknown, subjectArg: string) => readonly string[];
|
|
62
68
|
/** @internal Registers a write observer; the returned function removes it. */
|
|
63
69
|
export declare const addSmartdataWriteObserver: (dbArg: SmartdataDb, optionsArg: ISmartdataWriteObserverOptions) => (() => void);
|
|
64
70
|
/** @internal */
|
|
65
71
|
export declare const hasSmartdataWriteObservers: (dbArg: SmartdataDb) => boolean;
|
|
72
|
+
/**
|
|
73
|
+
* @internal The report of one acknowledged write with the values of `fieldsArg`,
|
|
74
|
+
* copied now and frozen.
|
|
75
|
+
*/
|
|
76
|
+
export declare const reportOfSmartdataWrite: (writeArg: ISmartdataWrite, fieldsArg: readonly string[]) => ISmartdataWriteReport;
|
|
77
|
+
/** @internal The same report with only the values of `fieldsArg`, which the report's fields include. */
|
|
78
|
+
export declare const narrowSmartdataWriteReport: (reportArg: ISmartdataWriteReport, fieldsArg: readonly string[]) => ISmartdataWriteReport;
|
|
66
79
|
/**
|
|
67
80
|
* @internal Captures one acknowledged write for the observers registered now,
|
|
68
81
|
* and returns the delivery, or `undefined` when nobody observes the database.
|