@lossless.org/nosqldb 10.7.3 → 10.8.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.
Files changed (33) hide show
  1. package/.smartconfig.json +0 -1
  2. package/dist_rust/rustdb_linux_amd64 +0 -0
  3. package/dist_rust/rustdb_linux_amd64.tsrust-build.json +5 -5
  4. package/dist_rust/rustdb_linux_amd64_musl +0 -0
  5. package/dist_rust/rustdb_linux_amd64_musl.tsrust-build.json +5 -5
  6. package/dist_rust/rustdb_linux_arm64 +0 -0
  7. package/dist_rust/rustdb_linux_arm64.tsrust-build.json +5 -5
  8. package/dist_rust/rustdb_linux_arm64_musl +0 -0
  9. package/dist_rust/rustdb_linux_arm64_musl.tsrust-build.json +5 -5
  10. package/dist_rust/rustdb_macos_amd64 +0 -0
  11. package/dist_rust/rustdb_macos_amd64.tsrust-build.json +5 -5
  12. package/dist_rust/rustdb_macos_arm64 +0 -0
  13. package/dist_rust/rustdb_macos_arm64.tsrust-build.json +5 -5
  14. package/dist_ts/00_commitinfo_data.js +1 -1
  15. package/dist_ts/ts_local/classes.localnosqldb.js +1 -1
  16. package/dist_ts/ts_nosqldb/classes.storagerehearsal.js +1 -1
  17. package/dist_ts/ts_nosqldb/resource-fencing.js +1 -1
  18. package/dist_ts/ts_nosqldb/rust-db-bridge.js +1 -1
  19. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +2 -0
  20. package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +24 -8
  21. package/dist_ts/ts_nosqldb/service-types.d.ts +2 -2
  22. package/dist_ts/ts_nosqldb/stopped-storage.js +1 -1
  23. package/dist_ts/ts_nosqldb/storage-rehearsal.d.ts +1 -1
  24. package/dist_ts/ts_nosqldb/storage-rehearsal.js +1 -1
  25. package/dist_ts/ts_nosqldb/storage-root-alignment.js +1 -1
  26. package/dist_ts/ts_nosqldb/storage-root-relocation.d.ts +1 -1
  27. package/dist_ts_debugserver/classes.debugserver.js +1 -1
  28. package/dist_ts_debugui/nosqldb-debugui.js +1 -1
  29. package/package.json +6 -6
  30. package/readme.md +80 -28
  31. package/third_party_licenses/inventory.json +1 -1
  32. package/ts/00_commitinfo_data.ts +1 -1
  33. package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +25 -7
package/readme.md CHANGED
@@ -250,11 +250,17 @@ Each entry contains:
250
250
 
251
251
  ---
252
252
 
253
+ ## 🔢 Document Field Order
254
+
255
+ Like MongoDB, every stored document leads with `_id`. Inserts, upserts, replacements, `findAndModify`, `$out` and `$merge` move a client-supplied `_id` to the front (the Node.js driver appends its generated `_id` after the caller's fields) and generate a missing one there. Every other field keeps its written order: update operators change existing fields in place and append the fields they create, and a replacement keeps its own field order after `_id`. Documents stored with a trailing `_id` by 10.7.3 and earlier keep it until an update, replacement or `$merge` changes them.
256
+
257
+ ---
258
+
253
259
  ## ✋ No-Op Write Detection
254
260
 
255
261
  The engine detects document rewrites that change nothing and skips them entirely — no storage write, no WAL append, no index update, and no oplog entry. A skipped rewrite still counts as matched (`matchedCount: 1`) but reports `modifiedCount: 0`.
256
262
 
257
- Only identical field values are skipped; top-level field order does not cause a write. Explicit changes to `_updatedAt` are persisted and returned just like changes to any other field. Applications decide when to advance their timestamps.
263
+ As in MongoDB, a rewrite is skipped only when the document stays identical: the same fields in the same order with the same values and BSON types, at every depth. Only the position of `_id` is ignored, so a document stored with a trailing `_id` by 10.7.3 and earlier is not rewritten by a write that changes nothing. A replacement or `$set` that only reorders fields is written. Explicit changes to `_updatedAt` are persisted and returned just like changes to any other field. Applications decide when to advance their timestamps.
258
264
 
259
265
  Counters are exposed through `serverStatus`:
260
266
 
@@ -390,9 +396,9 @@ semantics. A range is built from `$gt`, `$gte`, `$lt` and `$lte`, and from
390
396
  `$ne: null` or a `$nin` list holding null, which read only the non-null keys as
391
397
  MongoDB's `[MinKey, undefined) ∪ (null, MaxKey]` bounds do: documents without
392
398
  the field or with an explicit null are not examined. Ranges are built only on
393
- complete, non-multikey indexes whose values all have a scalar order; other
394
- indexes are not narrowed. The full predicate is still checked for every
395
- selected candidate.
399
+ complete indexes whose values all have a scalar order, and on a list field from
400
+ one side of the range only (see **List fields** below); other indexes are not
401
+ narrowed. The full predicate is still checked for every selected candidate.
396
402
 
397
403
  For a query whose filter references only `_id` and whose projection is exactly
398
404
  `{ _id: 1 }`, the native primary index can supply the original BSON identity
@@ -419,6 +425,17 @@ lifetimes remain held through durability. History publication also waits for the
419
425
  flush. DDL, transactions, index builds, imports and maintenance retain their
420
426
  existing independently durable publication paths.
421
427
 
428
+ The Rust engine registers SIGTERM and SIGINT before reporting readiness. The
429
+ first signal closes request admission, drains admitted commands and native
430
+ workers, and closes storage only after their durable publications finish. An
431
+ admitted transaction commit finishes atomically or aborts; idle transactions
432
+ discard their uncommitted changes. Successful shutdown releases storage-root
433
+ ownership and exits with code 0 even when the parent's management pipe remains
434
+ open. A second shutdown signal, a drain failure, or the overall 40-second
435
+ deadline forces exit with code 70; forced exit does not promise drain completion.
436
+ `NoSqlDbServer.stop()` also succeeds after a confirmed engine exit without
437
+ sending another management request to the dead child.
438
+
422
439
  **Storage compatibility:** version 10 adds a 40-byte certified document-ordinal
423
440
  record alongside the legacy 8-byte ordinal. It reads existing stores directly;
424
441
  startup and reads perform no format rewrite. After version 10 writes certified
@@ -594,7 +611,8 @@ unique index key wins, and the later writer is told at its own statement.
594
611
  publish every document.
595
612
  - A transaction open for longer than 60 seconds is aborted, as by MongoDB's
596
613
  transaction reaper: its staged writes are discarded and its cursors, write
597
- claims, transaction capacity and database gates are released. The server
614
+ claims, transaction capacity and database and collection gates are
615
+ released. The server
598
616
  checks idle sessions once a second; a statement still executing when the
599
617
  limit passes is not interrupted, and the transaction aborts when that
600
618
  statement has finished (MongoDB instead interrupts that statement with
@@ -630,11 +648,12 @@ reports `TransactionCommitted` (256), commit after abort reports
630
648
  `$out`/`$merge` reject before transaction admission or mutation.
631
649
 
632
650
  Outside a transaction, `createIndexes` for already installed, identical indexes
633
- completes without draining active transactions or publishing a new storage
651
+ completes without waiting for any transaction or publishing a new storage
634
652
  position. A repeated physical `create` preserves `NamespaceExists` (48) under
635
653
  the same shared admission. Both paths validate current authorization and catalog
636
- options. New collections and changed index catalogs still drain transactions
637
- and revalidate under exclusive admission before any publication.
654
+ options. A new collection or a changed index catalog waits only for the
655
+ transactions that named that collection, and revalidates under its exclusive
656
+ collection gate before any publication (see "Database waits" below).
638
657
 
639
658
  Logical sessions survive socket disconnects. Expiry, `endSessions` and
640
659
  `killSessions` drain active work and release cursors and namespace gates.
@@ -1599,6 +1618,23 @@ only its members. Engines older than this release refuse a storage root that
1599
1618
  holds a partial index instead of enforcing its uniqueness over every
1600
1619
  document.
1601
1620
 
1621
+ **List fields.** An index on a field that holds lists has an entry per element,
1622
+ as a MongoDB multikey index, and a compound index an entry per combination of
1623
+ its list fields' elements. Equality, `$in` and one-sided ranges on the elements
1624
+ read their bounds, and a document with several entries within them is returned
1625
+ once. A list matches a range bounded on both sides when one element meets the
1626
+ lower bound and one the upper, so such a read takes the side with fewer
1627
+ entries. Equality to a whole list, such as `{ tags: ['a', 'b'] }`, is not
1628
+ answered from element entries, and a list nested in a list stops range reads
1629
+ through the index. An index with element entries returns no order, so a sorted
1630
+ read sorts its matches, and a `hint` naming it is refused. A unique index on a
1631
+ list field is unique per element: a document that shares an element with
1632
+ another is a `DuplicateKey` (11000), while one document may repeat an element.
1633
+ An index whose field already held a list under 10.7.3 or earlier keeps its
1634
+ whole-list entries and uniqueness and is maintained but never selected until it
1635
+ is dropped and created again. Engines up to 10.7.3 refuse an index with element
1636
+ entries instead of maintaining it with whole lists.
1637
+
1602
1638
  `find()` and collection `aggregate()` accept `hint` as an index name (including
1603
1639
  `_id_`) or an exact ascending/descending key pattern. Key-pattern order matters;
1604
1640
  if several indexes share a key pattern, use a name. The selected active scalar
@@ -1619,8 +1655,8 @@ const count = await collection.countDocuments({ organizationId: 'org-1' }, {
1619
1655
 
1620
1656
  Unknown or quarantined indexes, malformed/ambiguous patterns, `$natural`, and
1621
1657
  hints on missing collections or views fail explicitly. Forced multikey traversal
1622
- is unsupported and rejects; unhinted array queries keep their existing scan
1623
- behavior. Sparse hints intentionally exclude documents absent from that index,
1658
+ is unsupported and rejects; unhinted queries on list fields use element entries
1659
+ as described under **List fields**. Sparse hints intentionally exclude documents absent from that index,
1624
1660
  including for empty filters and counts. A hint is never silently replaced by a
1625
1661
  collection scan or another index.
1626
1662
 
@@ -1775,9 +1811,9 @@ restores a `mongodump` of ordinary collections with its default flags: the
1775
1811
  documents, validators and supported indexes (unique, compound, sparse,
1776
1812
  partial and TTL). It sends this `idIndex` with every `create` and
1777
1813
  `createIndexes` with each index's `ns` and `ignoreUnknownIndexOptions: true`,
1778
- and it builds several collections' indexes in parallel, which queue on the
1779
- database (see "Database waits" below). Exclude the `admin` and `config`
1780
- namespaces (`--nsExclude 'admin.*' --nsExclude 'config.*'`) and create users
1814
+ and it builds several collections' indexes in parallel, which wait for one
1815
+ another instead of failing (see "Database waits" below). Exclude the `admin`
1816
+ and `config` namespaces (`--nsExclude 'admin.*' --nsExclude 'config.*'`) and create users
1781
1817
  through NoSQLDB's authentication options. Collections or indexes that use
1782
1818
  unsupported features (collation, text, geospatial, hashed or wildcard
1783
1819
  indexes, capped or time-series collections) fail the restore with the
@@ -2095,27 +2131,43 @@ fields:
2095
2131
  transaction. `getMore` rejects `maxTimeMS` with `BadValue` because NoSQLDB
2096
2132
  has no awaitData cursors, and a literal `maxCommitTimeMS` field is rejected
2097
2133
  with `InvalidOptions`.
2098
- - **Database waits.** DDL (`create`, `createIndexes`, `drop`, `dropIndexes`,
2099
- `collMod`, `dropDatabase`, `renameCollection`, `$out`/`$merge`) holds its
2100
- database exclusively; other commands on that database wait for it, as they
2101
- wait for a MongoDB collection or database lock. A command outside a
2134
+ - **Database waits.** DDL that drops or reshapes collections (`drop`,
2135
+ `dropIndexes`, `collMod`, `dropDatabase`, `renameCollection`,
2136
+ `$out`/`$merge`) holds its database exclusively; other commands on that
2137
+ database wait for it, as they wait for a MongoDB database lock. Creating a
2138
+ collection or its indexes (`create`, `createIndexes`) holds only that
2139
+ collection exclusively, as MongoDB locks the collection: it waits for the
2140
+ open transactions whose statements targeted that collection (a collection
2141
+ a transaction reads only through `$lookup` or `$unionWith` is not held; the
2142
+ transaction reads its snapshot of it), transaction statements on that
2143
+ collection that arrive after it queue behind it, and everything else
2144
+ proceeds. Commands outside a transaction take no collection
2145
+ gate; writes still queue for the storage writer, which an index build holds
2146
+ until its index is ready. A command outside a
2102
2147
  transaction waits first in, first out for as long as it may run: until
2103
2148
  `maxTimeMS` (`MaxTimeMSExpired`, code 50), until its client disconnects or
2104
2149
  shuts down its sending side (the wait ends with `ClientDisconnect`, code
2105
2150
  279, which a client that still reads receives before the connection closes)
2106
- or until the server shuts down (`InterruptedAtShutdown`, code 91). A DDL
2107
- command likewise waits for the open transactions on its database to commit
2108
- or abort, and every command on that database that arrives after the DDL
2109
- queues behind it, including reads of other collections. An open
2110
- transaction therefore delays DDL and the commands behind it at most until
2111
- the transaction lifetime limit (60 seconds, MongoDB's
2151
+ or until the server shuts down (`InterruptedAtShutdown`, code 91). A
2152
+ database DDL command likewise waits for the open transactions on its
2153
+ database to commit or abort, and every command on that database that
2154
+ arrives after it queues behind it, including reads of other collections. An
2155
+ open transaction therefore delays DDL and the commands behind it at most
2156
+ until the transaction lifetime limit (60 seconds, MongoDB's
2112
2157
  `transactionLifetimeLimitSeconds`) aborts it.
2113
- A transaction statement that needs a further database keeps a fixed
2114
- five-second limit and then fails with `IllegalOperation` (code 20,
2158
+ A transaction statement that needs a further database or collection keeps a
2159
+ fixed five-second limit and then fails with `IllegalOperation` (code 20,
2115
2160
  `EFENCE_BUSY: …`), so a transaction never waits without bound while it
2116
- holds its databases; like any failed statement, it aborts the transaction
2117
- and releases its databases. The service operations (export,
2118
- import, tenant and publication fencing) keep the same limit and report
2161
+ holds its databases and collections; like any failed statement, it aborts
2162
+ the transaction and releases them. One transaction targets at most 4,096
2163
+ distinct collections across all its databases; statements that revisit a
2164
+ collection retain no extra gate. This matches the process-wide collection-lock
2165
+ registry's 4,096-live-identity bound; concurrent transactions and collection
2166
+ DDL share that registry. It bounds process-local locks, not document staging,
2167
+ WAL record size or atomic publication. A statement targeting one more fails
2168
+ with `TooManyRequests`
2169
+ (code 16500) and aborts it. The service operations (export,
2170
+ import, tenant and publication fencing) keep the five-second gate-wait limit and report
2119
2171
  `EFENCE_BUSY`.
2120
2172
 
2121
2173
  Callers may therefore rely on a successful NoSQLDB response as a durable,
@@ -246,7 +246,7 @@
246
246
  "zmij@1.0.21"
247
247
  ],
248
248
  "files": {
249
- ".smartconfig.json": "3024d7ff0fea774ba57f7f1e64d801947bc24becc10299cdceddcb0c39305dd9",
249
+ ".smartconfig.json": "a6b17fae7da13c9b5f6eef7049cfd13b497ab7e80739e17446becc0edd5ccefe",
250
250
  "license.md": "cfa51b1d8428e453099273832dc2ec1cebdf23d7ba4eda307883158cfc77da2d",
251
251
  "rust-toolchain.toml": "8c3afbe90fa0feff766dffd92e6728a569c0a804cf49f1ecd47bf5ac262499b3",
252
252
  "rust/Cargo.lock": "aa9659174e579bc943719a5c92ab343d6505a5432e6375254e39abdb1792110a",
@@ -3,6 +3,6 @@
3
3
  */
4
4
  export const commitinfo = {
5
5
  name: '@lossless.org/nosqldb',
6
- version: '10.7.3',
6
+ version: '10.8.1',
7
7
  description: 'A MongoDB-compatible embedded database server with wire protocol support, backed by a high-performance Rust engine.'
8
8
  }
@@ -134,6 +134,7 @@ export class NoSqlDbServer {
134
134
  private bridgeOwned = false;
135
135
  private startInProgress = false;
136
136
  private startCompletionPromise: Promise<void> | null = null;
137
+ private stopCompletionPromise: Promise<void> | null = null;
137
138
  private isRunning = false;
138
139
  private resolvedConnectionUri = '';
139
140
  private resolvedPort: number | undefined;
@@ -157,9 +158,10 @@ export class NoSqlDbServer {
157
158
  };
158
159
  this.bridge = new RustDbBridge({ binaryPath: this.options.binaryPath });
159
160
  this.bridge.on('exit', (code: number | null, signal: string | null) => {
160
- const exitedUnexpectedly = this.isRunning;
161
+ const exitedUnexpectedly = this.isRunning
162
+ && !this.stopCompletionPromise
163
+ && (code !== 0 || signal !== null);
161
164
  this.isRunning = false;
162
- this.bridgeOwned = false;
163
165
  this.resolvedConnectionUri = '';
164
166
  this.resolvedPort = undefined;
165
167
  if (exitedUnexpectedly) {
@@ -174,7 +176,7 @@ export class NoSqlDbServer {
174
176
  async start(optionsArg?: INoSqlDbManagementOperationOptions): Promise<void> {
175
177
  validateNoSqlDbManagementOperationOptions(optionsArg);
176
178
  optionsArg?.signal?.throwIfAborted();
177
- if (this.isRunning || this.startInProgress || this.bridgeOwned) {
179
+ if (this.isRunning || this.startInProgress || this.bridgeOwned || this.stopCompletionPromise) {
178
180
  throw new Error('Server is already running or still owns a Rust bridge');
179
181
  }
180
182
 
@@ -333,7 +335,23 @@ export class NoSqlDbServer {
333
335
  /**
334
336
  * Stop the server
335
337
  */
336
- async stop(): Promise<void> {
338
+ stop(): Promise<void> {
339
+ if (this.stopCompletionPromise) {
340
+ return this.stopCompletionPromise;
341
+ }
342
+
343
+ const stopCompletionPromise = this.stopInternal();
344
+ this.stopCompletionPromise = stopCompletionPromise;
345
+ const releaseStopCompletion = () => {
346
+ if (this.stopCompletionPromise === stopCompletionPromise) {
347
+ this.stopCompletionPromise = null;
348
+ }
349
+ };
350
+ void stopCompletionPromise.then(releaseStopCompletion, releaseStopCompletion);
351
+ return stopCompletionPromise;
352
+ }
353
+
354
+ private async stopInternal(): Promise<void> {
337
355
  const startCompletionPromise = this.startCompletionPromise;
338
356
  if (startCompletionPromise) {
339
357
  await startCompletionPromise;
@@ -344,9 +362,6 @@ export class NoSqlDbServer {
344
362
  }
345
363
 
346
364
  const requestGracefulStop = this.isRunning && this.bridge.running;
347
- this.isRunning = false;
348
- this.resolvedConnectionUri = '';
349
- this.resolvedPort = undefined;
350
365
  if (requestGracefulStop) {
351
366
  try {
352
367
  await this.bridge.stopDb();
@@ -357,6 +372,9 @@ export class NoSqlDbServer {
357
372
 
358
373
  await this.bridge.terminate();
359
374
  this.bridgeOwned = false;
375
+ this.isRunning = false;
376
+ this.resolvedConnectionUri = '';
377
+ this.resolvedPort = undefined;
360
378
  }
361
379
 
362
380
  /**