@procedures/mongo 0.54.0 → 0.54.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/CHANGELOG.md +18 -0
- package/README.md +6 -0
- package/dist/storage/document-storage.d.ts +42 -1
- package/dist/storage/document-storage.d.ts.map +1 -1
- package/dist/storage/document-storage.js +125 -14
- package/dist/storage/document-storage.js.map +1 -1
- package/dist/storage/materializer.d.ts +1 -1
- package/dist/storage/materializer.d.ts.map +1 -1
- package/dist/storage/materializer.js +5 -4
- package/dist/storage/materializer.js.map +1 -1
- package/dist/storage/own-writes.d.ts +10 -4
- package/dist/storage/own-writes.d.ts.map +1 -1
- package/dist/storage/own-writes.js +13 -5
- package/dist/storage/own-writes.js.map +1 -1
- package/dist/storage/storage-hints.js +1 -1
- package/dist/storage/storage-hints.js.map +1 -1
- package/package.json +6 -5
- package/src/storage/document-storage.ts +160 -19
- package/src/storage/materializer.ts +6 -4
- package/src/storage/own-writes.ts +22 -9
- package/src/storage/storage-hints.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# @procedures/mongo
|
|
2
2
|
|
|
3
|
+
## 0.54.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 3914b5f: `MongoDocumentStorage` has the new option `sequence` (CLI-96). The default `collection` keeps one counter of the numbers for the collection. With `partition`, each partition has its own counter and its own write queue, so the writes of two partitions share no document.
|
|
8
|
+
|
|
9
|
+
- The gain is for the other partitions. One tenant wrote a burst in the CI tests of CLI-96. The p99 of the writes of the other tenants went from 316 to 758 ms to 12 to 51 ms.
|
|
10
|
+
- The rate of one partition does not change: approximately 400 writes a second. The stated limit of a partition is 200 writes a second.
|
|
11
|
+
- A live collection gains nothing. A store with the option refuses a collection with numbers on its counter of the collection, with a clear error. Use the option for a new collection. The move of a live collection is a step with all processes stopped.
|
|
12
|
+
- A store with the default refuses a collection whose stores have the option. Give the same `sequence` to each store of a collection, and to the storage of a view target.
|
|
13
|
+
- A first load of many tenants does not hold each schedule on each CPU model of the runners. The tail of the other tenants is the claim. A host that wants a warm pool for a first load sets `minPoolSize` of its client.
|
|
14
|
+
- The new option `writesInFlight` bounds the writes of each client at one time, over the stores with the same value. The default is 8. Before it, each partition of a process kept a transaction open at once, and the server queue reached 152 tickets with 20,000 partitions.
|
|
15
|
+
- The bound covers each store of a client, also with the default `sequence`. A client that writes more than 8 collections at once gets a queue for the permits. Set `writesInFlight: Infinity` to keep no bound.
|
|
16
|
+
- A watcher notes the writes of this process that it skips with their partition, because two partitions can have the same number.
|
|
17
|
+
- Updated dependencies [13c3a8b]
|
|
18
|
+
- @procedures/client@0.54.1
|
|
19
|
+
- @procedures/collections@0.54.1
|
|
20
|
+
|
|
3
21
|
## 0.54.0
|
|
4
22
|
|
|
5
23
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -666,6 +666,7 @@ server.usePartitions("notes", { storageOf: (partition) => notes.forPartition(par
|
|
|
666
666
|
| `maxTombstones` | The most deletes that the change log keeps in each partition (default 10000) |
|
|
667
667
|
| `queryPolicy` | The query policy of `query()` without a policy of the call |
|
|
668
668
|
| `counters` | The collection of the sequence counters (default `__storage_counters`) |
|
|
669
|
+
| `sequence` | `collection` (the default): one counter of the numbers for the collection. `partition`: one counter and one write queue for each partition (roadmap CLI-96). The next section tells when to use it. |
|
|
669
670
|
| `writeLease` | The time after which a pending number of a write that stopped no longer holds the readers (default `seconds(60)`). A plain number is a `TypeError`. |
|
|
670
671
|
| `client` | The client of the database, for the transactions. The default is the client of the default connection when `db` is absent. With `db` and no `client`, a write is not a transaction, also on a replica set: it uses the watermark. |
|
|
671
672
|
| `transactions` | `auto` (the default): a transaction for each write on a replica set or a sharded cluster. `never`: the watermark only. |
|
|
@@ -677,6 +678,11 @@ server.usePartitions("notes", { storageOf: (partition) => notes.forPartition(par
|
|
|
677
678
|
- **The change log.** Each write gets the next number of the sequence of the collection, and a delete leaves a tombstone. `getChangesSince()` thus gives the exact changes and deletes. A version of another epoch, or a version before the oldest dropped tombstone, gives a full result.
|
|
678
679
|
- **The versions.** `versionOf(id)` gives the number of the last write of an item.
|
|
679
680
|
- **The queries.** `query()` sends the equality of a string, a number or a boolean, and `$in` of them, to MongoDB as a filter. `queryEntries()` of the core then gives the exact result, so a query gives the result of the reference.
|
|
681
|
+
- **A counter for each partition.** With `sequence: "partition"`, the writes of two partitions share no document, so they do not wait for each other. The numbers of two partitions can be equal. A store reads only its partition, so the numbers stay in order in each partition.
|
|
682
|
+
- Give the same `sequence` to each store of a collection, and to the storage of a view target. A store refuses a collection that has the other sequence, with a clear error.
|
|
683
|
+
- Use the option only for a new collection. A store with the option refuses a collection with numbers on its counter of the collection. Thus a live collection gains nothing. Its move is a step with all processes stopped.
|
|
684
|
+
- One partition keeps the limit of one collection with the default: approximately 400 writes a second. The stated limit of a partition is 200 writes a second.
|
|
685
|
+
- The gain is for the other partitions. One tenant wrote a burst in the CI tests of CLI-96. The p99 of the writes of the other tenants went from 316 to 758 ms to 12 to 51 ms.
|
|
680
686
|
- **Several writers.** On a replica set, a write takes its numbers and writes in one transaction. On a single server, a watermark keeps the log exact. Each write keeps its numbers pending until it ends, and a version stops below the lowest pending number. A write that ends after its lease moves its documents to new numbers.
|
|
681
687
|
- **The cases that stay on a single server.** A process that stops between a late write and the move of its documents leaves that change below the watermark. A reader that passed those numbers does not get the change. And a precondition (`expectedVersion`) of a document without `_seq` has the guard `{ _seq: { $exists: false } }`: a writer outside the storage between the test and the write is not seen. On a replica set, the test and the write are one transaction, and these cases do not occur.
|
|
682
688
|
- **The partitions.** An id cannot have the character U+001F, and each write filter has the partition. A write cannot change a document of another partition.
|
|
@@ -166,6 +166,26 @@ export interface MongoDocumentStorageOptions<T = unknown> {
|
|
|
166
166
|
queryPolicy?: StorageQueryPolicy | undefined;
|
|
167
167
|
/** The collection of the sequence counters (default `__storage_counters`). */
|
|
168
168
|
counters?: string | undefined;
|
|
169
|
+
/**
|
|
170
|
+
* The sequence of the numbers of the writes (roadmap CLI-96). `collection` (the default): one counter for the collection.
|
|
171
|
+
* `partition`: one counter and one write queue for each partition, so the writes of two partitions share no document.
|
|
172
|
+
*
|
|
173
|
+
* - Give the same value to each store of a collection. A store refuses a collection that has the other sequence, with a clear error.
|
|
174
|
+
* - A store with `partition` refuses a collection with numbers on its counter of the collection. Use it for a new collection.
|
|
175
|
+
* - One partition keeps the limit of one collection with the default: approximately 400 writes a second, and a stated limit of 200.
|
|
176
|
+
*/
|
|
177
|
+
sequence?: "collection" | "partition" | undefined;
|
|
178
|
+
/**
|
|
179
|
+
* The most writes of each client at one time, over the stores with the same value (roadmap CLI-96). The default is 8.
|
|
180
|
+
* A write waits for a permit after its turn in the write queue of its counter.
|
|
181
|
+
*
|
|
182
|
+
* - With `sequence: "partition"`, each partition has its own queue. Without this bound, each partition of a process has a transaction open at once.
|
|
183
|
+
* - A whole number of 1 or more, or `Infinity` for no bound. Another value is a `RangeError`.
|
|
184
|
+
* - The sweeps of 2, 4, 8, 16 and `Infinity` at the bench cases `400-p2` and `4000-p2` set the default of 8.
|
|
185
|
+
* - With `sequence: "partition"`, `Infinity` costs approximately 30 % of the writes a second. The bench run 42428048767
|
|
186
|
+
* gives 638.5 against 916.5 of the default sequence at `4000-p2`. The store emits a process warning one time for it.
|
|
187
|
+
*/
|
|
188
|
+
writesInFlight?: number | undefined;
|
|
169
189
|
/**
|
|
170
190
|
* The time after which a pending number of a write no longer holds the watermark (default 60 s):
|
|
171
191
|
* a writer that stopped does not stop the change reads. A plain number is a `TypeError`.
|
|
@@ -196,6 +216,10 @@ export interface MongoDocumentStorageOptions<T = unknown> {
|
|
|
196
216
|
*/
|
|
197
217
|
form?: StoredForm | undefined;
|
|
198
218
|
}
|
|
219
|
+
/** The default of the option `writesInFlight`: the most writes of each client at one time (roadmap CLI-96). */
|
|
220
|
+
export declare const DEFAULT_WRITES_IN_FLIGHT = 8;
|
|
221
|
+
/** The code of the process warning for a store with `sequence: "partition"` and no bound on its writes. */
|
|
222
|
+
export declare const UNBOUNDED_PARTITION_WARNING = "PROCEDURES_MONGO_UNBOUNDED_PARTITION";
|
|
199
223
|
/** The options of `decodeStoredDocument`. */
|
|
200
224
|
export interface DecodeStoredDocumentOptions {
|
|
201
225
|
/** The stored form of the storage, for example `storage.form` (default `DEFAULT_STORED_FORM`). */
|
|
@@ -363,6 +387,8 @@ export interface StorageWriteTarget {
|
|
|
363
387
|
withPendingNumbers<R>(db: Db, count: number, write: (first: number) => Promise<R>): Promise<R>;
|
|
364
388
|
/** This method writes the floor document of the partition once (roadmap O66). */
|
|
365
389
|
noteFloor(db: Db): Promise<void>;
|
|
390
|
+
/** The sequence of the numbers: one counter for the collection, or one for each partition (roadmap CLI-96). */
|
|
391
|
+
readonly sequence: "collection" | "partition";
|
|
366
392
|
/** This method drops the oldest tombstones over the limit, and raises the floor of the change log. */
|
|
367
393
|
prune(db: Db): Promise<void>;
|
|
368
394
|
}
|
|
@@ -400,6 +426,8 @@ export declare class MongoDocumentStorage<T> implements CollectionStorage<T> {
|
|
|
400
426
|
private readonly maxTombstones;
|
|
401
427
|
private readonly countersName;
|
|
402
428
|
private readonly writeLeaseMs;
|
|
429
|
+
/** The most writes of each client at one time, over the stores with this value. `Infinity` is no bound. */
|
|
430
|
+
private readonly writesInFlight;
|
|
403
431
|
/** The storages of the partitions, for `forPartition`: the same storage for the same partition. */
|
|
404
432
|
private readonly partitions;
|
|
405
433
|
/** The stored form of the documents (roadmap CLI-76). A `ViewMaterializer` of the collection takes the same form. */
|
|
@@ -413,6 +441,12 @@ export declare class MongoDocumentStorage<T> implements CollectionStorage<T> {
|
|
|
413
441
|
private indexes;
|
|
414
442
|
/** True after this storage wrote (or found) the floor document of its partition (roadmap O66). A failed write leaves it false. */
|
|
415
443
|
private floorNoted;
|
|
444
|
+
/** The sequence of the numbers (roadmap CLI-96). */
|
|
445
|
+
private readonly sequence;
|
|
446
|
+
/** The `_id` of the counter of the numbers: the collection, or the partition with the option `sequence: "partition"`. */
|
|
447
|
+
private readonly counterId;
|
|
448
|
+
/** The partition of the notes of the own numbers with a counter for each partition, else `undefined` (roadmap CLI-96). */
|
|
449
|
+
private readonly ownScope;
|
|
416
450
|
private closed;
|
|
417
451
|
constructor(options: MongoDocumentStorageOptions<T>, partitions?: Map<string, MongoDocumentStorage<T>>);
|
|
418
452
|
/** The storage of a partition of the same collection. It gives the same storage for the same partition. */
|
|
@@ -480,9 +514,16 @@ export declare class MongoDocumentStorage<T> implements CollectionStorage<T> {
|
|
|
480
514
|
/**
|
|
481
515
|
* The watermark of the collection: the highest number below each pending number of a write whose
|
|
482
516
|
* lease has not ended, the epoch, and the floor of the partition. Each number up to it is written
|
|
483
|
-
* or abandoned. One command reads them. Only the first read of a
|
|
517
|
+
* or abandoned. One command reads them. Only the first read of a store writes: it makes the counter. With the
|
|
518
|
+
* option `sequence: "partition"`, the first read of each process also marks the counter of the collection.
|
|
484
519
|
*/
|
|
485
520
|
private watermark;
|
|
521
|
+
/**
|
|
522
|
+
* This method checks once for each collection that a store with the option `sequence: "partition"` can use it (roadmap
|
|
523
|
+
* CLI-96). One atomic step marks the counter of the collection, unless that counter has numbers. A store with the default
|
|
524
|
+
* reads the mark in its own steps on that counter, so it needs no check of its own.
|
|
525
|
+
*/
|
|
526
|
+
private checkSequence;
|
|
486
527
|
/** The id of the document of the oldest kept change of the partition. */
|
|
487
528
|
private floorOf;
|
|
488
529
|
private floorId;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"document-storage.d.ts","sourceRoot":"","sources":["../../src/storage/document-storage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiGG;AAIH,OAAO,EAAyE,KAAK,EAAE,EAAE,KAAK,QAAQ,EAAe,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACvJ,OAAO,EAML,KAAK,iBAAiB,EAEtB,KAAK,wBAAwB,EAC7B,KAAK,cAAc,EACnB,KAAK,uBAAuB,EAC5B,KAAK,eAAe,EACpB,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EAEzB,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAGrE,OAAO,EAAqE,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAiB7H,mEAAmE;AACnE,eAAO,MAAM,SAAS,WAAW,CAAC;AAElC;;;;GAIG;AACH,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAA+E,CAAC;AAErI,8HAA8H;AAC9H,eAAO,MAAM,eAAe,EAAE,OAAc,CAAC;AAE7C;;;;GAIG;AACH,MAAM,WAAW,UAAU;IACzB;;;OAGG;IACH,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,EAAE,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;IAC7D,kHAAkH;IAClH,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAAC;IAC3E;;;;;;;OAOG;IACH,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAClF;;;;;;;OAOG;IACH,WAAW,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAC;CAChD;AAED;;;;GAIG;AACH,eAAO,MAAM,mBAAmB,EAAE,UAmBhC,CAAC;AAEH,+CAA+C;AAC/C,MAAM,WAAW,2BAA2B,CAAC,CAAC,GAAG,OAAO;IACtD,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB;;;;OAIG;IACH,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IAC1C,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0DAA0D;IAC1D,aAAa,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,kEAAkE;IAClE,WAAW,CAAC,EAAE,kBAAkB,GAAG,SAAS,CAAC;IAC7C,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC9B;;;OAGG;IACH,UAAU,CAAC,EAAE,QAAQ,GAAG,SAAS,CAAC;IAClC;;;;OAIG;IACH,MAAM,CAAC,EAAE,CAAC,MAAM,WAAW,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC,GAAG,SAAS,CAAC;IAChE;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC;IAC5C;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,KAAK,MAAM,GAAG,SAAS,CAAC,GAAG,SAAS,CAAC;IAC1D;;;OAGG;IACH,IAAI,CAAC,EAAE,UAAU,GAAG,SAAS,CAAC;CAC/B;
|
|
1
|
+
{"version":3,"file":"document-storage.d.ts","sourceRoot":"","sources":["../../src/storage/document-storage.ts"],"names":[],"mappings":"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"}
|
|
@@ -170,6 +170,18 @@ function duplicateIndex(error) {
|
|
|
170
170
|
const stopped = [writeErrors].flat().find((entry) => entry?.code === 11000);
|
|
171
171
|
return typeof stopped?.index === "number" ? stopped.index : undefined;
|
|
172
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* The checks of the sequence of each collection (roadmap CLI-96): by the client, then by the names of the database, the
|
|
175
|
+
* counters and the collection. A check that fails leaves no entry, so the next call tries again.
|
|
176
|
+
*/
|
|
177
|
+
const SEQUENCE_CHECKS = new WeakMap();
|
|
178
|
+
/** The error of a store whose sequence is not the sequence of its collection (roadmap CLI-96). */
|
|
179
|
+
function sequenceMismatch(collection, sequence) {
|
|
180
|
+
return sequence === "partition"
|
|
181
|
+
? new Error(`The collection ${collection} has numbers on its counter of the collection, so the option sequence "partition" refuses it. ` +
|
|
182
|
+
"The option is for a collection with no writes. The move of a live collection is a step with all processes stopped.")
|
|
183
|
+
: new Error(`The collection ${collection} has a counter for each partition. Give the option sequence "partition" to each store of it.`);
|
|
184
|
+
}
|
|
173
185
|
/** True when a client is on a replica set or a sharded cluster: it has transactions. */
|
|
174
186
|
const TRANSACTIONAL = new WeakMap();
|
|
175
187
|
function hasTransactions(db) {
|
|
@@ -198,6 +210,31 @@ const MOST_FULL_TRIES = 3;
|
|
|
198
210
|
const QUEUES = new WeakMap();
|
|
199
211
|
/** The change reads of each client at one time. A burst of reads leaves the pool to the other operations. */
|
|
200
212
|
const READS = new WeakMap();
|
|
213
|
+
/** The default of the option `writesInFlight`: the most writes of each client at one time (roadmap CLI-96). */
|
|
214
|
+
export const DEFAULT_WRITES_IN_FLIGHT = 8;
|
|
215
|
+
/** The code of the process warning for a store with `sequence: "partition"` and no bound on its writes. */
|
|
216
|
+
export const UNBOUNDED_PARTITION_WARNING = "PROCEDURES_MONGO_UNBOUNDED_PARTITION";
|
|
217
|
+
let warnedUnboundedPartition = false;
|
|
218
|
+
/** This function emits the process warning of an unbounded store with a counter for each partition, one time. */
|
|
219
|
+
function warnUnboundedPartition() {
|
|
220
|
+
if (warnedUnboundedPartition)
|
|
221
|
+
return;
|
|
222
|
+
warnedUnboundedPartition = true;
|
|
223
|
+
process.emitWarning('writesInFlight: Infinity with sequence: "partition" costs approximately 30 % of the writes a second. Use the default bound of 8.', { code: UNBOUNDED_PARTITION_WARNING });
|
|
224
|
+
}
|
|
225
|
+
/** The permits of the writes of each client, by the value of `writesInFlight`: the stores with one value share the permits. */
|
|
226
|
+
const WRITES = new WeakMap();
|
|
227
|
+
/** This function gives the permits of the writes of a client for a bound. */
|
|
228
|
+
function writePermitsOf(db, bound) {
|
|
229
|
+
const owner = ownerOf(db);
|
|
230
|
+
let byBound = WRITES.get(owner);
|
|
231
|
+
if (!byBound)
|
|
232
|
+
WRITES.set(owner, (byBound = new Map()));
|
|
233
|
+
let permits = byBound.get(bound);
|
|
234
|
+
if (!permits)
|
|
235
|
+
byBound.set(bound, (permits = semaphore(bound)));
|
|
236
|
+
return permits;
|
|
237
|
+
}
|
|
201
238
|
/** This function starts a write after each earlier write of the collection. */
|
|
202
239
|
function enqueueWrite(db, name, task) {
|
|
203
240
|
const owner = ownerOf(db);
|
|
@@ -500,6 +537,8 @@ export class MongoDocumentStorage {
|
|
|
500
537
|
maxTombstones;
|
|
501
538
|
countersName;
|
|
502
539
|
writeLeaseMs;
|
|
540
|
+
/** The most writes of each client at one time, over the stores with this value. `Infinity` is no bound. */
|
|
541
|
+
writesInFlight;
|
|
503
542
|
/** The storages of the partitions, for `forPartition`: the same storage for the same partition. */
|
|
504
543
|
partitions;
|
|
505
544
|
/** The stored form of the documents (roadmap CLI-76). A `ViewMaterializer` of the collection takes the same form. */
|
|
@@ -513,6 +552,12 @@ export class MongoDocumentStorage {
|
|
|
513
552
|
indexes;
|
|
514
553
|
/** True after this storage wrote (or found) the floor document of its partition (roadmap O66). A failed write leaves it false. */
|
|
515
554
|
floorNoted = false;
|
|
555
|
+
/** The sequence of the numbers (roadmap CLI-96). */
|
|
556
|
+
sequence;
|
|
557
|
+
/** The `_id` of the counter of the numbers: the collection, or the partition with the option `sequence: "partition"`. */
|
|
558
|
+
counterId;
|
|
559
|
+
/** The partition of the notes of the own numbers with a counter for each partition, else `undefined` (roadmap CLI-96). */
|
|
560
|
+
ownScope;
|
|
516
561
|
closed = false;
|
|
517
562
|
static {
|
|
518
563
|
sourceOfStorage = (storage) => ({
|
|
@@ -530,6 +575,7 @@ export class MongoDocumentStorage {
|
|
|
530
575
|
writeTargetOfStorage = (storage) => ({
|
|
531
576
|
collection: storage.collectionName,
|
|
532
577
|
counters: storage.countersName,
|
|
578
|
+
sequence: storage.sequence,
|
|
533
579
|
form: storage.form,
|
|
534
580
|
partition: storage.partition,
|
|
535
581
|
writeLeaseMs: storage.writeLeaseMs,
|
|
@@ -576,9 +622,21 @@ export class MongoDocumentStorage {
|
|
|
576
622
|
if (!Number.isSafeInteger(this.maxTombstones) || this.maxTombstones < 0)
|
|
577
623
|
throw new Error("maxTombstones must be a whole number of 0 or more");
|
|
578
624
|
this.countersName = options.counters ?? "__storage_counters";
|
|
625
|
+
const sequence = options.sequence ?? "collection";
|
|
626
|
+
if (sequence !== "collection" && sequence !== "partition")
|
|
627
|
+
throw new TypeError(`sequence must be "collection" or "partition", not ${JSON.stringify(sequence)}`);
|
|
628
|
+
this.sequence = sequence;
|
|
629
|
+
this.counterId = sequence === "partition" ? `seq${SEPARATOR}${this.collectionName}${SEPARATOR}${this.partition ?? ""}` : this.collectionName;
|
|
630
|
+
this.ownScope = sequence === "partition" ? (this.partition ?? "") : undefined;
|
|
579
631
|
this.writeLeaseMs = Math.round(optionalMs(options.writeLease, "writeLease") ?? 60_000);
|
|
580
632
|
if (!Number.isSafeInteger(this.writeLeaseMs) || this.writeLeaseMs < 1)
|
|
581
633
|
throw new Error("writeLease must be 1 ms or more");
|
|
634
|
+
this.writesInFlight = options.writesInFlight ?? DEFAULT_WRITES_IN_FLIGHT;
|
|
635
|
+
if (!(this.writesInFlight === Infinity || (Number.isSafeInteger(this.writesInFlight) && this.writesInFlight >= 1))) {
|
|
636
|
+
throw new RangeError(`writesInFlight must be a whole number of 1 or more, or Infinity, not ${String(options.writesInFlight)}`);
|
|
637
|
+
}
|
|
638
|
+
if (this.writesInFlight === Infinity && this.sequence === "partition")
|
|
639
|
+
warnUnboundedPartition();
|
|
582
640
|
this.partitions = partitions ?? new Map();
|
|
583
641
|
}
|
|
584
642
|
/** The storage of a partition of the same collection. It gives the same storage for the same partition. */
|
|
@@ -728,7 +786,7 @@ export class MongoDocumentStorage {
|
|
|
728
786
|
* document until `release()`, so the watermark of the readers stays below them.
|
|
729
787
|
*/
|
|
730
788
|
async allocate(db, count) {
|
|
731
|
-
const counter = (await db.collection(this.countersName).findOneAndUpdate({ _id: this.
|
|
789
|
+
const counter = (await db.collection(this.countersName).findOneAndUpdate({ _id: this.counterId }, [
|
|
732
790
|
{ $set: { seq: { $add: [{ $ifNull: ["$seq", 0] }, count] }, epoch: { $ifNull: ["$epoch", randomUUID()] } } },
|
|
733
791
|
{
|
|
734
792
|
$set: {
|
|
@@ -740,7 +798,12 @@ export class MongoDocumentStorage {
|
|
|
740
798
|
], { upsert: true, returnDocument: "after" }));
|
|
741
799
|
if (!counter)
|
|
742
800
|
throw new Error(`The sequence of ${this.collectionName} is not available`);
|
|
743
|
-
|
|
801
|
+
const first = counter.seq - count + 1;
|
|
802
|
+
if (this.sequence === "collection" && counter.sequence === "partition") {
|
|
803
|
+
await this.release(db, first).catch(() => undefined);
|
|
804
|
+
throw sequenceMismatch(this.collectionName, "collection");
|
|
805
|
+
}
|
|
806
|
+
return { first, epoch: counter.epoch };
|
|
744
807
|
}
|
|
745
808
|
/**
|
|
746
809
|
* This method ends the pending numbers of a write, and drops the pending numbers whose lease
|
|
@@ -750,7 +813,7 @@ export class MongoDocumentStorage {
|
|
|
750
813
|
*/
|
|
751
814
|
async release(db, first) {
|
|
752
815
|
const live = { $gt: ["$$this.at", { $subtract: ["$$NOW", this.writeLeaseMs] }] };
|
|
753
|
-
const after = (await db.collection(this.countersName).findOneAndUpdate({ _id: this.
|
|
816
|
+
const after = (await db.collection(this.countersName).findOneAndUpdate({ _id: this.counterId }, [
|
|
754
817
|
{
|
|
755
818
|
$set: {
|
|
756
819
|
lastRelease: {
|
|
@@ -795,16 +858,20 @@ export class MongoDocumentStorage {
|
|
|
795
858
|
*/
|
|
796
859
|
async withNumbers(db, collection, count, write, own = true) {
|
|
797
860
|
const noteOwn = own ? noteOwnNumbers : () => undefined;
|
|
861
|
+
await this.checkSequence(db);
|
|
798
862
|
const client = await this.transactionClient(db);
|
|
799
863
|
if (client) {
|
|
800
864
|
const session = client.startSession();
|
|
801
865
|
try {
|
|
802
866
|
return await session.withTransaction(async () => {
|
|
803
|
-
const counter = (await db.collection(this.countersName).findOneAndUpdate({ _id: this.
|
|
867
|
+
const counter = (await db.collection(this.countersName).findOneAndUpdate({ _id: this.counterId }, { $inc: { seq: count }, $setOnInsert: { epoch: randomUUID() } }, { upsert: true, returnDocument: "after", session }));
|
|
804
868
|
if (!counter)
|
|
805
869
|
throw new Error(`The sequence of ${this.collectionName} is not available`);
|
|
870
|
+
// The error aborts the transaction, so the counter keeps its number
|
|
871
|
+
if (this.sequence === "collection" && counter.sequence === "partition")
|
|
872
|
+
throw sequenceMismatch(this.collectionName, "collection");
|
|
806
873
|
// A watcher of the collection in this process skips the events of these numbers (roadmap CLI-77)
|
|
807
|
-
noteOwn(db, this.collectionName, counter.seq - count + 1, count);
|
|
874
|
+
noteOwn(db, this.collectionName, counter.seq - count + 1, count, this.ownScope);
|
|
808
875
|
return write(counter.seq - count + 1, session);
|
|
809
876
|
});
|
|
810
877
|
}
|
|
@@ -821,8 +888,9 @@ export class MongoDocumentStorage {
|
|
|
821
888
|
* the numbers for a watcher of this process (roadmap CLI-77).
|
|
822
889
|
*/
|
|
823
890
|
async withPendingNumbers(db, collection, count, write, noteOwn) {
|
|
891
|
+
await this.checkSequence(db);
|
|
824
892
|
let { first } = await this.allocate(db, count);
|
|
825
|
-
noteOwn(db, this.collectionName, first, count);
|
|
893
|
+
noteOwn(db, this.collectionName, first, count, this.ownScope);
|
|
826
894
|
let result;
|
|
827
895
|
try {
|
|
828
896
|
result = await write(first);
|
|
@@ -836,7 +904,7 @@ export class MongoDocumentStorage {
|
|
|
836
904
|
throw new Error(`A write of ${this.collectionName} stayed late after ${MAX_MOVES} moves to new numbers`);
|
|
837
905
|
// Late: a reader can have passed these numbers. The write takes new ones.
|
|
838
906
|
const next = (await this.allocate(db, count)).first;
|
|
839
|
-
noteOwn(db, this.collectionName, next, count);
|
|
907
|
+
noteOwn(db, this.collectionName, next, count, this.ownScope);
|
|
840
908
|
await this.move(collection, first, count, next);
|
|
841
909
|
first = next;
|
|
842
910
|
}
|
|
@@ -845,19 +913,22 @@ export class MongoDocumentStorage {
|
|
|
845
913
|
/**
|
|
846
914
|
* The watermark of the collection: the highest number below each pending number of a write whose
|
|
847
915
|
* lease has not ended, the epoch, and the floor of the partition. Each number up to it is written
|
|
848
|
-
* or abandoned. One command reads them. Only the first read of a
|
|
916
|
+
* or abandoned. One command reads them. Only the first read of a store writes: it makes the counter. With the
|
|
917
|
+
* option `sequence: "partition"`, the first read of each process also marks the counter of the collection.
|
|
849
918
|
*/
|
|
850
919
|
async watermark(db) {
|
|
920
|
+
await this.checkSequence(db);
|
|
851
921
|
const counters = db.collection(this.countersName);
|
|
852
922
|
const floorId = this.floorId();
|
|
853
923
|
const read = () => counters
|
|
854
924
|
.aggregate([
|
|
855
|
-
{ $match: { _id: { $in: [this.
|
|
925
|
+
{ $match: { _id: { $in: [this.counterId, floorId] } } },
|
|
856
926
|
{
|
|
857
927
|
$project: {
|
|
858
928
|
seq: 1,
|
|
859
929
|
epoch: 1,
|
|
860
930
|
floor: 1,
|
|
931
|
+
sequence: 1,
|
|
861
932
|
low: {
|
|
862
933
|
$min: {
|
|
863
934
|
$map: {
|
|
@@ -873,21 +944,56 @@ export class MongoDocumentStorage {
|
|
|
873
944
|
])
|
|
874
945
|
.toArray();
|
|
875
946
|
let docs = await read();
|
|
876
|
-
if (!docs.some((doc) => doc._id === this.
|
|
947
|
+
if (!docs.some((doc) => doc._id === this.counterId)) {
|
|
877
948
|
// A duplicate key: another read made the counter at the same time
|
|
878
|
-
await counters.updateOne({ _id: this.
|
|
949
|
+
await counters.updateOne({ _id: this.counterId }, { $setOnInsert: { seq: 0, epoch: randomUUID() } }, { upsert: true }).catch((error) => {
|
|
879
950
|
if (!isDuplicateKey(error))
|
|
880
951
|
throw error;
|
|
881
952
|
});
|
|
882
953
|
docs = await read();
|
|
883
954
|
}
|
|
884
|
-
const mark = docs.find((doc) => doc._id === this.
|
|
955
|
+
const mark = docs.find((doc) => doc._id === this.counterId);
|
|
885
956
|
if (!mark)
|
|
886
957
|
throw new Error(`The sequence of ${this.collectionName} is not available`);
|
|
958
|
+
if (this.sequence === "collection" && mark.sequence === "partition")
|
|
959
|
+
throw sequenceMismatch(this.collectionName, "collection");
|
|
887
960
|
const seq = typeof mark.low === "number" ? Math.min(mark.seq, mark.low - 1) : mark.seq;
|
|
888
961
|
const floor = docs.find((doc) => doc._id === floorId)?.floor;
|
|
889
962
|
return { seq, epoch: mark.epoch, floor: typeof floor === "number" ? floor : 0, held: seq < mark.seq };
|
|
890
963
|
}
|
|
964
|
+
/**
|
|
965
|
+
* This method checks once for each collection that a store with the option `sequence: "partition"` can use it (roadmap
|
|
966
|
+
* CLI-96). One atomic step marks the counter of the collection, unless that counter has numbers. A store with the default
|
|
967
|
+
* reads the mark in its own steps on that counter, so it needs no check of its own.
|
|
968
|
+
*/
|
|
969
|
+
checkSequence(db) {
|
|
970
|
+
if (this.sequence === "collection")
|
|
971
|
+
return Promise.resolve();
|
|
972
|
+
const owner = ownerOf(db);
|
|
973
|
+
let checks = SEQUENCE_CHECKS.get(owner);
|
|
974
|
+
if (!checks)
|
|
975
|
+
SEQUENCE_CHECKS.set(owner, (checks = new Map()));
|
|
976
|
+
const key = `${db.databaseName}\u0000${this.countersName}\u0000${this.collectionName}`;
|
|
977
|
+
let check = checks.get(key);
|
|
978
|
+
if (!check) {
|
|
979
|
+
const mark = async () => {
|
|
980
|
+
const counter = await db.collection(this.countersName).findOneAndUpdate({ _id: this.collectionName },
|
|
981
|
+
// A counter with numbers keeps its mark as it is: absent for a collection of the default sequence
|
|
982
|
+
[{ $set: { sequence: { $cond: [{ $gt: [{ $ifNull: ["$seq", 0] }, 0] }, "$sequence", "partition"] } } }], { upsert: true, returnDocument: "after" });
|
|
983
|
+
if (counter?.sequence !== "partition")
|
|
984
|
+
throw sequenceMismatch(this.collectionName, "partition");
|
|
985
|
+
};
|
|
986
|
+
// Two processes can insert the counter at the same time: the second finds it at its next try
|
|
987
|
+
check = mark().catch((error) => (isDuplicateKey(error) ? mark() : Promise.reject(error)));
|
|
988
|
+
checks.set(key, check);
|
|
989
|
+
const own = checks;
|
|
990
|
+
check.catch(() => {
|
|
991
|
+
if (own.get(key) === check)
|
|
992
|
+
own.delete(key);
|
|
993
|
+
});
|
|
994
|
+
}
|
|
995
|
+
return check;
|
|
996
|
+
}
|
|
891
997
|
/** The id of the document of the oldest kept change of the partition. */
|
|
892
998
|
async floorOf(db) {
|
|
893
999
|
const doc = await db.collection(this.countersName).findOne({ _id: this.floorId() });
|
|
@@ -1154,11 +1260,16 @@ export class MongoDocumentStorage {
|
|
|
1154
1260
|
// ===========================================================================
|
|
1155
1261
|
async write(task) {
|
|
1156
1262
|
const { db, items } = await this.collection();
|
|
1157
|
-
|
|
1263
|
+
const run = async () => {
|
|
1158
1264
|
// The floor document of the partition, outside each transaction of the task (roadmap O66)
|
|
1159
1265
|
await this.noteFloor(db);
|
|
1160
1266
|
return task(db, items);
|
|
1161
|
-
}
|
|
1267
|
+
};
|
|
1268
|
+
// The permit comes after the turn in the queue, so a write that waits for an earlier write of its counter holds no permit
|
|
1269
|
+
if (this.writesInFlight === Infinity)
|
|
1270
|
+
return enqueueWrite(db, this.counterId, run);
|
|
1271
|
+
const permits = writePermitsOf(db, this.writesInFlight);
|
|
1272
|
+
return enqueueWrite(db, this.counterId, () => permits.run(run));
|
|
1162
1273
|
}
|
|
1163
1274
|
async set(id, value, options) {
|
|
1164
1275
|
await this.setBatch([[id, value]], options?.expectedVersion === undefined ? undefined : { expectedVersions: [[id, options.expectedVersion]] });
|