@lossless.org/nosqldb 10.11.0 → 11.0.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/.smartconfig.json +117 -0
- package/dist_rust/rustdb_linux_amd64 +0 -0
- package/dist_rust/rustdb_linux_amd64.tsrust-build.json +5 -5
- package/dist_rust/rustdb_linux_amd64_musl +0 -0
- package/dist_rust/rustdb_linux_amd64_musl.tsrust-build.json +5 -5
- package/dist_rust/rustdb_linux_arm64 +0 -0
- package/dist_rust/rustdb_linux_arm64.tsrust-build.json +5 -5
- package/dist_rust/rustdb_linux_arm64_musl +0 -0
- package/dist_rust/rustdb_linux_arm64_musl.tsrust-build.json +5 -5
- package/dist_rust/rustdb_macos_amd64 +0 -0
- package/dist_rust/rustdb_macos_amd64.tsrust-build.json +5 -5
- package/dist_rust/rustdb_macos_arm64 +0 -0
- package/dist_rust/rustdb_macos_arm64.tsrust-build.json +5 -5
- package/dist_ts/00_commitinfo_data.js +2 -2
- package/dist_ts/ts_local/classes.localnosqldb.d.ts +15 -4
- package/dist_ts/ts_local/classes.localnosqldb.js +31 -11
- package/dist_ts/ts_nosqldb/index.d.ts +3 -3
- package/dist_ts/ts_nosqldb/index.js +5 -4
- package/dist_ts/ts_nosqldb/rust-db-bridge.d.ts +125 -2
- package/dist_ts/ts_nosqldb/rust-db-bridge.js +261 -46
- package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.d.ts +69 -3
- package/dist_ts/ts_nosqldb/server/classes.nosqldbserver.js +91 -12
- package/dist_ts/ts_nosqldb/server/index.d.ts +2 -2
- package/dist_ts/ts_nosqldb/server/index.js +2 -2
- package/dist_ts/ts_nosqldb/stopped-storage.d.ts +9 -1
- package/dist_ts/ts_nosqldb/stopped-storage.js +1 -1
- package/dist_ts/ts_nosqldb/storage-rehearsal.js +1 -1
- package/dist_ts/ts_nosqldb/storage-root-alignment.js +7 -3
- package/dist_ts/ts_nosqldb/storage-root-relocation.js +2 -2
- package/dist_ts_debugserver/bundled.js +6 -2
- package/package.json +28 -15
- package/readme.md +470 -730
- package/third-party-notices.md +40 -8
- package/third_party_licenses/inventory.json +6 -6
- package/ts/00_commitinfo_data.ts +8 -0
- package/ts/index.ts +85 -0
- package/ts/ts_local/classes.localnosqldb.ts +631 -0
- package/ts/ts_local/index.ts +19 -0
- package/ts/ts_local/plugins.ts +1 -0
- package/ts/ts_nosqldb/classes.storagerehearsal.ts +109 -0
- package/ts/ts_nosqldb/committed-writes.ts +38 -0
- package/ts/ts_nosqldb/file-storage-options.ts +33 -0
- package/ts/ts_nosqldb/file-storage-startup.ts +10 -0
- package/ts/ts_nosqldb/index.ts +132 -0
- package/ts/ts_nosqldb/offline-inspection.ts +375 -0
- package/ts/ts_nosqldb/plugins.ts +12 -0
- package/ts/ts_nosqldb/resource-fencing.ts +368 -0
- package/ts/ts_nosqldb/rust-db-bridge.ts +1309 -0
- package/ts/ts_nosqldb/server/classes.nosqldbserver.ts +874 -0
- package/ts/ts_nosqldb/server/index.ts +4 -0
- package/ts/ts_nosqldb/service-types.ts +499 -0
- package/ts/ts_nosqldb/stopped-storage.ts +116 -0
- package/ts/ts_nosqldb/storage-rehearsal.ts +215 -0
- package/ts/ts_nosqldb/storage-root-alignment.ts +180 -0
- package/ts/ts_nosqldb/storage-root-relocation.ts +279 -0
package/readme.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @lossless.org/nosqldb
|
|
2
2
|
|
|
3
|
-
A MongoDB-wire-compatible
|
|
3
|
+
A MongoDB-wire-compatible database server powered by its own native Rust MVCC engine, engineered from scratch in a clean room with a public API surface derived from the Apache-2.0-licensed official MongoDB Node.js driver, and built for every scale: from a test fixture or a database embedded in a single application to large enterprise deployments. Its goal is to beat MongoDB on performance and on features. It supports the documented command surface through the official `mongodb` driver without an external MongoDB server. Memory and file storage use the same format-3 store, including the operation log, point-in-time revert and debug dashboard. Matching executables are bundled with the package.
|
|
4
4
|
|
|
5
5
|
## Issue Reporting and Security
|
|
6
6
|
|
|
@@ -12,32 +12,29 @@ For reporting bugs, issues, or security vulnerabilities, please visit [community
|
|
|
12
12
|
pnpm add @lossless.org/nosqldb
|
|
13
13
|
```
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
---
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## What It Does
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
| --- | --- |
|
|
21
|
-
| `@push.rocks/smartdb` | `@lossless.org/nosqldb` |
|
|
22
|
-
| `SmartdbServer` | `NoSqlDbServer` |
|
|
23
|
-
| `LocalSmartDb` | `LocalNoSqlDb` |
|
|
24
|
-
| `SmartdbStorageRehearsal` | `NoSqlDbStorageRehearsal` |
|
|
25
|
-
| `SmartdbDebugServer` / `SmartdbDebugUi` | `NoSqlDbDebugServer` / `NoSqlDbDebugUi` |
|
|
26
|
-
| `ISmartdb*`, `ISmartDb*`, `TSmartdb*`, `TSmartDb*` | `INoSqlDb*`, `TNoSqlDb*` |
|
|
27
|
-
| `createSmartDb*`, `normalizeSmartDb*`, `smartDb*` | `createNoSqlDb*`, `normalizeNoSqlDb*`, `noSqlDb*` |
|
|
28
|
-
| `SMARTDB_*` environment variables | `NOSQLDB_*` environment variables |
|
|
29
|
-
| `<smartdb-debugui>` / `/api/smartdb/*` | `<nosqldb-debugui>` / `/api/nosqldb/*` |
|
|
30
|
-
| Benchmark `--engine smartdb` | Benchmark `--engine nosqldb` |
|
|
19
|
+
`@lossless.org/nosqldb` is a **real database server** that speaks the wire protocol used by MongoDB drivers. The core engine is written in Rust for high performance, with a thin TypeScript orchestration layer. Connect with the standard `mongodb` Node.js driver — no mocks, no stubs, no external binaries required.
|
|
31
20
|
|
|
32
|
-
|
|
21
|
+
### From one process to enterprise scale
|
|
33
22
|
|
|
34
|
-
|
|
23
|
+
NoSQLDB brings its own clean-room engineered, MongoDB-compatible engine to every stage of an application's life:
|
|
35
24
|
|
|
36
|
-
|
|
25
|
+
- **Development and tests:** start a server from code, in memory or on disk, with nothing else to install.
|
|
26
|
+
- **Embedded:** ship the database inside your application and reach it over TCP or a Unix socket.
|
|
27
|
+
- **Enterprise scale:** large datasets, many collections and indexes, and many concurrent clients on its own durable MVCC engine.
|
|
37
28
|
|
|
38
|
-
|
|
29
|
+
The target is to beat MongoDB on performance and on features: more throughput, lower latency, and less memory and disk per operation, with MongoDB-compatible behaviour wherever applications rely on it, and capabilities beyond it where they help, such as the built-in operation log, point-in-time revert and debug dashboard.
|
|
39
30
|
|
|
40
|
-
|
|
31
|
+
### MongoDB compatibility
|
|
32
|
+
|
|
33
|
+
NoSQLDB is an independent implementation, written from scratch to be MongoDB compatible. Its public API surface is derived from the client side: the official MongoDB Node.js driver (`mongodb`, Apache-2.0 licensed) and the commands and replies it exchanges. Compatibility is established by runtime testing against MongoDB through its wire protocol and the official drivers. NoSQLDB contains no MongoDB server code. It is built with the clean-room setup of [AGL](https://code.foss.global/modelprofile.com/agl): the sessions that write its code work in confined containers that hold nothing but this repository. Where this readme says a behaviour, code or message is MongoDB compatible, it was implemented to match what such testing observes.
|
|
34
|
+
|
|
35
|
+
Contributions follow the same clean-room rules: describe behaviour as implemented to be MongoDB compatible, never as the same as a MongoDB version; record provenance as observed by runtime testing against MongoDB; and never read, quote, cite or link MongoDB server source code or its test suites.
|
|
36
|
+
|
|
37
|
+
MongoDB is a trademark of MongoDB, Inc. NoSQLDB is not affiliated with or endorsed by MongoDB, Inc.
|
|
41
38
|
|
|
42
39
|
### Why NoSQLDB?
|
|
43
40
|
|
|
@@ -49,7 +46,7 @@ This is a package and API rename, not a storage-format conversion. Existing curr
|
|
|
49
46
|
| **Persistence** | Memory or file-based | Full disk engine |
|
|
50
47
|
| **Debug UI** | Built in | External tooling |
|
|
51
48
|
| **Point-in-time revert** | Built in | Depends on the database |
|
|
52
|
-
| **
|
|
49
|
+
| **Built for** | Unit tests, CI/CD, local dev, embedded applications and large-scale production | Production |
|
|
53
50
|
|
|
54
51
|
### Three Ways to Use It
|
|
55
52
|
|
|
@@ -252,7 +249,7 @@ Each entry contains:
|
|
|
252
249
|
|
|
253
250
|
## 🔢 Document Field Order
|
|
254
251
|
|
|
255
|
-
|
|
252
|
+
Every stored document leads with `_id`, implemented to be MongoDB compatible. 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
253
|
|
|
257
254
|
---
|
|
258
255
|
|
|
@@ -260,7 +257,7 @@ Like MongoDB, every stored document leads with `_id`. Inserts, upserts, replacem
|
|
|
260
257
|
|
|
261
258
|
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`.
|
|
262
259
|
|
|
263
|
-
|
|
260
|
+
To be MongoDB compatible, 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.
|
|
264
261
|
|
|
265
262
|
Counters are exposed through `serverStatus`:
|
|
266
263
|
|
|
@@ -338,6 +335,59 @@ try {
|
|
|
338
335
|
|
|
339
336
|
The socket parent directory must already exist with the caller's desired access
|
|
340
337
|
permissions. `RustDbBridge` also accepts `{ binaryPath }` for direct lifecycle use.
|
|
338
|
+
When its `spawn()` returns false, `spawnFailureMessage` says what that means as
|
|
339
|
+
far as the bridge knows: with a `binaryPath`, that the engine there did not
|
|
340
|
+
report ready within the bridge's limit, with the path as given and the time
|
|
341
|
+
waited; without one, that no engine binary was found or that the engine found
|
|
342
|
+
did not report ready, as nothing tells which. If ending that engine fails,
|
|
343
|
+
`spawn()` throws instead of returning false, and `spawnFailureMessage` keeps the
|
|
344
|
+
message. After any other `spawn()`, including one that throws on its own
|
|
345
|
+
`timeoutMs` or `signal`, it is undefined. A `binaryPath` that does not exist
|
|
346
|
+
is refused before `spawn()` returns, with `RustBinaryLocatorError`.
|
|
347
|
+
|
|
348
|
+
#### Engine output
|
|
349
|
+
|
|
350
|
+
The engine writes its log to its standard error. None of it reaches the host
|
|
351
|
+
process's standard error: by default the server keeps only the engine's last
|
|
352
|
+
lines, for errors. To receive the log, pass `engineLog`, which is called once
|
|
353
|
+
for every line, in order, without the line break. `onEngineExit` receives a
|
|
354
|
+
`NoSqlDbEngineExitError` for each unexpected exit of the engine: an exit with a
|
|
355
|
+
non-zero status or by a signal while the server runs and no stop is in
|
|
356
|
+
progress. An exit with status 0, which the engine makes after a graceful
|
|
357
|
+
shutdown it was signalled to make, is not reported; the server counts as
|
|
358
|
+
stopped either way.
|
|
359
|
+
|
|
360
|
+
```typescript
|
|
361
|
+
import { NoSqlDbServer } from '@lossless.org/nosqldb';
|
|
362
|
+
|
|
363
|
+
const server = new NoSqlDbServer({
|
|
364
|
+
storage: 'file',
|
|
365
|
+
storagePath: '/var/lib/app/nosqldb',
|
|
366
|
+
engineLog: (line) => logger.info(`nosqldb: ${line}`),
|
|
367
|
+
onEngineExit: (error) => logger.error(error.message, error.engineStderrTail),
|
|
368
|
+
});
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
Whatever is set, these carry the engine's last 64 standard-error lines in their
|
|
372
|
+
field `engineStderrTail`, a frozen array of strings, without any change to
|
|
373
|
+
their message:
|
|
374
|
+
|
|
375
|
+
- every rejection of `start()` of `NoSqlDbServer` and `LocalNoSqlDb`, with an
|
|
376
|
+
empty array when the start failed before it spawned an engine;
|
|
377
|
+
- the `NoSqlDbEngineExitError`, whose message is
|
|
378
|
+
`nosqldb Rust process exited unexpectedly (code=<code>, signal=<signal>)` and
|
|
379
|
+
which has `exitCode` and `signal`;
|
|
380
|
+
- the rejection of a management call that fails because the engine no longer
|
|
381
|
+
runs.
|
|
382
|
+
|
|
383
|
+
A line longer than 1,024 characters keeps its first 1,024, followed by
|
|
384
|
+
` [<n> more characters]`; `engineLog` receives it whole.
|
|
385
|
+
`noSqlDbEngineStderrTailLimits` exports both bounds. `LocalNoSqlDb` accepts
|
|
386
|
+
both options and passes them to its server. `RustDbBridge` accepts `engineLog`
|
|
387
|
+
as well, and its `engineStderrTail` getter returns the last lines of the
|
|
388
|
+
running or most recently spawned engine. Each function runs in its own
|
|
389
|
+
microtask, after the server has recorded the line or the exit; an exception it
|
|
390
|
+
throws is not caught and reaches the process as an uncaught exception.
|
|
341
391
|
|
|
342
392
|
#### Native memory and file storage
|
|
343
393
|
|
|
@@ -380,54 +430,68 @@ above is an example, not a universal default. Reopen with the stored SCRAM work
|
|
|
380
430
|
factor; bootstrap users may be omitted after initialization. `LocalNoSqlDb`
|
|
381
431
|
accepts `folderPath`, `socketPath`, `binaryPath` and the same `auth` options.
|
|
382
432
|
|
|
433
|
+
The engine allocates its memory with jemalloc, under settings built into the
|
|
434
|
+
engine: two arenas, freed memory returned to the operating system over 5
|
|
435
|
+
seconds (dirty decay 5,000 ms, muzzy decay 0), and on Linux the allocator's
|
|
436
|
+
background threads. At start the engine checks that its allocator runs these
|
|
437
|
+
settings and raises its soft open-files limit to its hard limit (on macOS to at
|
|
438
|
+
most `kern.maxfilesperproc`). When either fails, as the check does when
|
|
439
|
+
`_RJEM_MALLOC_CONF` in its environment changes a setting, the engine does not
|
|
440
|
+
start: it exits with status 71, and `start()` rejects with the reason in
|
|
441
|
+
`engineStderrTail`. The `MIMALLOC_ARENA_EAGER_COMMIT` and
|
|
442
|
+
`MIMALLOC_ALLOW_LARGE_OS_PAGES` variables that earlier releases set for the
|
|
443
|
+
engine are no longer set or read.
|
|
444
|
+
|
|
445
|
+
The performance figures this release states are measured with file storage on
|
|
446
|
+
XFS. ext4 was measured only on a loop-backed substitute, so this release makes
|
|
447
|
+
no latency claim for ext4. Recovery after a crash relies on a filesystem that
|
|
448
|
+
never exposes stale blocks after a crash: XFS, or ext4 with ordered data
|
|
449
|
+
(`data=ordered`, its default mode). ext4 mounted with `data=writeback` is not
|
|
450
|
+
supported. On macOS, file storage runs on APFS; the durability gates and the
|
|
451
|
+
timed gates of this release run on Linux only.
|
|
452
|
+
|
|
383
453
|
#### Native query and maintenance efficiency
|
|
384
454
|
|
|
385
455
|
Bounded synchronous reads, including principal resolution, publication authority
|
|
386
|
-
and query setup, share
|
|
387
|
-
authorization is reused across requests or asynchronous suspensions.
|
|
456
|
+
and query setup, share one retained snapshot of the root. No such snapshot or
|
|
457
|
+
resolved authorization is reused across requests or asynchronous suspensions.
|
|
388
458
|
|
|
389
459
|
Filesystem ownership and root bindings are validated once per command for
|
|
390
460
|
reads: the reads of each wire command or background maintenance turn reuse one
|
|
391
|
-
full validation of the root and its bindings that began
|
|
392
|
-
started, run by its own first storage read or by a
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
461
|
+
full validation of the enclosing owner, the root and its bindings that began
|
|
462
|
+
after the command started, run by its own first storage read or by a
|
|
463
|
+
concurrent command. Reads go through retained descriptors, so a path swap
|
|
464
|
+
cannot change what they return. A failed validation revokes every earlier
|
|
465
|
+
one, so commands still running stop at their next read. A validation covers
|
|
466
|
+
reads for at most one second after it began, measured on a monotonic clock,
|
|
467
|
+
so a long-running command such as a large aggregation or sort validates again
|
|
468
|
+
at its first read after that and fails at that read once its root or a
|
|
469
|
+
binding has been replaced. Every write, whether a commit's, a group's, a
|
|
470
|
+
maintenance operation's or an index build's, validates the enclosing owner
|
|
471
|
+
and every binding before it writes, and a commit's conflict checks validate
|
|
472
|
+
in full before and after themselves, before the commit takes its place in
|
|
473
|
+
the log. After a write, when its outcome can be unknown, the root's bindings
|
|
474
|
+
and each file the write wrote, by its binding and its length, are checked
|
|
475
|
+
through the retained descriptors alone, without opening the owner's path; a
|
|
476
|
+
failure there fences the root, and the next command's first access validates
|
|
477
|
+
the enclosing owner again. Startup, recovery and native management calls,
|
|
478
|
+
which run outside wire commands, validate every access. A root or container
|
|
404
479
|
renamed or replaced between commands fails the next command's first storage
|
|
405
|
-
access
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
next field. For example, `{ serverId: 1, timestamp: 1 }` seeks directly into the
|
|
409
|
-
requested server's time interval. Inclusive/exclusive endpoints, descending
|
|
410
|
-
components, trailing fields and reverse pagination retain their normal
|
|
411
|
-
semantics. A range is built from `$gt`, `$gte`, `$lt` and `$lte`, and from
|
|
412
|
-
`$ne: null` or a `$nin` list holding null, which read only the non-null keys as
|
|
413
|
-
MongoDB's `[MinKey, undefined) ∪ (null, MaxKey]` bounds do: documents without
|
|
414
|
-
the field or with an explicit null are not examined. Ranges are built only on
|
|
415
|
-
complete indexes whose values all have a scalar order, and on a list field from
|
|
416
|
-
one side of the range only (see **List fields** below); other indexes are not
|
|
417
|
-
narrowed. The full predicate is still checked for every selected candidate.
|
|
480
|
+
access, and a data or log file replaced underneath the root fails the next
|
|
481
|
+
write that writes it. Durability, cancellation and close checks remain
|
|
482
|
+
enforced.
|
|
418
483
|
|
|
419
484
|
For a query whose filter references only `_id` and whose projection is exactly
|
|
420
485
|
`{ _id: 1 }`, the native primary index can supply the original BSON identity
|
|
421
486
|
without loading the document payload. Range endpoints, order, skip, limit and
|
|
422
487
|
cursor pagination retain their normal semantics. The engine verifies the index
|
|
423
|
-
and unique-owner values, the document
|
|
424
|
-
|
|
425
|
-
Updates that keep the same identity leave the primary owner and
|
|
488
|
+
and unique-owner values, the document record's existence, and the storage-owned
|
|
489
|
+
SHA-256 certificate of the exact BSON `_id` that every document record holds.
|
|
490
|
+
Updates that keep the same identity leave the primary owner and the certificate
|
|
426
491
|
unchanged, avoiding an extra index rewrite. Numeric BSON width changes update
|
|
427
492
|
the certificate and primary identity atomically with the body. A covered read
|
|
428
|
-
does not checksum an unrequested document payload.
|
|
429
|
-
|
|
430
|
-
adds the certificate.
|
|
493
|
+
does not checksum an unrequested document payload. An entry whose owner names
|
|
494
|
+
another identity than the certificate uses the normal bounded body read.
|
|
431
495
|
Secondary-index projections and predicates requiring other fields retain normal
|
|
432
496
|
document reads. No application cache or configuration flag is required.
|
|
433
497
|
|
|
@@ -449,27 +513,20 @@ discard their uncommitted changes. An admitted command that reaches the closing
|
|
|
449
513
|
session owner fails with `ShutdownInProgress` (code 91), and one still waiting
|
|
450
514
|
for a database or collection gate fails with `InterruptedAtShutdown` (code
|
|
451
515
|
11600); a transaction statement carries `TransientTransactionError` with
|
|
452
|
-
either,
|
|
516
|
+
either, implemented to be MongoDB compatible. Successful shutdown releases storage-root
|
|
453
517
|
ownership and exits with code 0 even when the parent's management pipe remains
|
|
454
518
|
open. A second shutdown signal, a drain failure, or the overall 40-second
|
|
455
519
|
deadline forces exit with code 70; forced exit does not promise drain completion.
|
|
456
520
|
`NoSqlDbServer.stop()` also succeeds after a confirmed engine exit without
|
|
457
521
|
sending another management request to the dead child.
|
|
458
522
|
|
|
459
|
-
**Storage compatibility:** version 10 adds a 40-byte certified document-ordinal
|
|
460
|
-
record alongside the legacy 8-byte ordinal. It reads existing stores directly;
|
|
461
|
-
startup and reads perform no format rewrite. After version 10 writes certified
|
|
462
|
-
records, use version 10 or later to reopen that store. Older binaries reject the
|
|
463
|
-
new record representation. Keep a pre-upgrade backup if a binary downgrade is
|
|
464
|
-
required. The K2 pages, commit markers and recovery protocol are unchanged.
|
|
465
|
-
|
|
466
523
|
Maintenance batches catalog work and parks completed reclamation and no-TTL
|
|
467
524
|
sweeps until the committed position changes. Session cleanup skips its metadata
|
|
468
|
-
scan until a session can expire.
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
525
|
+
scan until a session can expire. The store maintains itself on its own thread:
|
|
526
|
+
it merges its write buffer into the tree, writes checkpoints, prunes the
|
|
527
|
+
tombstones no snapshot needs and moves the live data out of data segments
|
|
528
|
+
that hold much garbage. No pass copies the whole root, and there is no
|
|
529
|
+
compaction to run, resume or restart.
|
|
473
530
|
|
|
474
531
|
#### File startup admission
|
|
475
532
|
|
|
@@ -480,20 +537,26 @@ The exported type is `TNoSqlDbFileStorageStartup`. Invalid values, including
|
|
|
480
537
|
`'migrate'`, reject at construction. Selecting the policy with memory storage
|
|
481
538
|
also rejects.
|
|
482
539
|
|
|
483
|
-
Default startup accepts a fresh root or a current native root
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
540
|
+
Default startup accepts a fresh root or a current native root, and converts a
|
|
541
|
+
root that a 10.x release wrote before it serves it (see **Storage format
|
|
542
|
+
migration** below). Use `fileStorageStartup: 'existing-only'` when the database
|
|
543
|
+
must already exist: missing paths, empty directories and identity-only roots
|
|
544
|
+
without committed initialization fail instead of creating an empty database,
|
|
545
|
+
and a 10.x root that holds a committed database counts as existing and is
|
|
546
|
+
converted as under the default. The native owner enforces this policy; no
|
|
547
|
+
TypeScript existence check or fallback can create a new root. Older binaries
|
|
548
|
+
reject the new option rather than silently ignoring it.
|
|
489
549
|
Rehearsal startup retains its separate current-format-only configuration.
|
|
490
550
|
|
|
491
|
-
Foreign
|
|
492
|
-
|
|
551
|
+
Foreign and mixed layouts, a 10.x name beside entries of this release without
|
|
552
|
+
the record of a conversion in progress, and a 10.x root that the conversion
|
|
553
|
+
cannot take are rejected before anything is written. TypeScript
|
|
493
554
|
performs no storage writes before starting the engine. Native format admission
|
|
494
555
|
uses the owning detector; the exclusive native file-root lease is acquired before
|
|
495
556
|
initialization, recovery, authentication or index preparation.
|
|
496
557
|
|
|
558
|
+
A root that carries a format or a feature that this release does not know, such as a root that a later release wrote, is refused before anything is written, and no root is created in its place or beside it. The start's rejection then carries `responseErrorCode: 'UNKNOWN_ROOT'`, which no other start failure carries, and its message names the cause. A missing storage path is created together with every directory missing above it, each accessible to its owner alone; `'existing-only'` creates none of them.
|
|
559
|
+
|
|
497
560
|
Only after `await server.start()` succeeds should consumers prepare SmartData
|
|
498
561
|
models or run application migrations. Start cancellation and deadlines cover the
|
|
499
562
|
native operation; failed-start cleanup confirms child exit, and `stop()` remains
|
|
@@ -501,10 +564,9 @@ retryable if termination cannot yet be confirmed. A second owner of the same
|
|
|
501
564
|
root is rejected without disturbing the winner. Callers must also exclude
|
|
502
565
|
manual edits and lifecycle changes outside NoSQLDB's ownership protocol.
|
|
503
566
|
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
`
|
|
507
|
-
release contains no old-engine or authentication-schema conversion.
|
|
567
|
+
**Storage format migration.** Every root this release creates is a format-3 root under `.nosqldb-control` and `.nosqldb-store`. A start, under either `fileStorageStartup` policy, converts a root that a 10.x release wrote, which it recognizes by `.__rustdb_internal` and `.__rustdb_mvcc`, and then serves the result. The conversion reads the 10.x root without writing into it, builds the format-3 root beside it under this release's names, verifies it record for record against the 10.x content, and only then removes the 10.x entries. It records its progress in `.nosqldb-conversion`, so a start that is stopped or interrupted during the conversion resumes it at the next start. Before it writes anything it declares the space it needs beside the 10.x root, and the start is refused when the file system does not hold it. It carries the database as of the root's last completed commit, with no history of writes committed before the conversion and no part of an import that had not finished. No earlier release reads a root that this release has created, converted or aligned: keep a stopped copy of the 10.x root if you may need to go back. That copy is a copied root: restored into another directory or onto another machine, it keeps the binding of its old place, and 10.8.1, like this release, refuses to start on it until it is aligned. Alignment refuses a root that holds auth users, so for such a root a copy restored that way is no way back. A start refuses, and leaves the path as it found it, a 10.x root that holds no committed database or only part of one, a 10.x name beside entries of this release without the conversion's record, a 10.x root that a running 10.x process holds or that a rehearsal created, a collection whose recorded count its documents do not hold, and a unique index that would hold two documents under one key once each array member is indexed on its own. A 10.x root that was copied, moved or restored converts too but keeps the binding of its old place; like every copied root, it must be aligned (`alignStoppedStorageRoot`) before a start serves it. No authentication or permission migration runs during startup, and this release contains no authentication-schema conversion.
|
|
568
|
+
|
|
569
|
+
A stopped root that a 10.x release wrote can be read before a start converts it. The stopped operations that only read open it read-only, under its own names and its own lock as a 10.x release's stopped operations did, and change nothing in it: the root audit (`inspectStoppedStorageRoot`), the string and namespace inspections (`inspectOfflineStringValue`, `inspectOfflinePhysicalNamespaces`) and data validation (`--validate-data`), which runs the namespace inspection. Alignment, relocation and rehearsal refuse a 10.x root with `UNSUPPORTED`: start a server on it to convert it, then copy and align the converted root. Every stopped operation refuses a path on which a conversion is in progress, one that holds `.nosqldb-conversion`; the root audit answers it as `unavailable` with `UNSUPPORTED`, and the string and namespace inspections and data validation reject it with `UNSUPPORTED`. A start on that path resumes the conversion and finishes it.
|
|
508
570
|
|
|
509
571
|
```typescript
|
|
510
572
|
import { NoSqlDbServer } from '@lossless.org/nosqldb';
|
|
@@ -546,6 +608,15 @@ const lean = new NoSqlDbServer({
|
|
|
546
608
|
oplog: { enabled: false },
|
|
547
609
|
});
|
|
548
610
|
|
|
611
|
+
// The store's memory budget, which its node cache and write buffer share:
|
|
612
|
+
// 4 MiB to 1 TiB, refused outside that range and never clamped
|
|
613
|
+
// (default: computed from the memory the process may use)
|
|
614
|
+
const budgeted = new NoSqlDbServer({
|
|
615
|
+
storage: 'file',
|
|
616
|
+
storagePath: './data',
|
|
617
|
+
storageBudgetBytes: 256 * 1024 * 1024,
|
|
618
|
+
});
|
|
619
|
+
|
|
549
620
|
// TLS transport for TCP mode
|
|
550
621
|
const tlsServer = new NoSqlDbServer({
|
|
551
622
|
port: 27017,
|
|
@@ -608,7 +679,7 @@ the transaction's first statement. Supported reads and writes see the
|
|
|
608
679
|
transaction snapshot plus its staged changes. Commit publishes atomically;
|
|
609
680
|
abort discards staged changes.
|
|
610
681
|
|
|
611
|
-
Write conflicts
|
|
682
|
+
Write conflicts are implemented to be MongoDB compatible: the first statement to write a document or a
|
|
612
683
|
unique index key wins, and the later writer is told at its own statement.
|
|
613
684
|
|
|
614
685
|
- A transaction statement that inserts, updates, replaces, deletes or upserts a
|
|
@@ -620,8 +691,16 @@ unique index key wins, and the later writer is told at its own statement.
|
|
|
620
691
|
`commitTransaction` report `NoSuchTransaction` (251), also labelled
|
|
621
692
|
`TransientTransactionError`. The transaction that wrote first keeps its
|
|
622
693
|
documents and commits. The driver's `withTransaction` retries the loser on a
|
|
623
|
-
fresh snapshot,
|
|
624
|
-
|
|
694
|
+
fresh snapshot, and each attempt can lose again: no priority, ageing or wait
|
|
695
|
+
rule favours a retrying transaction. So a transaction that writes a hot
|
|
696
|
+
document and then does more work makes progress against clients looping
|
|
697
|
+
short transactions on it only by its odds of winning each attempt, with no
|
|
698
|
+
bound on its attempts or its time. Those per-attempt odds are lower than
|
|
699
|
+
MongoDB's, as observed by runtime testing against MongoDB (`mongo:7.0`,
|
|
700
|
+
7.0.39): MongoDB releases a committing transaction's documents before it
|
|
701
|
+
acknowledges the commit, while NoSQLDB acknowledges a commit as it releases
|
|
702
|
+
them, after the commit is durable. On a host whose durable sync is slower,
|
|
703
|
+
the odds fall further.
|
|
625
704
|
- Reads never conflict: they see the snapshot, not another transaction's
|
|
626
705
|
uncommitted writes. A statement that writes nothing (a filter that matches
|
|
627
706
|
nothing in the snapshot, an update that leaves the document unchanged) does
|
|
@@ -635,96 +714,18 @@ unique index key wins, and the later writer is told at its own statement.
|
|
|
635
714
|
finish. Writes to other documents are not delayed. A multi-document write
|
|
636
715
|
outside a transaction stays all-or-nothing: it publishes nothing until it can
|
|
637
716
|
publish every document.
|
|
638
|
-
- A transaction open for longer than 60 seconds is aborted, as by MongoDB's
|
|
639
|
-
transaction reaper: its staged writes are discarded and its cursors, write
|
|
640
|
-
claims, transaction capacity and database and collection gates are
|
|
641
|
-
released. The server
|
|
642
|
-
checks idle sessions once a second; a statement still executing when the
|
|
643
|
-
limit passes is not interrupted, and the transaction aborts when that
|
|
644
|
-
statement has finished (MongoDB instead interrupts that statement with
|
|
645
|
-
`TransactionExceededLifetimeLimitSeconds` (290)). Its later statements, `commitTransaction` and
|
|
646
|
-
`abortTransaction` report `NoSuchTransaction` (251); statements and
|
|
647
|
-
`commitTransaction` carry `TransientTransactionError`, so `withTransaction`
|
|
648
|
-
retries from the start. Before that abort, the next writer that needs one of
|
|
649
|
-
its documents aborts it too rather than wait. The limit is fixed at
|
|
650
|
-
MongoDB's default `transactionLifetimeLimitSeconds` and is not configurable.
|
|
651
|
-
Ending or expiring the session, and stopping the server, release a
|
|
652
|
-
transaction's writes as well.
|
|
653
717
|
- TTL expiry is not a contending writer. It leaves out the documents an open
|
|
654
718
|
transaction has written, deletes the other expired documents of the index
|
|
655
719
|
and moves past the held ones. A later pass over the index expires those
|
|
656
720
|
still expired once the transaction has ended. It never waits for a transaction and never aborts
|
|
657
|
-
one, whatever its age. MongoDB's TTL
|
|
658
|
-
the transaction ends and leaves aborting it to
|
|
721
|
+
one, whatever its age. MongoDB's TTL expiry, as observed by runtime testing, instead retries a conflicting delete until
|
|
722
|
+
the transaction ends and leaves aborting it to the transaction lifetime limit.
|
|
659
723
|
- A key already visible in the transaction's snapshot is not a conflict:
|
|
660
724
|
inserting it fails the statement with `DuplicateKey` (11000), as outside a
|
|
661
725
|
transaction. A collection created inside a transaction has no stored identity
|
|
662
726
|
until commit, so two transactions creating the same collection both succeed
|
|
663
|
-
their statements and the second commit reports `WriteConflict` (112),
|
|
664
|
-
MongoDB
|
|
665
|
-
|
|
666
|
-
#### Read fences
|
|
667
|
-
|
|
668
|
-
`readFence` is a NoSQLDB command, not a MongoDB one, available from 10.11.0. It
|
|
669
|
-
lets a transaction depend on documents it read without writing them: its commit
|
|
670
|
-
fails if any of them changed after the transaction's snapshot. Two transactions
|
|
671
|
-
that only read and fence one document therefore both commit, where fencing by
|
|
672
|
-
writing the document would make one of them conflict.
|
|
673
|
-
|
|
674
|
-
```typescript
|
|
675
|
-
await session.withTransaction(async () => {
|
|
676
|
-
const lane = await lanes.findOne({ _id: 'dns' }, { session });
|
|
677
|
-
await db.command({ readFence: 'lanes', ids: ['dns'] }, { session });
|
|
678
|
-
await jobs.insertOne({ _id: 'next', basedOn: lane?.revision }, { session });
|
|
679
|
-
});
|
|
680
|
-
```
|
|
681
|
-
|
|
682
|
-
- `{ readFence: <collection>, ids: [...] }` answers `{ ok: 1 }`, only as a
|
|
683
|
-
statement of a multi-document transaction; outside one it is refused with
|
|
684
|
-
`InvalidOptions` (72). `ids` holds 1 to 1,000 `_id` values, otherwise the
|
|
685
|
-
statement fails with `BadValue` (2). Duplicates count once, compared as
|
|
686
|
-
`_id` values are, so `1`, `NumberLong(1)` and `1.0` are one id. It needs the
|
|
687
|
-
read privilege of the database, as `find` does, and refuses a view with
|
|
688
|
-
`CommandNotSupportedOnView` (166). A refused statement ends its transaction,
|
|
689
|
-
as every failed transaction statement does.
|
|
690
|
-
- `commitTransaction` is refused with `WriteConflict` (112), labelled
|
|
691
|
-
`TransientTransactionError` and never `UnknownTransactionCommitResult`, when
|
|
692
|
-
a fenced `_id` has a committed change newer than the transaction's snapshot:
|
|
693
|
-
an insert, replace, update, delete or TTL expiry, by any writer, inside or
|
|
694
|
-
outside a transaction. Fencing an absent `_id` holds its absence. A drop,
|
|
695
|
-
rename or re-create of the collection, a drop of its database, and the
|
|
696
|
-
creation of a fenced collection or database that was absent also refuse the
|
|
697
|
-
commit. The check is atomic with the commit, and nothing of a refused
|
|
698
|
-
transaction becomes visible; `withTransaction` retries it on a fresh
|
|
699
|
-
snapshot.
|
|
700
|
-
- A fence can also name documents by the value of a unique key, which holds
|
|
701
|
-
a value whether a document holds it or not:
|
|
702
|
-
`{ readFence: <collection>, key: <field>, values: [...] }`. On an existing
|
|
703
|
-
collection the field needs a unique, non-partial, single-field index with
|
|
704
|
-
the simple collation, otherwise the statement fails with `IndexNotFound`
|
|
705
|
-
(27). A null value, empty `values`, more than 1,000 values, or `ids` and
|
|
706
|
-
`key` together fail with `BadValue` (2); equal values count once.
|
|
707
|
-
`commitTransaction` is refused when, after the snapshot, a committed write
|
|
708
|
-
changed the document that held a fenced value, or made any document hold
|
|
709
|
-
one: an insert of an absent value, an update or replace of the holder, its
|
|
710
|
-
delete, a move of its key to another value, a delete and re-insert, another
|
|
711
|
-
document taking the value, or TTL expiry. Dropping the index also refuses
|
|
712
|
-
the commit. `key: "_id"` is the fence by `ids`.
|
|
713
|
-
- The statement itself never fails because of a concurrent change: only the
|
|
714
|
-
commit decides.
|
|
715
|
-
- A fence takes no write claim. Fences never conflict with each other, never
|
|
716
|
-
wait for a writer and never make one wait or fail. A writer that commits
|
|
717
|
-
after the fencer has committed succeeds: the order is fencer, then writer.
|
|
718
|
-
Changes to other documents of the collection, new indexes and new
|
|
719
|
-
collections do not refuse the fencer.
|
|
720
|
-
- Fencing and writing the same document makes the transaction a plain writer
|
|
721
|
-
of it. A transaction that only reads and fences still gets the commit check.
|
|
722
|
-
Transactions without fences behave exactly as before.
|
|
723
|
-
- The statement takes the database gate every transaction statement takes, so
|
|
724
|
-
`drop`, `renameCollection` and `dropDatabase` in that database wait for the
|
|
725
|
-
transaction to end, as they wait for any transaction with a statement there.
|
|
726
|
-
It retains no collection gate: creating the fenced collection or its indexes
|
|
727
|
-
does not wait for the transaction.
|
|
727
|
+
their statements and the second commit reports `WriteConflict` (112),
|
|
728
|
+
implemented to be MongoDB compatible.
|
|
728
729
|
|
|
729
730
|
Write intents live in memory only; no transaction survives a restart, so
|
|
730
731
|
there is nothing to recover.
|
|
@@ -744,15 +745,6 @@ options. A new collection or a changed index catalog waits only for the
|
|
|
744
745
|
transactions that named that collection, and revalidates under its exclusive
|
|
745
746
|
collection gate before any publication (see "Database waits" below).
|
|
746
747
|
|
|
747
|
-
Logical sessions survive socket disconnects. Expiry, `endSessions` and
|
|
748
|
-
`killSessions` drain active work and release cursors and namespace gates.
|
|
749
|
-
A `commitTransaction` or `abortTransaction` retried while its session is being
|
|
750
|
-
ended still replays the transaction's stored outcome, so a committed
|
|
751
|
-
transaction is never reported as aborted; a transaction that was still open
|
|
752
|
-
when its session ended publishes nothing and reports `NoSuchTransaction` (251).
|
|
753
|
-
Rejected new transaction envelopes do not consume session capacity or reset
|
|
754
|
-
existing sessions. See the native resource budgets below.
|
|
755
|
-
|
|
756
748
|
### Durable Committed Writes
|
|
757
749
|
|
|
758
750
|
`NoSqlDbServer.getCommittedWrites()` and the database-scoped wire command
|
|
@@ -789,13 +781,16 @@ an application write; subsequent serving publications start retained coverage
|
|
|
789
781
|
after that boundary. A move preserving the provider identity preserves the
|
|
790
782
|
cursor epoch, but any uncovered native publication still reports a gap.
|
|
791
783
|
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
before
|
|
797
|
-
|
|
798
|
-
|
|
784
|
+
A start converts a root that a 10.x release wrote, a 10.8.1 root included, into
|
|
785
|
+
this release's format (see **Storage format migration** under File startup
|
|
786
|
+
admission). The converted root keeps the 10.x root's committed position as its
|
|
787
|
+
head and carries no history of writes committed before the conversion: a cursor
|
|
788
|
+
before that head gets `gap: true`, and publications after the conversion are
|
|
789
|
+
retained from that head on. No earlier release reads the converted root. A
|
|
790
|
+
deployment pinned to 10.8.1 is unaffected until a start of this release opens
|
|
791
|
+
its root; keep a stopped copy of the root before that if you may need to go
|
|
792
|
+
back. For a root that holds auth users, a copy restored into another directory
|
|
793
|
+
is no way back (see **Storage format migration** under File startup admission).
|
|
799
794
|
|
|
800
795
|
Pages are exclusive of `after` and follow commit order. Their `cursor` advances
|
|
801
796
|
over examined publications even when none belongs to the requested database;
|
|
@@ -882,7 +877,7 @@ const released = await server.commitDatabasePublication({
|
|
|
882
877
|
});
|
|
883
878
|
```
|
|
884
879
|
|
|
885
|
-
The held barrier survives restart and blocks wire commands, transactions and index restoration for that database while unrelated databases remain available. A blocked wire command fails with `ConflictingOperationInProgress` (code 117, no error label) naming `EFENCE_PUBLICATION_HELD`. `exportHeldDatabase()` is a bounded management-only read of one pinned committed catalog, auth and fence root. It requires the exact still-held receipt and capability, returns the same canonical Extended JSON shape as `exportDatabase()`, and does not release the barrier. Ordinary export and wire access remain closed; native TTL maintenance cannot expire held documents. A wrong, stale or already released receipt fails closed.
|
|
880
|
+
The held barrier survives restart and blocks wire commands, transactions and index restoration for that database while unrelated databases remain available. A blocked wire command fails with `ConflictingOperationInProgress` (code 117, no error label) naming `EFENCE_PUBLICATION_HELD`. `exportHeldDatabase()` is a bounded management-only read of one pinned committed catalog, auth and fence root. It requires the exact still-held receipt and capability, returns the same canonical Extended JSON shape as `exportDatabase()`, and does not release the barrier. Ordinary export and wire access remain closed; native TTL maintenance cannot expire held documents. A wrong, stale or already released receipt fails closed. Commit is exact and idempotent. A successful commit removes the raw capability from durable state and retains only protected verification material. Provider/root identity and bounded validation of native fencing records make copied, incomplete or corrupt allocation state fail closed. A higher fencing token compacts resolved older receipts, so long-lived coordinators do not exhaust receipt capacity.
|
|
886
881
|
|
|
887
882
|
Durable database publication holds and allocation fencing use native file storage on the supported Linux and macOS filesystems.
|
|
888
883
|
|
|
@@ -892,7 +887,7 @@ Coordinators that do not yet have their own durable record can call `getDatabase
|
|
|
892
887
|
|
|
893
888
|
`allocateDatabaseTenant()` is the create-only control-plane API for permanently allocation-managed database names. It is available only when `allocationFencingVersion === 1`, `allocationFencingSupported === true`, and `allocationFencingRequiresDrain === true`. The request requires literal `expectedAbsent: true`, an `INoSqlDbResourceFence`, and the tenant database, username, and password. `roles` is optional and defaults to `['readWrite', 'dbAdmin']`. Under native ownership and database/control admission, NoSQLDB proves catalog and principal absence, then commits the catalog, principal, complete auth allocation binding and fencing receipt at one position. It returns an `INoSqlDbAllocateDatabaseTenantResult`; its `.allocation` field is the durable `INoSqlDbDatabaseAllocationIdentity`. Exact fence replay returns the same allocation ID, generation, principal ID, and receipt digests.
|
|
894
889
|
|
|
895
|
-
Pass the returned `allocation` identity to every later `ensureDatabaseTenant()`, `importDatabase()`, and `deleteDatabaseTenant()` request for that name. Managed import and delete requests also require the exact allocation `username`; the identity alone is insufficient. Each distinct managed mutation requires a strictly newer fence token; the current token is accepted only for exact receipt replay or continuation, and older receipts become explicitly stale after advancement. Held publication receipts carry the allocation into `commitDatabasePublication()`. Managed requests without it and unmanaged requests with it fail closed. Deprovision drains affected runtime authority, then atomically retires the catalog, exact principal, grants and auth binding with a permanent `deprovisioned` fencing tombstone. A later allocation requires a newer fence token, increments generation, and receives a fresh allocation and principal identity. Wire data access is available only while active and only to the current allocation principal; any other principal is refused with `Unauthorized` (code 13), and while the allocation is not active every wire command on the database is refused with `Unauthorized` naming `EALLOCATION_DEPROVISIONED`. The database/user ownership-changing wire commands `dropDatabase`, `createUser`, `updateUser`, `dropUser`, `grantRolesToUser` and `revokeRolesFromUser` are refused with `Unauthorized` (code 13),
|
|
890
|
+
Pass the returned `allocation` identity to every later `ensureDatabaseTenant()`, `importDatabase()`, and `deleteDatabaseTenant()` request for that name. Managed import and delete requests also require the exact allocation `username`; the identity alone is insufficient. Each distinct managed mutation requires a strictly newer fence token; the current token is accepted only for exact receipt replay or continuation, and older receipts become explicitly stale after advancement. Held publication receipts carry the allocation into `commitDatabasePublication()`. Managed requests without it and unmanaged requests with it fail closed. Deprovision drains affected runtime authority, then atomically retires the catalog, exact principal, grants and auth binding with a permanent `deprovisioned` fencing tombstone. A later allocation requires a newer fence token, increments generation, and receives a fresh allocation and principal identity. Wire data access is available only while active and only to the current allocation principal; any other principal is refused with `Unauthorized` (code 13), and while the allocation is not active every wire command on the database is refused with `Unauthorized` naming `EALLOCATION_DEPROVISIONED`. The database/user ownership-changing wire commands `dropDatabase`, `createUser`, `updateUser`, `dropUser`, `grantRolesToUser` and `revokeRolesFromUser` are refused with `Unauthorized` (code 13), the MongoDB-compatible refusal of a user without the privilege.
|
|
896
891
|
|
|
897
892
|
Allocation bindings are native records, excluded from logical database exports and never accepted from import payloads. Startup and stopped-storage inspection require the complete matching fence and auth binding even when no read grant was ever issued. Missing bindings are rejected without reconstruction. This includes allocated roots produced by experimental native versions that did not create the complete binding. This release provides no automatic conversion or downgrade path; never remove or edit private metadata to force admission.
|
|
898
893
|
|
|
@@ -996,7 +991,7 @@ await client.db('admin').command({ usersInfo: 'reader' });
|
|
|
996
991
|
| Method / Property | Type | Description |
|
|
997
992
|
|---|---|---|
|
|
998
993
|
| `start(options?)` | `Promise<void>` | Start under one optional cancellation/deadline budget; join partial sidecars before rejection or retain ownership for `stop()` retry if termination fails |
|
|
999
|
-
| `stop()` | `Promise<
|
|
994
|
+
| `stop()` | `Promise<INoSqlDbServerStopResult>` | Wait for active startup work, then stop the server and confirm Rust bridge cleanup, including partial startup cleanup; resolves with how the engine ended |
|
|
1000
995
|
| `getConnectionUri()` | `string` | Get the active `mongodb://` URI; before start and after stop, port `0` remains unresolved |
|
|
1001
996
|
| `running` | `boolean` | Whether the server is currently running |
|
|
1002
997
|
| `port` | `number` | Actual bound port while running; otherwise the configured port (TCP mode) |
|
|
@@ -1024,6 +1019,30 @@ await client.db('admin').command({ usersInfo: 'reader' });
|
|
|
1024
1019
|
| `deleteDatabaseTenant(params, options?)` | `Promise<INoSqlDbDeleteDatabaseTenantResult>` | Durably delete an exact tenant database/user, optionally leaving publication held; supports fail-stop cancellation and deadlines |
|
|
1025
1020
|
| `commitDatabasePublication(params, options?)` | `Promise<TNoSqlDbCommitDatabasePublicationResult>` | Idempotently release an exact held publication receipt; supports fail-stop cancellation and deadlines |
|
|
1026
1021
|
|
|
1022
|
+
`stop()` resolves with an `INoSqlDbServerStopResult` that says how the engine
|
|
1023
|
+
ended: `stopRequestSent`, whether a stop request was sent to a running
|
|
1024
|
+
engine; `stopAcknowledged`, whether the engine acknowledged it;
|
|
1025
|
+
`forcedByServer`, whether the server had to signal or kill the engine to end
|
|
1026
|
+
it; and `exitCode` and `exitSignal`, as the engine process's exit reported
|
|
1027
|
+
them, each `null` where there was none. `clean` is true exactly when the
|
|
1028
|
+
request was sent and acknowledged, the server neither signalled nor killed the
|
|
1029
|
+
engine, and the engine exited with code 0 and no signal. After an acknowledged
|
|
1030
|
+
request, `stop()` closes the engine's management input and gives the engine
|
|
1031
|
+
`noSqlDbServerStopGraceMs` (10,000 ms) to exit by itself; only an engine that
|
|
1032
|
+
has not exited by then, or one that did not acknowledge, is ended by the
|
|
1033
|
+
server. A stop when no engine runs resolves with a result that says no request
|
|
1034
|
+
was sent, and concurrent callers share one result. `stop()` rejects only when
|
|
1035
|
+
the bridge's termination fails, as before. `LocalNoSqlDb.stop()` resolves
|
|
1036
|
+
with its server's result.
|
|
1037
|
+
|
|
1038
|
+
A start that meets a storage root, or a feature of one, from a later NoSQLDB
|
|
1039
|
+
release than the one starting refuses it before it writes anything there and
|
|
1040
|
+
creates no root in its place. `start()` of `NoSqlDbServer` and of
|
|
1041
|
+
`LocalNoSqlDb` then rejects with a `NoSqlDbUnknownRootError`, whose `code`
|
|
1042
|
+
is `UNKNOWN_ROOT` and whose message names that cause; its `cause` is the
|
|
1043
|
+
management error that carried the code. A caller tells this refusal apart
|
|
1044
|
+
from every other start failure by the code, never by the message.
|
|
1045
|
+
|
|
1027
1046
|
### NoSqlDbStorageRehearsal
|
|
1028
1047
|
|
|
1029
1048
|
`NoSqlDbStorageRehearsal.createDestination(input, options?)` creates an isolated,
|
|
@@ -1113,11 +1132,14 @@ publication and validated on retry. Every other record's bytes and version are
|
|
|
1113
1132
|
preserved, with unchanged complete non-profile record count and fingerprint.
|
|
1114
1133
|
Its authority admits no internal relocation or alignment workspace. An
|
|
1115
1134
|
unrelated staged workspace rejects before native opening, append or recovery.
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1135
|
+
This release neither rehearses a root that a 10.x release wrote nor finishes
|
|
1136
|
+
a rehearsal that a 10.x release began, or returns the receipt of one that it
|
|
1137
|
+
finished. A request whose source, destination or unfinished copy is such a
|
|
1138
|
+
root, in whole or in part, fails with `UNSUPPORTED`, and the retry of one
|
|
1139
|
+
whose 10.x roots starts have converted since fails with `UNSUPPORTED` or
|
|
1140
|
+
`SOURCE_CHANGED`; no such request changes either path or the operation
|
|
1141
|
+
workspace. Finish a 10.x rehearsal with that release before a start converts
|
|
1142
|
+
its source, or rehearse the converted root into another destination.
|
|
1121
1143
|
|
|
1122
1144
|
Active source owners, rehearsal-as-source, old or mixed layouts, unknown entries,
|
|
1123
1145
|
unfinished import or fencing operations, active read grants, unfinished or
|
|
@@ -1220,7 +1242,7 @@ underlying server and bridge are constructed by `LocalNoSqlDb.start()`.
|
|
|
1220
1242
|
| Method / Property | Type | Description |
|
|
1221
1243
|
|---|---|---|
|
|
1222
1244
|
| `start()` | `Promise<ILocalNoSqlDbConnectionInfo>` | Start and return connection info |
|
|
1223
|
-
| `stop()` | `Promise<
|
|
1245
|
+
| `stop()` | `Promise<INoSqlDbServerStopResult>` | Stop the server; resolves with its server's stop result |
|
|
1224
1246
|
| `getConnectionInfo()` | `ILocalNoSqlDbConnectionInfo` | Get current connection info |
|
|
1225
1247
|
| `getConnectionUri()` | `string` | Get the connection URI |
|
|
1226
1248
|
| `getServer()` | `NoSqlDbServer` | Access the underlying server |
|
|
@@ -1288,9 +1310,10 @@ source, inspect it for pins, copy it while it remains stopped, and align the cop
|
|
|
1288
1310
|
before its first start. Pins cannot prove that an unverified backup is complete;
|
|
1289
1311
|
a file truncated exactly at a commit boundary can still be internally consistent.
|
|
1290
1312
|
|
|
1291
|
-
Version 1 requires current production file storage, a clean committed tail
|
|
1292
|
-
|
|
1293
|
-
publication-hold, grant or import-stage authority.
|
|
1313
|
+
Version 1 requires current production file storage, a clean committed tail that
|
|
1314
|
+
no serving open would still repair, no auth users, and no fencing, allocation,
|
|
1315
|
+
publication-hold, grant or import-stage authority. A root that a 10.x release
|
|
1316
|
+
wrote is refused; a start converts it first. The complete catalog, auth,
|
|
1294
1317
|
index, transaction-outcome and fencing audits must pass. Rehearsals, memory
|
|
1295
1318
|
storage, unknown files (including `.DS_Store`/AppleDouble), symlinks, hard links
|
|
1296
1319
|
and unsafe ownership or modes are refused. The operation never repairs them.
|
|
@@ -1323,8 +1346,9 @@ existing file is never overwritten. Use the relocation identifier rules for
|
|
|
1323
1346
|
`acknowledgeUnverifiedCopy: true` when no source pins survive, never both.
|
|
1324
1347
|
`binaryPath`, `timeoutMs` and `signal` select the engine and bound the operation.
|
|
1325
1348
|
|
|
1326
|
-
The following archive recipe was qualified
|
|
1327
|
-
|
|
1349
|
+
The following archive recipe was qualified on this release's format-3 roots,
|
|
1350
|
+
from macOS arm64 APFS to Linux x86_64 ext4 and XFS (alignment.md records the
|
|
1351
|
+
run). Run as the service user, with the source stopped throughout,
|
|
1328
1352
|
and retain its inspection pins separately. Start with a new private destination
|
|
1329
1353
|
parent; preserve the private file/directory modes and do not include filesystem
|
|
1330
1354
|
metadata sidecars. On macOS use the exact canonical case and Unicode spelling;
|
|
@@ -1365,16 +1389,16 @@ include `INVALID_REQUEST`, `NOT_FOUND`, `DESTINATION_CONFLICT`, `STATE_CORRUPT`,
|
|
|
1365
1389
|
`abortStoppedStorageRootAlignment(request, options?)` accepts the identical
|
|
1366
1390
|
request and only rolls back an exact **incomplete** planned append. A complete
|
|
1367
1391
|
append must finish alignment. If permissions changed after interruption, restore
|
|
1368
|
-
the required ownership and modes first. Abort permits changed
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1392
|
+
the required ownership and modes first. Abort permits a changed change time,
|
|
1393
|
+
which restoring ownership or modes leaves, while still checking every file's
|
|
1394
|
+
identity, length and modification time, the original bytes, and that the planned
|
|
1395
|
+
append's place holds only its own bytes or zeros. It durably records abort,
|
|
1396
|
+
writes zeros where the interrupted append wrote and syncs, removes its exact
|
|
1397
|
+
reserved receipt and cleans up. Retry abort after an interrupted abort; alignment
|
|
1398
|
+
cannot override that decision. An absent workspace returns `not-pending`.
|
|
1399
|
+
|
|
1400
|
+
Finish or abort a pending alignment with this release; deleting the workspace is
|
|
1401
|
+
not a fix. No earlier release reads a root that this release has aligned.
|
|
1378
1402
|
Qualification results and study corrections are recorded in the repository's
|
|
1379
1403
|
[alignment design note](https://code.foss.global/lossless.org/nosqldb/src/branch/main/alignment.md).
|
|
1380
1404
|
|
|
@@ -1387,7 +1411,14 @@ through the complete record-owner audit. A running engine returns `BUSY`.
|
|
|
1387
1411
|
|
|
1388
1412
|
An `audited` result includes the recorded provider identity, live binding state,
|
|
1389
1413
|
lineage (`groupId`/`timelineId`), decimal-string LSN and counts, full-record and
|
|
1390
|
-
profile-excluded SHA-256 digests,
|
|
1414
|
+
profile-excluded SHA-256 digests, `storageFormat: 'format-3'` for a root of this
|
|
1415
|
+
release or the generation inventory of a root that a 10.x release wrote, and
|
|
1416
|
+
alignment eligibility. A format-3 root that a serving open would still repair,
|
|
1417
|
+
such as one whose log ends inside an unfinished write, is ineligible with
|
|
1418
|
+
`RECOVERY_PENDING`. A root that a 10.x release wrote is audited read-only as that
|
|
1419
|
+
release audited it, with the same identity, counts, digests and generation
|
|
1420
|
+
inventory, and is ineligible with `CONVERSION_PENDING` first among its reasons:
|
|
1421
|
+
this release does not align it, and a start converts it first.
|
|
1391
1422
|
It counts allocation replays as well as fences, receipts, holds and allocations.
|
|
1392
1423
|
The audit accepts no active grants or import stages. Auth users or authority
|
|
1393
1424
|
history make a fully audited root ineligible for independent-fork alignment.
|
|
@@ -1407,10 +1438,15 @@ the TypeScript interfaces for older engine binaries.
|
|
|
1407
1438
|
#### Offline String-Value Inspection
|
|
1408
1439
|
|
|
1409
1440
|
`LocalNoSqlDb.inspectOfflineStringValue(input, options?)` reads one string from a
|
|
1410
|
-
stopped
|
|
1441
|
+
stopped native file root of this release, or read-only from one that a 10.x
|
|
1442
|
+
release wrote, on supported Linux filesystems or macOS APFS.
|
|
1411
1443
|
It starts only a disposable management sidecar, acquires the existing native
|
|
1412
1444
|
leases read-only, and pins the committed root. It never starts a listener,
|
|
1413
|
-
initializes storage
|
|
1445
|
+
initializes storage or writes to the source: a root of this release is
|
|
1446
|
+
recovered in memory only, as a start would recover it, and a cut log tail or a
|
|
1447
|
+
leftover anchor replacement stays on disk as found. A root that a 10.x release
|
|
1448
|
+
wrote is read up to its committed state, and one that a 10.x process left
|
|
1449
|
+
without a clean stop is refused with `RECOVERY_REQUIRED`.
|
|
1414
1450
|
|
|
1415
1451
|
```typescript
|
|
1416
1452
|
import { LocalNoSqlDb } from '@lossless.org/nosqldb';
|
|
@@ -1501,7 +1537,7 @@ await debugServer.start();
|
|
|
1501
1537
|
await debugServer.stop();
|
|
1502
1538
|
```
|
|
1503
1539
|
|
|
1504
|
-
The UI is bundled as base64-encoded content (via `@git.zone/
|
|
1540
|
+
The UI is bundled as base64-encoded content (via `tsrepo bundle` from `@git.zone/tsrepo`) and served from memory — no static file directory needed.
|
|
1505
1541
|
|
|
1506
1542
|
### NoSqlDbDebugUi (Web Component)
|
|
1507
1543
|
|
|
@@ -1553,72 +1589,20 @@ await collection.findOneAndReplace({ _id: id }, { name: 'Replaced' }, { returnDo
|
|
|
1553
1589
|
|
|
1554
1590
|
### Query Operators
|
|
1555
1591
|
|
|
1556
|
-
|
|
1557
|
-
// Comparison
|
|
1558
|
-
{ age: { $eq: 25 } } { age: { $ne: 25 } }
|
|
1559
|
-
{ age: { $gt: 18 } } { age: { $lt: 65 } }
|
|
1560
|
-
{ age: { $gte: 18 } } { age: { $lte: 65 } }
|
|
1561
|
-
{ status: { $in: ['active', 'pending'] } }
|
|
1562
|
-
{ status: { $nin: ['deleted'] } }
|
|
1563
|
-
|
|
1564
|
-
// Logical
|
|
1565
|
-
{ $and: [{ age: { $gte: 18 } }, { active: true }] }
|
|
1566
|
-
{ $or: [{ status: 'active' }, { admin: true }] }
|
|
1567
|
-
{ $not: { status: 'deleted' } }
|
|
1568
|
-
|
|
1569
|
-
// Element
|
|
1570
|
-
{ email: { $exists: true } }
|
|
1571
|
-
{ type: { $type: 'string' } } { value: { $type: ['int', 'long'] } }
|
|
1572
|
-
|
|
1573
|
-
// Array
|
|
1574
|
-
{ tags: { $all: ['mongodb', 'database'] } }
|
|
1575
|
-
{ scores: { $elemMatch: { $gte: 80, $lt: 90 } } }
|
|
1576
|
-
{ labels: { $not: { $elemMatch: { $nin: ['linux', 'gpu'] } } } }
|
|
1577
|
-
{ items: { $elemMatch: { sku: 'a', qty: { $gt: 2 } } } }
|
|
1578
|
-
{ items: { $all: [{ $elemMatch: { sku: 'a' } }, { $elemMatch: { qty: 0 } }] } }
|
|
1579
|
-
{ tags: { $size: 3 } }
|
|
1580
|
-
|
|
1581
|
-
// Regex
|
|
1582
|
-
{ name: /^Al/i } { name: { $regex: '^al', $options: 'i' } }
|
|
1583
|
-
{ tags: { $in: [/^db/, 'mongodb'] } }
|
|
1584
|
-
|
|
1585
|
-
// Aggregation expressions
|
|
1586
|
-
{ owner: 'o', $expr: { $lte: [{ $add: ['$retainedBytes', '$reservedBytes', size] }, max] } }
|
|
1587
|
-
{ $expr: { $gt: ['$leaseExpiresAt', '$$NOW'] } }
|
|
1588
|
-
```
|
|
1589
|
-
|
|
1590
|
-
`$type` takes a type alias (`'string'`, `'number'`, …), a numeric BSON type
|
|
1591
|
-
code or an array of them, and matches an array field through its elements as
|
|
1592
|
-
well as `'array'` itself.
|
|
1593
|
-
|
|
1594
|
-
`$comment` annotates a filter without constraining it, at the top level and
|
|
1595
|
-
in an `$and`, `$or`, `$nor` or `$elemMatch` document; under a field it is
|
|
1596
|
-
refused as an unknown operator, as on MongoDB.
|
|
1597
|
-
|
|
1598
|
-
A filter is checked as MongoDB 8 checks it, before any document is read and
|
|
1599
|
-
with its messages: `$and`, `$or` and `$nor` need a non-empty array of
|
|
1600
|
-
documents, `$in`, `$nin` and `$all` an array, and a `$in` or `$nin` member
|
|
1601
|
-
whose first field starts with `$`, such as `{ $gt: 1 }`, is refused
|
|
1602
|
-
(`cannot nest $ under $in`), each with `BadValue` (2). A DBRef member such as
|
|
1603
|
-
`{ $ref: 'users', $id: id }` is a value. Partial index filters and `$pull`
|
|
1604
|
-
conditions refuse the query features MongoDB 8 bans there: `$expr` and
|
|
1605
|
-
`$jsonSchema` with `QueryFeatureNotAllowed` (224), `$where` and `$text` with
|
|
1606
|
-
`BadValue`, and `$near`, `$nearSphere` and `$geoNear` with `Location5626500`.
|
|
1607
|
-
|
|
1608
|
-
`$elemMatch` has MongoDB's two forms. An operand whose first key is a query
|
|
1592
|
+
`$elemMatch` has two forms, implemented to be MongoDB compatible. An operand whose first key is a query
|
|
1609
1593
|
operator applies its operators (`$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`,
|
|
1610
1594
|
`$in`, `$nin`, `$exists`, `$type`, `$regex`, `$size`, `$mod`, `$all`, `$not`,
|
|
1611
1595
|
`$elemMatch`) to each element as a whole; any other operand is a query over
|
|
1612
1596
|
each embedded document, and a nested array is matched as a document keyed by
|
|
1613
1597
|
index. A dotted path tests every array it reaches on its own. `$expr` inside
|
|
1614
|
-
`$elemMatch` is refused with `BadValue` (2),
|
|
1598
|
+
`$elemMatch` is refused with `BadValue` (2), implemented to be MongoDB compatible.
|
|
1615
1599
|
|
|
1616
1600
|
A regular expression matches the same way wherever a filter holds one — as a
|
|
1617
1601
|
field's value, through `$regex` and `$options`, in a `$in`, `$nin` or `$all`
|
|
1618
1602
|
list, under `$not`, and as a `$pull` condition: a string or symbol matches when
|
|
1619
1603
|
the pattern finds a match in it, an array matches through its elements, a
|
|
1620
1604
|
stored regular expression matches only when its pattern and options are equal,
|
|
1621
|
-
and a missing field never matches. `$options` takes MongoDB
|
|
1605
|
+
and a missing field never matches. `$options` takes the MongoDB-compatible flags `i`, `m`,
|
|
1622
1606
|
`s`, `x` and `u`. Patterns run on Rust's `regex` crate rather than PCRE, so
|
|
1623
1607
|
matching takes time linear in the input, constructs only PCRE has
|
|
1624
1608
|
(look-around, backreferences, `\Q…\E`, `\Z`, `(?#…)` comments, a `{` that
|
|
@@ -1627,28 +1611,16 @@ starts no repetition) are refused as invalid, and a few match differently:
|
|
|
1627
1611
|
does not match before a final newline, a possessive quantifier such as `a++`
|
|
1628
1612
|
is greedy, and `x` ignores whitespace inside a character class too. A
|
|
1629
1613
|
pattern is checked when the filter is validated, before any document is read
|
|
1630
|
-
and even when the collection does not exist, with MongoDB
|
|
1631
|
-
MongoDB
|
|
1614
|
+
and even when the collection does not exist, with MongoDB-compatible errors in
|
|
1615
|
+
a MongoDB-compatible order: options given in both `$regex` and `$options` are
|
|
1632
1616
|
`Location51074` or `Location51075`, an unknown flag `Location51108`, a null
|
|
1633
1617
|
byte `BadValue` (2), a pattern longer than 32,764 bytes or one that does not
|
|
1634
1618
|
compile `Location51091` `Regular expression is invalid: <reason>` (the reason
|
|
1635
|
-
is the crate's, such as `unclosed group` where MongoDB
|
|
1619
|
+
is the crate's, such as `unclosed group` where MongoDB, as observed by runtime testing, answers `missing closing
|
|
1636
1620
|
parenthesis`), and a regular expression as the operand of `$ne`, `$gt`,
|
|
1637
1621
|
`$gte`, `$lt` or `$lte` `BadValue` (2). A match charges the work budget the
|
|
1638
1622
|
size of the field's value times the length of the pattern.
|
|
1639
1623
|
|
|
1640
|
-
`$expr` evaluates an aggregation expression per document in `find`, `count`,
|
|
1641
|
-
`distinct`, `update`, `delete`, `findAndModify` and a `$match` stage, and
|
|
1642
|
-
matches when the result is truthy. It accepts the operators listed under
|
|
1643
|
-
[Aggregation Pipeline](#aggregation-pipeline). Evaluation is charged against
|
|
1644
|
-
the same work budget as every other predicate, and an operator the engine does
|
|
1645
|
-
not implement is refused by name with `TypeMismatch` (code 14) before any
|
|
1646
|
-
document is read. `$$NOW` is one instant per command, as in MongoDB: the
|
|
1647
|
-
engine reads the clock once when the command starts, and the command's filter,
|
|
1648
|
-
every document of a pipeline update, every aggregation stage, `$lookup` and
|
|
1649
|
-
`$unionWith` sub-pipeline, and every `getMore` of its cursor see that instant.
|
|
1650
|
-
Filters outside a command, such as a partial index filter, have no `$$NOW`.
|
|
1651
|
-
|
|
1652
1624
|
### Update Operators
|
|
1653
1625
|
|
|
1654
1626
|
```typescript
|
|
@@ -1671,8 +1643,8 @@ may be an array of `$addFields`, `$set`, `$project`, `$unset`, `$replaceRoot`
|
|
|
1671
1643
|
or `$replaceWith` stages, and any other stage is refused by name. The pipeline
|
|
1672
1644
|
must produce exactly one document and cannot change `_id`.
|
|
1673
1645
|
|
|
1674
|
-
An upsert that matches nothing inserts the filter's equality conditions,
|
|
1675
|
-
MongoDB
|
|
1646
|
+
An upsert that matches nothing inserts the filter's equality conditions, to be
|
|
1647
|
+
MongoDB compatible: top-level fields and `$and` clauses, `$eq`, a single-element
|
|
1676
1648
|
`$in`, `$all` members and a single-clause `$or`. A dotted path such as
|
|
1677
1649
|
`'owner.id'` becomes a nested document, and a regular expression is copied only
|
|
1678
1650
|
as an `$eq` operand. A replacement takes only `_id` from the filter and fails
|
|
@@ -1695,64 +1667,6 @@ const results = await collection.aggregate([
|
|
|
1695
1667
|
|
|
1696
1668
|
**Supported stages:** `$match`, `$project`, `$group`, `$sort`, `$limit`, `$skip`, `$unwind`, `$lookup`, `$addFields`, `$count`, `$facet`, `$replaceRoot`, `$set`, `$unionWith`, `$out`, `$merge`
|
|
1697
1669
|
|
|
1698
|
-
**Supported expression operators:**
|
|
1699
|
-
|
|
1700
|
-
| Group | Operators |
|
|
1701
|
-
| --- | --- |
|
|
1702
|
-
| Comparison | `$eq`, `$ne`, `$gt`, `$gte`, `$lt`, `$lte`, `$cmp` |
|
|
1703
|
-
| Arithmetic | `$add`, `$subtract`, `$multiply`, `$divide` |
|
|
1704
|
-
| Logical | `$and`, `$or`, `$not` |
|
|
1705
|
-
| Conditional and variables | `$cond` (array or `if`/`then`/`else` form), `$ifNull` (two or more operands), `$let` |
|
|
1706
|
-
| Type and arrays | `$isNumber`, `$type`, `$in`, `$size`, `$arrayElemAt` |
|
|
1707
|
-
| Dates | `$dateAdd` |
|
|
1708
|
-
| Collections | `$map`, `$objectToArray`, `$setEquals` |
|
|
1709
|
-
| Literals | `$literal` |
|
|
1710
|
-
|
|
1711
|
-
plus `$field` paths, `$$variable` references including `$$ROOT` and `$$NOW`,
|
|
1712
|
-
and documents or arrays of those. Every other operator is refused by name with
|
|
1713
|
-
`TypeMismatch` (code 14) before any document is read; the engine never treats
|
|
1714
|
-
an operator it cannot execute as a literal value.
|
|
1715
|
-
|
|
1716
|
-
Expressions follow MongoDB's value semantics. A field that is absent is
|
|
1717
|
-
*missing*, which is not `null`: `$type` answers `'missing'`, `$ifNull`
|
|
1718
|
-
replaces both, and comparisons order values as MongoDB does (MinKey, missing,
|
|
1719
|
-
`null`, numbers, strings, objects, arrays, binary data, ObjectIds, booleans,
|
|
1720
|
-
dates, timestamps, regular expressions, MaxKey), so `{ $eq: ['$absent', null] }`
|
|
1721
|
-
is false. Numbers compare by value across their wire types. A path through an
|
|
1722
|
-
array maps over its elements and always answers an array: `'$mailbox.path'`
|
|
1723
|
-
over one looked-up document is `['INBOX']`, so `$arrayElemAt` and `$size`
|
|
1724
|
-
apply to it. Arithmetic answers `null` when an operand is `null` or missing;
|
|
1725
|
-
an int result that overflows widens to a long and a long to a double, a double
|
|
1726
|
-
operand makes the result a double, and `$divide` always answers a double.
|
|
1727
|
-
`$add` accepts one date and answers a date; date minus date is milliseconds.
|
|
1728
|
-
Runtime errors keep MongoDB 7's codes: a non-numeric operand is
|
|
1729
|
-
`TypeMismatch` (14), `can't $divide by zero` is `BadValue` (2), a date that
|
|
1730
|
-
leaves the 64-bit range is `Overflow` (15), and the array and date operators
|
|
1731
|
-
answer MongoDB's location codes (a second date in `$add` 16612, `$in` without
|
|
1732
|
-
an array 40081, `$size` without an array 17124, `$arrayElemAt` 28689–28691,
|
|
1733
|
-
`$dateAdd` 5166403, 5166405, 5166406 and 5439013, a non-string time zone
|
|
1734
|
-
40517, an unknown unit `FailedToParse` (9)). Decimal128 operands in arithmetic
|
|
1735
|
-
are refused with `TypeMismatch` (14) — decimal arithmetic is not
|
|
1736
|
-
implemented. `$and`, `$or`, `$cond` and `$ifNull`
|
|
1737
|
-
evaluate, and charge, only the operands MongoDB evaluates, so a guard keeps an
|
|
1738
|
-
invalid branch from failing the statement. `$dateAdd` computes in UTC with the
|
|
1739
|
-
units `millisecond` to `year`, clamping `month`, `quarter` and `year` to the
|
|
1740
|
-
end of the target month; a `timezone` other than UTC is refused by name.
|
|
1741
|
-
Within a document expression a member that is missing is still written as
|
|
1742
|
-
`null`, where MongoDB omits the member.
|
|
1743
|
-
|
|
1744
|
-
`$lookup` supports the equality form and a bounded correlated pipeline form with
|
|
1745
|
-
`let`, an optional `$match` (any filter, `$expr` included), and an optional
|
|
1746
|
-
following `$limit`. A `let` variable that reads an absent local field is bound
|
|
1747
|
-
to the missing value, as in MongoDB. Correlated string equality predicates
|
|
1748
|
-
(`$expr` with `$eq`, optionally under `$and`) can use a full-key or subset
|
|
1749
|
-
foreign equality index to reduce candidates when available. Candidates are
|
|
1750
|
-
paged, the complete expression remains authoritative, and `$limit` counts only
|
|
1751
|
-
exact expression matches. Other BSON value shapes use the bounded scan path.
|
|
1752
|
-
Mixed `localField`/`foreignField` plus `pipeline` lookups and other inner stages
|
|
1753
|
-
are rejected explicitly. Complete filter trees are validated before data is
|
|
1754
|
-
read, and query nesting plus aggregation work are bounded.
|
|
1755
|
-
|
|
1756
1670
|
A leading `$match` narrows the pipeline's source load instead of filtering
|
|
1757
1671
|
after it: only matching documents are materialized, using an equality index when
|
|
1758
1672
|
one serves the filter and a bounded scan otherwise. Filter shapes the query
|
|
@@ -1772,19 +1686,6 @@ describes, as it does on its own.
|
|
|
1772
1686
|
|
|
1773
1687
|
**Group accumulators:** `$sum`, `$avg`, `$min`, `$max`, `$first`, `$last`, `$push`, `$addToSet`, `$count`
|
|
1774
1688
|
|
|
1775
|
-
`$group._id` accepts a literal (`null`, a number, a string), a `$field`
|
|
1776
|
-
reference, or a document of expressions: `{ _id: { owner: '$owner', repo:
|
|
1777
|
-
'$repo' } }` groups by the evaluated pair, one group per distinct composite
|
|
1778
|
-
key. Inside a composite `_id`, a member whose field is absent evaluates to
|
|
1779
|
-
`null` and stays part of the key, where MongoDB omits the member instead.
|
|
1780
|
-
Accumulator operands accept the same expression forms. A field reference
|
|
1781
|
-
resolves per document, and a missing field is not a value: `$sum`, `$avg`,
|
|
1782
|
-
`$min`, `$max`, `$push` and `$addToSet` skip it, while `$first` and `$last`
|
|
1783
|
-
record `null` for it. Every field beside `_id` must hold an accumulator
|
|
1784
|
-
document naming exactly one accumulator, such as `{ $sum: 1 }`, and an operator
|
|
1785
|
-
outside the two lists above is refused with `TypeMismatch` (code 14) when the
|
|
1786
|
-
stage is parsed.
|
|
1787
|
-
|
|
1788
1689
|
### Indexes
|
|
1789
1690
|
|
|
1790
1691
|
```typescript
|
|
@@ -1798,77 +1699,8 @@ await collection.dropIndexes(); // drop all except _id
|
|
|
1798
1699
|
|
|
1799
1700
|
> 🛡️ **Unique indexes are enforced at the engine level.** Duplicate values are rejected with a `DuplicateKey` error (code 11000) *before* the document is written to disk — on `insertOne`, `updateOne`, `findAndModify`, and upserts. Index definitions and entries are committed with native storage and survive restart.
|
|
1800
1701
|
|
|
1801
|
-
NoSQLDB supports ascending and descending index keys with `name`, `unique`,
|
|
1802
|
-
`sparse`, `partialFilterExpression` and `expireAfterSeconds` options. It
|
|
1803
|
-
accepts `background` as a validated no-op and index version `v: 2`.
|
|
1804
|
-
Unsupported options, including `collation` and `hidden`, are rejected before
|
|
1805
|
-
any index in the request is created. A legacy `ns` field is accepted only when
|
|
1806
|
-
it names the target namespace. With `ignoreUnknownIndexOptions: true`,
|
|
1807
|
-
`createIndexes` removes the fields MongoDB 7 does not know as index options
|
|
1808
|
-
(and plugin-specific fields such as `weights` or `bits` on an ascending or
|
|
1809
|
-
descending key) before validation, as MongoDB does; options MongoDB knows but
|
|
1810
|
-
NoSQLDB does not implement are still rejected. Native maintenance expires eligible BSON
|
|
1811
|
-
dates and date arrays in bounded batches, preserving publication holds and
|
|
1812
|
-
active snapshots.
|
|
1813
|
-
|
|
1814
|
-
**Partial indexes.** An index with a `partialFilterExpression` holds only the
|
|
1815
|
-
documents that match the filter, and a unique partial index enforces
|
|
1816
|
-
uniqueness among those documents only:
|
|
1817
|
-
|
|
1818
|
-
```typescript
|
|
1819
|
-
await jobs.createIndex(
|
|
1820
|
-
{ activeClaimKey: 1 },
|
|
1821
|
-
{ unique: true, partialFilterExpression: { activeClaimKey: { $exists: true } } },
|
|
1822
|
-
);
|
|
1823
|
-
await pulls.createIndex(
|
|
1824
|
-
{ org: 1, repo: 1, sourceRef: 1, targetRef: 1 },
|
|
1825
|
-
{ unique: true, partialFilterExpression: { state: 'open' } },
|
|
1826
|
-
);
|
|
1827
|
-
```
|
|
1828
|
-
|
|
1829
|
-
The filter accepts MongoDB 8's subset: equality (`field: value` or `$eq`),
|
|
1830
|
-
`$exists: true`, `$gt`, `$gte`, `$lt`, `$lte`, `$type`, `$in`, whose list may
|
|
1831
|
-
hold regular expressions that match as they do in a query, `$all` of values,
|
|
1832
|
-
which is the conjunction of their equalities and counts as one level, `$and`
|
|
1833
|
-
and `$or`, nested at most four expression levels deep; `$comment` is accepted
|
|
1834
|
-
and ignored. Any other expression — `$exists: false`, `$ne`, `$nin`, `$not`,
|
|
1835
|
-
`$nor`, a regular expression outside `$in`, an empty `$all` or one holding a
|
|
1836
|
-
regular expression or `$elemMatch`, `$elemMatch` and `$size` — and a filter
|
|
1837
|
-
combined with `sparse` are refused with `CannotCreateIndex` (code 67);
|
|
1838
|
-
`$expr`, `$jsonSchema`, `$where`, `$text` and the `$near` operators are
|
|
1839
|
-
refused with the codes listed under [Query Operators](#query-operators), a
|
|
1840
|
-
malformed filter, such as an empty `$and`, is `BadValue` (2) and a
|
|
1841
|
-
non-document is `TypeMismatch` (14). As in MongoDB, the whole filter is first
|
|
1842
|
-
validated as a query, so a malformed operand or an invalid regular expression
|
|
1843
|
-
anywhere in it (`Location51091`, `Location51108`) is answered before an
|
|
1844
|
-
expression outside the subset.
|
|
1845
|
-
An update that moves a document into the filter claims its key, one that moves
|
|
1846
|
-
it out releases it, and transactions claim keys the same way, so a racing
|
|
1847
|
-
claim is a `WriteConflict` (112) with `TransientTransactionError`.
|
|
1848
|
-
`listIndexes`, canonical export and import report the filter exactly as it was
|
|
1849
|
-
created. The filter identifies the index: the same name with a different
|
|
1850
|
-
filter is `IndexKeySpecsConflict` (86); the same key with an equivalent filter
|
|
1851
|
-
under another name is `IndexOptionsConflict` (85); a different filter over the
|
|
1852
|
-
same key under another name is a separate index. Two filters are equivalent
|
|
1853
|
-
when MongoDB 8 normalises them to the same expression: implicit equality and
|
|
1854
|
-
`$eq`, numeric wire types, `$in: [1]` and `1`, an `$or` of equalities on one
|
|
1855
|
-
path and their `$in`, `$in` order and duplicates, `$all` and the `$and` of
|
|
1856
|
-
its values, nested and flat `$and` or `$or`, `$type` aliases and their codes,
|
|
1857
|
-
`$exists: true` and `1`, and a `$comment` or none compare equal, and so do
|
|
1858
|
-
conjuncts and `$or` branches on different paths or of different kinds in any
|
|
1859
|
-
order. Predicates of one kind on one path keep their written order, so
|
|
1860
|
-
`{ $and: [{ a: 1 }, { a: 2 }] }` and its reverse are separate indexes, as are a
|
|
1861
|
-
repeated conjunct and the conjunct alone, a document operand with its fields
|
|
1862
|
-
in another order, and `$type: 'number'` and the list of the four numeric
|
|
1863
|
-
types. The query
|
|
1864
|
-
planner never selects a partial index on its own; a `hint` naming one reads
|
|
1865
|
-
only its members, as MongoDB's hinted scan does. A partial TTL index expires
|
|
1866
|
-
only its members. Engines older than this release refuse a storage root that
|
|
1867
|
-
holds a partial index instead of enforcing its uniqueness over every
|
|
1868
|
-
document.
|
|
1869
|
-
|
|
1870
1702
|
**List fields.** An index on a field that holds lists has an entry per element,
|
|
1871
|
-
|
|
1703
|
+
to be compatible with MongoDB multikey indexes, and a compound index an entry per combination of
|
|
1872
1704
|
its list fields' elements. Equality, `$in` and one-sided ranges on the elements
|
|
1873
1705
|
read their bounds, and a document with several entries within them is returned
|
|
1874
1706
|
once. A list matches a range bounded on both sides when one element meets the
|
|
@@ -1879,10 +1711,12 @@ through the index. An index with element entries returns no order, so a sorted
|
|
|
1879
1711
|
read sorts its matches, and a `hint` naming it is refused. A unique index on a
|
|
1880
1712
|
list field is unique per element: a document that shares an element with
|
|
1881
1713
|
another is a `DuplicateKey` (11000), while one document may repeat an element.
|
|
1882
|
-
An index whose field already held a list under 10.7.3 or earlier
|
|
1883
|
-
whole-list entries
|
|
1884
|
-
|
|
1885
|
-
|
|
1714
|
+
An index whose field already held a list under 10.7.3 or earlier, which those
|
|
1715
|
+
releases kept with whole-list entries, gets element entries when 11.0.0
|
|
1716
|
+
converts its root at start, and is then selected like any other index; a unique
|
|
1717
|
+
one whose documents share an element refuses the start before anything is
|
|
1718
|
+
written. Engines up to 10.7.3 refuse an index with element entries instead of
|
|
1719
|
+
maintaining it with whole lists.
|
|
1886
1720
|
|
|
1887
1721
|
`find()` and collection `aggregate()` accept `hint` as an index name (including
|
|
1888
1722
|
`_id_`) or an exact ascending/descending key pattern. Key-pattern order matters;
|
|
@@ -1929,7 +1763,7 @@ Nontransaction aggregation pipelines composed of `$match`, `$project`, `$unset`,
|
|
|
1929
1763
|
`$set`, `$addFields`, `$replaceRoot`, `$replaceWith`, `$skip`, `$limit`, `$sort`,
|
|
1930
1764
|
`$group`, `$count`, `$unwind`, `$lookup`, `$unionWith`, and `$facet` also operate
|
|
1931
1765
|
beyond 10,000 input or output documents.
|
|
1932
|
-
Counts use
|
|
1766
|
+
Counts use each collection's exact count from its statistics record where applicable, otherwise stream matching
|
|
1933
1767
|
records. A limit bounds the work needed to find the requested number of matches.
|
|
1934
1768
|
Without a limit, exact counts continue to EOF. Hints, deadlines and empty-result
|
|
1935
1769
|
shapes remain supported; signed BSON count overflow and resource exhaustion
|
|
@@ -1995,8 +1829,9 @@ transaction and native commit byte admission. `$out` and `$merge` have no
|
|
|
1995
1829
|
10,000-document output ceiling; their atomic output is subject to the same write
|
|
1996
1830
|
budgets and rejects before publication if those budgets are exceeded. Native
|
|
1997
1831
|
database import uses private staging for larger supported exports, then publishes
|
|
1998
|
-
the catalog and receipt atomically.
|
|
1999
|
-
|
|
1832
|
+
the catalog and receipt atomically. A start converts a root that a 10.x release
|
|
1833
|
+
wrote (see **Storage format migration** under File startup admission); any other
|
|
1834
|
+
old storage is rejected without conversion.
|
|
2000
1835
|
|
|
2001
1836
|
### Comparing performance with MongoDB
|
|
2002
1837
|
|
|
@@ -2051,40 +1886,23 @@ time-series collections, clustered indexes, encrypted fields, and all general
|
|
|
2051
1886
|
view pipelines remain unsupported and are rejected before the database or
|
|
2052
1887
|
namespace is created.
|
|
2053
1888
|
|
|
2054
|
-
`create` accepts an `idIndex` only when it describes the primary index every
|
|
2055
|
-
collection gets: key `{ _id: 1 }`, name `_id_`, optionally `v: 2` and an `ns`
|
|
2056
|
-
naming the new collection. Any other `idIndex` is `InvalidOptions` (72).
|
|
2057
|
-
|
|
2058
1889
|
**Collection names.** A name with a NUL, an empty name and a name starting
|
|
2059
1890
|
with `.` that storage cannot hold, such as `.` or `..`, are `InvalidNamespace`
|
|
2060
|
-
(73) on every command naming them, with MongoDB
|
|
2061
|
-
command runs. A name MongoDB
|
|
1891
|
+
(73) on every command naming them, with MongoDB-compatible messages, before the
|
|
1892
|
+
command runs. A name MongoDB accepts that one storage path component cannot
|
|
2062
1893
|
hold, one with `/`, `\` or a control character or longer than 255 bytes, names
|
|
2063
1894
|
a collection that cannot exist: reads, `drop`, `viewOn` and a rename source see
|
|
2064
1895
|
it missing, and commands that create or write it are refused with
|
|
2065
1896
|
`InvalidNamespace`, or `IllegalOperation` (20) for `$out` or a rename into a
|
|
2066
|
-
name longer than 255 bytes. Such a refused write ends its transaction,
|
|
2067
|
-
|
|
2068
|
-
the collection name to 255 bytes where MongoDB
|
|
1897
|
+
name longer than 255 bytes. Such a refused write ends its transaction, to be compatible with MongoDB's
|
|
1898
|
+
refusal of a namespace longer than 255 bytes. NoSQLDB limits
|
|
1899
|
+
the collection name to 255 bytes where MongoDB, as observed by runtime testing, limits the whole
|
|
2069
1900
|
`database.collection`, and it accepts a name such as `.a` that starts with `.`
|
|
2070
|
-
but that its storage holds, which MongoDB
|
|
2071
|
-
|
|
2072
|
-
**Importing from MongoDB.** `mongorestore` (MongoDB Database Tools 100.x)
|
|
2073
|
-
restores a `mongodump` of ordinary collections with its default flags: the
|
|
2074
|
-
documents, validators and supported indexes (unique, compound, sparse,
|
|
2075
|
-
partial and TTL). It sends this `idIndex` with every `create` and
|
|
2076
|
-
`createIndexes` with each index's `ns` and `ignoreUnknownIndexOptions: true`,
|
|
2077
|
-
and it builds several collections' indexes in parallel, which wait for one
|
|
2078
|
-
another instead of failing (see "Database waits" below). Exclude the `admin`
|
|
2079
|
-
and `config` namespaces (`--nsExclude 'admin.*' --nsExclude 'config.*'`) and create users
|
|
2080
|
-
through NoSQLDB's authentication options. Collections or indexes that use
|
|
2081
|
-
unsupported features (collation, text, geospatial, hashed or wildcard
|
|
2082
|
-
indexes, capped or time-series collections) fail the restore with the
|
|
2083
|
-
refusing command's error instead of being restored differently.
|
|
1901
|
+
but that its storage holds, which MongoDB refuses.
|
|
2084
1902
|
|
|
2085
1903
|
### Collection Validators
|
|
2086
1904
|
|
|
2087
|
-
`create` and `collMod` accept MongoDB
|
|
1905
|
+
`create` and `collMod` accept the MongoDB-compatible `validator`, `validationLevel`
|
|
2088
1906
|
(`strict`, `moderate`, `off`) and `validationAction` (`error`, `warn`), and
|
|
2089
1907
|
`listCollections` reports them under `options` exactly as set:
|
|
2090
1908
|
|
|
@@ -2111,7 +1929,7 @@ is `bsonType` (every BSON alias and `number`), `type` (`object`, `array`,
|
|
|
2111
1929
|
`additionalProperties` (boolean), `minProperties`, `maxProperties`, `enum`,
|
|
2112
1930
|
`minimum`, `maximum`, `exclusiveMinimum`, `exclusiveMaximum`, `minLength`,
|
|
2113
1931
|
`maxLength`, `pattern`, `items` (one schema), `minItems`, `maxItems`, `title`
|
|
2114
|
-
and `description`, with MongoDB
|
|
1932
|
+
and `description`, with MongoDB-compatible semantics: a keyword applies only to values
|
|
2115
1933
|
of its kind and not at all when `bsonType`/`type` excludes that kind, `enum`
|
|
2116
1934
|
compares numbers by value and embedded documents regardless of field order,
|
|
2117
1935
|
NaN fails `minimum` and `maximum`, and lengths count code points. Everything
|
|
@@ -2119,9 +1937,9 @@ else is refused when the validator is set, never ignored: `patternProperties`,
|
|
|
2119
1937
|
`additionalItems`, `uniqueItems`, `dependencies`, `allOf`, `anyOf`, `oneOf`,
|
|
2120
1938
|
`not`, `multipleOf`, `encrypt`, `encryptMetadata`, `items` as an array and
|
|
2121
1939
|
`additionalProperties` as a schema answer `FailedToParse` (9) `$jsonSchema
|
|
2122
|
-
keyword '…' is not currently supported`,
|
|
1940
|
+
keyword '…' is not currently supported`, the MongoDB-compatible answer for `$ref`,
|
|
2123
1941
|
`format`, `default`, `definitions`, `id` and `$schema`; `$jsonSchema` below the
|
|
2124
|
-
top level answers `BadValue` (2). Malformed validators answer MongoDB
|
|
1942
|
+
top level answers `BadValue` (2). Malformed validators answer MongoDB-compatible codes
|
|
2125
1943
|
(2, 9, 14, `Location51091` for an invalid `pattern`). `pattern` uses the same
|
|
2126
1944
|
regular-expression dialect as `$regex`; constructs only PCRE supports are
|
|
2127
1945
|
refused as invalid.
|
|
@@ -2133,10 +1951,10 @@ written document; `moderate` skips updates to documents that did not satisfy
|
|
|
2133
1951
|
the validator before the update; `off` validates nothing; a no-op update and a
|
|
2134
1952
|
delete are never validated. A refused document answers
|
|
2135
1953
|
`DocumentValidationFailure` (121), `errmsg: "Document failed validation"`, and
|
|
2136
|
-
MongoDB
|
|
1954
|
+
the MongoDB-compatible `errInfo: { failingDocumentId, details }` — as a write error of
|
|
2137
1955
|
`insert`/`update`, keeping ordered and unordered batch semantics, and as the
|
|
2138
1956
|
command error of `findAndModify` and `aggregate`. A refused statement aborts
|
|
2139
|
-
its transaction. `details` has MongoDB
|
|
1957
|
+
its transaction. `details` has a MongoDB-compatible shape for every `$jsonSchema` keyword
|
|
2140
1958
|
above and for `$and`, `$or`, `$nor`, `$not`, `$eq`, `$ne`, `$gt`, `$gte`,
|
|
2141
1959
|
`$lt`, `$lte`, `$exists`, `$type`, `$in`, `$nin`, `$regex`, `$size`, `$all`,
|
|
2142
1960
|
`$elemMatch`, `$mod` and `$expr`; other operators report only `operatorName`
|
|
@@ -2145,12 +1963,12 @@ and `specifiedAs`. `validationAction: 'warn'` writes the document and logs a
|
|
|
2145
1963
|
`bypassDocumentValidation` (a boolean or number) on `insert`, `update`,
|
|
2146
1964
|
`findAndModify` and writing `aggregate` skips validation; with authentication
|
|
2147
1965
|
it requires `dbAdmin` on the written database, as `collMod` does — `readWrite`
|
|
2148
|
-
alone is refused with `Unauthorized` (13),
|
|
1966
|
+
alone is refused with `Unauthorized` (13), implemented to be compatible with MongoDB's built-in roles.
|
|
2149
1967
|
|
|
2150
1968
|
`collMod` changes only the options it names; `validator: {}` removes the
|
|
2151
1969
|
validator; a `null` option is absent; `collMod` without options answers
|
|
2152
1970
|
`{ ok: 1 }`. A missing collection answers `NamespaceNotFound` (26), a view or
|
|
2153
|
-
`system.*` collection `InvalidOptions` (72),
|
|
1971
|
+
`system.*` collection `InvalidOptions` (72), the other MongoDB `collMod` options
|
|
2154
1972
|
(`index`, `viewOn`, `pipeline`, `expireAfterSeconds`, `timeseries`,
|
|
2155
1973
|
`changeStreamPreAndPostImages`, `dryRun`, `cappedSize`, `cappedMax`, …)
|
|
2156
1974
|
`InvalidOptions` (72) by name, and unknown fields `IDLUnknownField` (40415).
|
|
@@ -2163,13 +1981,13 @@ content digest includes them only then, so digests of collections without
|
|
|
2163
1981
|
options are unchanged. Engines up to 10.6.1 refuse a storage root or an export
|
|
2164
1982
|
holding collection options instead of silently not enforcing them.
|
|
2165
1983
|
|
|
2166
|
-
Differences from MongoDB
|
|
1984
|
+
Differences from MongoDB, as observed by runtime testing: `create` on an existing collection answers
|
|
2167
1985
|
`NamespaceExists` (48) even when the options are identical (MongoDB answers
|
|
2168
1986
|
`ok`); a validator together with `viewOn` is refused (MongoDB ignores it); an
|
|
2169
|
-
invalid `pattern` at the schema root is refused (MongoDB
|
|
1987
|
+
invalid `pattern` at the schema root is refused (MongoDB accepts it
|
|
2170
1988
|
there);
|
|
2171
|
-
command errors say `Document failed validation` without
|
|
2172
|
-
`Plan executor error during …` prefix; the `warn` log carries `errInfo`, not
|
|
1989
|
+
command errors say `Document failed validation` without the
|
|
1990
|
+
`Plan executor error during …` prefix MongoDB adds; the `warn` log carries `errInfo`, not
|
|
2173
1991
|
the whole document.
|
|
2174
1992
|
|
|
2175
1993
|
### Restricted Deny-All Views
|
|
@@ -2205,7 +2023,7 @@ replaced by a view keeps that original snapshot for `find`, `count`, and
|
|
|
2205
2023
|
with `CommandNotSupportedOnView` (code 166). `dropCollection` is the supported
|
|
2206
2024
|
DDL exception for removing a logical restricted view.
|
|
2207
2025
|
|
|
2208
|
-
Definitions are persisted in the physical `system.views` catalog.
|
|
2026
|
+
Definitions are persisted in the physical `system.views` catalog. To be MongoDB compatible,
|
|
2209
2027
|
`listCollections` lists `system.views` as a physical storage collection and the
|
|
2210
2028
|
logical namespace as type `view` with `{ viewOn, pipeline }`, read-only metadata,
|
|
2211
2029
|
and no `idIndex`. `system.views` is reserved from direct wire reads,
|
|
@@ -2242,9 +2060,6 @@ const estimated = await collection.estimatedDocumentCount();
|
|
|
2242
2060
|
const names = await collection.distinct('name');
|
|
2243
2061
|
```
|
|
2244
2062
|
|
|
2245
|
-
The `count` command answers `n` as a 32-bit integer when the count fits and as
|
|
2246
|
-
a 64-bit integer above that, as MongoDB does.
|
|
2247
|
-
|
|
2248
2063
|
---
|
|
2249
2064
|
|
|
2250
2065
|
## Wire Protocol Commands
|
|
@@ -2256,87 +2071,9 @@ a 64-bit integer above that, as MongoDB does.
|
|
|
2256
2071
|
| **Aggregation** | `aggregate`, `count`, `distinct` |
|
|
2257
2072
|
| **Indexes** | `createIndexes`, `dropIndexes`, `listIndexes` |
|
|
2258
2073
|
| **Sessions** | `startSession`, `endSessions`, `killSessions` |
|
|
2259
|
-
| **Transactions** | `startTransaction`, `commitTransaction`, `abortTransaction` through driver sessions
|
|
2074
|
+
| **Transactions** | `startTransaction`, `commitTransaction`, `abortTransaction` through driver sessions |
|
|
2260
2075
|
| **Admin** | `ping`, `buildInfo`/`buildinfo`, `listDatabases`, `listCollections`, `drop`, `dropDatabase`, `create`, `collMod` (validation options), `serverStatus`, `dbStats`, `collStats`, `connectionStatus`, `renameCollection` |
|
|
2261
2076
|
|
|
2262
|
-
Advertises MongoDB wire protocol versions 0–21 and reports MongoDB 7.0.0
|
|
2263
|
-
compatibility for its command surface. The documented command surface is tested
|
|
2264
|
-
with the official `mongodb` Node.js driver version 7.5.x. Wire limits and error
|
|
2265
|
-
replies follow MongoDB 8.0, whose document size tiers are larger than 7.0's;
|
|
2266
|
-
where NoSQLDB still differs from MongoDB 8.0, the list at the end of this
|
|
2267
|
-
section says so.
|
|
2268
|
-
|
|
2269
|
-
OP_MSG parsing rejects unknown required flags, invalid CRC-32C checksums,
|
|
2270
|
-
duplicate BSON keys, noncanonical array indexes, malformed section boundaries,
|
|
2271
|
-
multiple body sections, duplicate or colliding document-sequence identifiers,
|
|
2272
|
-
and missing or non-string `$db` fields. Early command-envelope validation then
|
|
2273
|
-
accepts document sequences only as `insert.documents`, `update.updates`, or
|
|
2274
|
-
`delete.deletes`, before authentication or command resources are acquired. A
|
|
2275
|
-
write command refuses any other sequence as an unknown field of its own,
|
|
2276
|
-
`IDLUnknownField` (40415) naming `insert.x`, or `insert` for an empty
|
|
2277
|
-
identifier, as MongoDB 8 does; any other command refuses document sequences with
|
|
2278
|
-
`InvalidOptions` (72). All three accepted forms are consumed as the
|
|
2279
|
-
corresponding bulk command argument. Unknown optional flags are ignored.
|
|
2280
|
-
|
|
2281
|
-
As in MongoDB 8, a complete frame that is malformed or over a document limit is
|
|
2282
|
-
answered with an error reply to its request id, and the connection stays usable
|
|
2283
|
-
for the requests behind it: for example `InvalidBSON` (22) for a bad document
|
|
2284
|
-
length or a body cut short, `Overflow` (15) for nesting beyond 100 levels, the
|
|
2285
|
-
`Location404xx` codes for malformed sections, `Location51252` for a checksum
|
|
2286
|
-
that leaves no room for a section, and `BSONObjectTooLarge` (10334) for an
|
|
2287
|
-
oversized document. The 10334 message shows the document's first element as
|
|
2288
|
-
MongoDB's does, cut after 160 bytes. In MongoDB 8's order, a document's
|
|
2289
|
-
structure (lengths, terminators, element types and booleans) and nesting are
|
|
2290
|
-
checked before its size, and a message's sections and BSON before its checksum.
|
|
2291
|
-
A defect in a legacy OP_QUERY document, its nesting included, is `InvalidBSON`
|
|
2292
|
-
(22), as MongoDB 8 answers it, and bytes after an OP_QUERY's field selector are
|
|
2293
|
-
ignored, as MongoDB 8 ignores them. A refused `moreToCome` request gets no
|
|
2294
|
-
reply. A frame that cannot be delimited or trusted closes the connection, again
|
|
2295
|
-
as in MongoDB: an invalid length prefix, an OP_MSG too short to hold its flags,
|
|
2296
|
-
a request whose `responseTo` is not 0, an unknown required flag, more than 128
|
|
2297
|
-
sections, or a checksum mismatch.
|
|
2298
|
-
|
|
2299
|
-
A message is limited to 48,000,000 bytes. A command body may be up to 16,809,984
|
|
2300
|
-
bytes and a document-sequence or legacy OP_QUERY document up to 16,793,600 bytes,
|
|
2301
|
-
MongoDB 8's 16 MiB plus 32 KiB and 16 KiB of command overhead; a stored document
|
|
2302
|
-
is limited to 16,777,216 bytes, and an insert row over it is that row's
|
|
2303
|
-
`BSONObjectTooLarge` write error. An `insert`, `update` or `delete` batch must
|
|
2304
|
-
hold 1 to 10,000 operations, the advertised `maxWriteBatchSize`; other batches
|
|
2305
|
-
are refused with `InvalidLength` (16), and drivers split larger input batches.
|
|
2306
|
-
|
|
2307
|
-
Before any of a request is decoded, its BSON is checked and the heap its decoded
|
|
2308
|
-
form takes is computed from the bytes alone, each allocation rounded up as the
|
|
2309
|
-
allocator rounds it. A request holds that charge against the server's
|
|
2310
|
-
decoded-memory budget, 1 GiB by default, until its command has finished with
|
|
2311
|
-
it. A request whose decoded form takes 16 KiB or less, as handshakes, pings
|
|
2312
|
-
and ordinary commands do, holds it against a separate 16 MiB share kept beside
|
|
2313
|
-
the budget instead, which holds one such request for each of the 1,024
|
|
2314
|
-
connections a server accepts, so it never waits behind larger requests. A
|
|
2315
|
-
request whose frame is small but whose decoded form is larger, such as a find
|
|
2316
|
-
with a long `$in` list, waits in the budget with the large ones. A request
|
|
2317
|
-
that needs more than the
|
|
2318
|
-
whole budget is refused whole with `BSONObjectTooLarge` (10334) and the message
|
|
2319
|
-
"send smaller batches", after its sections and BSON are checked and before its
|
|
2320
|
-
checksum. The number of elements, nested documents and arrays a request holds
|
|
2321
|
-
is not limited, so a full batch of 10,000 wide documents is accepted as in
|
|
2322
|
-
MongoDB. A request that does not fit what the budget has left waits for it
|
|
2323
|
-
behind every request of its share that arrived before it, for at most its
|
|
2324
|
-
`maxTimeMS` or five seconds from when it arrived. A request still waiting then
|
|
2325
|
-
is answered without being decoded or run: with `MaxTimeMSExpired` (50) when its
|
|
2326
|
-
`maxTimeMS` is five seconds or less, and otherwise with `LockTimeout` (24),
|
|
2327
|
-
which a transaction statement gets with the `TransientTransactionError` label.
|
|
2328
|
-
A statement of a running transaction answered so aborts its transaction; a
|
|
2329
|
-
`commitTransaction` or `abortTransaction` answered so, or a statement that would
|
|
2330
|
-
start its transaction, leaves the transaction as it was. A `moreToCome` request
|
|
2331
|
-
still waiting closes its connection. A command's `maxTimeMS` counts from when
|
|
2332
|
-
the server had the whole request, so the wait is part of it; a request sent
|
|
2333
|
-
while the connection's previous command ran counts from when that command
|
|
2334
|
-
finished, as MongoDB reads it only then. `maxTimeMS: 0` means no limit, as in
|
|
2335
|
-
MongoDB, so such a request waits the five seconds and gets `LockTimeout`. A
|
|
2336
|
-
legacy OP_QUERY command wrapped in `$query` is answered from an envelope that
|
|
2337
|
-
misses its `maxTimeMS` and `lsid`, so it waits five seconds and gets
|
|
2338
|
-
`LockTimeout` without a transaction label.
|
|
2339
|
-
|
|
2340
2077
|
The `moreToCome` request flag is accepted only for the exact `endSessions` or
|
|
2341
2078
|
`killSessions` cleanup envelope: an `admin` command body containing only the
|
|
2342
2079
|
session array, `writeConcern: { w: 0 }`, and `$db: "admin"`, with no document
|
|
@@ -2402,40 +2139,10 @@ The decoded budget also bounds the largest request the server accepts, so a
|
|
|
2402
2139
|
smaller one refuses smaller requests with `BSONObjectTooLarge`. Each server has
|
|
2403
2140
|
its own budgets, so servers in one process do not share them.
|
|
2404
2141
|
|
|
2405
|
-
NoSQLDB still differs from MongoDB 8.0 on the wire in these points:
|
|
2406
|
-
|
|
2407
|
-
- An opcode other than OP_MSG and OP_QUERY closes the connection. MongoDB 8
|
|
2408
|
-
ignores most unknown opcodes and answers the legacy `OP_GET_MORE` with error
|
|
2409
|
-
5739101.
|
|
2410
|
-
- A legacy OP_QUERY to a collection other than `$cmd` is routed as a `find`,
|
|
2411
|
-
where MongoDB 8 answers 5739101.
|
|
2412
|
-
- Nesting is limited to 100 levels; MongoDB 8 allows 200.
|
|
2413
|
-
- A write batch over 10,000 operations is refused with `InvalidLength` (16);
|
|
2414
|
-
MongoDB 8 accepts up to 100,000.
|
|
2415
|
-
- A request whose decoded form needs more than the decoded-memory budget, 1 GiB
|
|
2416
|
-
by default, is refused with `BSONObjectTooLarge` (10334); MongoDB 8 has no
|
|
2417
|
-
such limit.
|
|
2418
|
-
- A request that waits for decode memory longer than its `maxTimeMS` or five
|
|
2419
|
-
seconds is answered with `MaxTimeMSExpired` (50) or `LockTimeout` (24)
|
|
2420
|
-
without being run; MongoDB 8 has no decode budget.
|
|
2421
2142
|
- A frame over 8 KiB whose bytes average less than 1 MiB per second after a
|
|
2422
2143
|
10-second grace closes its connection while another connection waits for
|
|
2423
|
-
the encoded budget or more than seven eighths of it are charged; MongoDB
|
|
2144
|
+
the encoded budget or more than seven eighths of it are charged; MongoDB
|
|
2424
2145
|
has no such limit.
|
|
2425
|
-
- The identifier a `Location40431` or `Location40433` message names is cut
|
|
2426
|
-
after 256 bytes; MongoDB 8 names it whole.
|
|
2427
|
-
- A duplicate key anywhere in a request is refused with `InvalidBSON` (22);
|
|
2428
|
-
MongoDB 8 does not refuse duplicate keys on the wire.
|
|
2429
|
-
- A field name or string that is not valid UTF-8 is refused with `InvalidBSON`
|
|
2430
|
-
(22); MongoDB 8 does not check UTF-8 on the wire and stores such data.
|
|
2431
|
-
- Most `InvalidBSON` (22) messages describe the defect in NoSQLDB's own words.
|
|
2432
|
-
- A message with three document sequences gets an error reply, where MongoDB 8
|
|
2433
|
-
closes the connection.
|
|
2434
|
-
- A command other than `insert`, `update` and `delete` refuses document
|
|
2435
|
-
sequences with `InvalidOptions` (72); MongoDB 8 runs it.
|
|
2436
|
-
- `moreToCome` is accepted only for the session cleanup envelope above, where
|
|
2437
|
-
MongoDB 8 runs any command sent with it.
|
|
2438
|
-
- A request that carries a checksum is answered without one.
|
|
2439
2146
|
|
|
2440
2147
|
### Identifying the Engine
|
|
2441
2148
|
|
|
@@ -2458,9 +2165,9 @@ is. Every `hello`, `ismaster` and `isMaster` reply carries a self-contained
|
|
|
2458
2165
|
`version` is the released `@lossless.org/nosqldb` version the running binary was
|
|
2459
2166
|
built from: the Rust build compiles it in from the package manifest, so the
|
|
2460
2167
|
answer belongs to the shipped artifact and cannot drift from it.
|
|
2461
|
-
`mongodbCompatibility` is the
|
|
2462
|
-
|
|
2463
|
-
be
|
|
2168
|
+
`mongodbCompatibility` is the compatibility version NoSQLDB reports to drivers
|
|
2169
|
+
and tools for the command surface it implements to be MongoDB compatible — a
|
|
2170
|
+
compatibility value, never a claim to be a MongoDB build.
|
|
2464
2171
|
|
|
2465
2172
|
The handshake carries the identity unconditionally, before authentication, so a
|
|
2466
2173
|
client can derive its backend from the connection it already has. `buildInfo` is
|
|
@@ -2468,7 +2175,7 @@ available to **every authenticated connection**, including a `readWrite`-only
|
|
|
2468
2175
|
tenant user and an allocation read grant; it requires no `clusterMonitor` role
|
|
2469
2176
|
and creates no session state. `buildInfo` answers from build metadata alone and
|
|
2470
2177
|
therefore still answers on a fenced or closed runtime, while `hello` fails
|
|
2471
|
-
closed. It answers in MongoDB
|
|
2178
|
+
closed. It answers in a MongoDB-compatible shape so existing tooling keeps working:
|
|
2472
2179
|
|
|
2473
2180
|
```json
|
|
2474
2181
|
{
|
|
@@ -2494,16 +2201,65 @@ reported, including `version` and `process: "rustdb"`, and now reports the same
|
|
|
2494
2201
|
`nosqldb` identity beside them — but it remains a `clusterMonitor` command, so
|
|
2495
2202
|
least-privilege connections use `hello` or `buildInfo`.
|
|
2496
2203
|
|
|
2204
|
+
`serverStatus.engineFigures` reports the storage engine's own figures, each
|
|
2205
|
+
read from a counter or from the allocator's own statistics, so forming them
|
|
2206
|
+
takes no time that grows with the data. Every count is an int64 that is 0 when
|
|
2207
|
+
the server opens its storage and never decreases while it runs. `commitMode` is
|
|
2208
|
+
`buffered`: commits go through the write buffer, the only commit path.
|
|
2209
|
+
`bufferedBytes` is the bytes of committed writes that the buffer holds and no
|
|
2210
|
+
merge has yet written into the tree, the sum of `activeGenerationBytes` and
|
|
2211
|
+
`frozenGenerationBytes`; `commits` counts the publications made durable and
|
|
2212
|
+
visible, and `mergesStarted` and `mergesCompleted` the buffer's merges.
|
|
2213
|
+
`freezes` counts the buffer's generations frozen for a merge, by cause: `size`
|
|
2214
|
+
when the generation reached its size, `idle` when no commit arrived for the
|
|
2215
|
+
quiet period, `age` when its oldest commit reached the age limit, and `prune`
|
|
2216
|
+
when records that history pruning wrote waited for a merge. `backstopStall`
|
|
2217
|
+
counts the commits that waited at the buffer's backstop (`stalls`) and the
|
|
2218
|
+
nanoseconds they waited (`stalledNanoseconds`), and gives the commits waiting
|
|
2219
|
+
there now (`stalledWriters`). A commit waits there when it would take the
|
|
2220
|
+
active generation past its size while a generation is pending, from its
|
|
2221
|
+
freeze until the checkpoint after its merge is durable, or while commits that
|
|
2222
|
+
passed the backstop before it and are not linked yet fill the active
|
|
2223
|
+
generation; and a background commit waits there at its claim on the buffer
|
|
2224
|
+
while another background commit holds one. `refusals` counts the
|
|
2225
|
+
requests for space that the engine refused for lack of free
|
|
2226
|
+
space, a commit's or the engine's own work's (`space`), and the
|
|
2227
|
+
commits refused after waiting at the backstop as long as their limit
|
|
2228
|
+
allows (`backstop`). While a merge cannot get room,
|
|
2229
|
+
`serverStatus.engineFigures.mergeWedge` reports what it lacks (`bytesShort` or
|
|
2230
|
+
`segmentsShort`), and is null again once a later attempt of the merge is
|
|
2231
|
+
admitted; `mergeRefusals` counts the merge attempts refused since the root
|
|
2232
|
+
opened. `restarts` counts the write preparations started again by cause,
|
|
2233
|
+
`catalogChange`, `pruningWindow` and `merge`; one for a conflict on a document
|
|
2234
|
+
or unique key counts under none. `bytesWritten` counts the bytes handed to the
|
|
2235
|
+
file system for the storage files by duty: `redo`, `merge`, `cleaner`,
|
|
2236
|
+
`indexes`, `checkpoint`, `directory` and `fill`. `liveBytes` is the bytes the
|
|
2237
|
+
current state reaches and `fixedBytes` the bytes the engine's files hold apart
|
|
2238
|
+
from data: the anchor, segment headers and pre-sized log space.
|
|
2239
|
+
`directoryBytes` and `cacheResidentBytes` are the memory that the directory of
|
|
2240
|
+
stored frames and the node cache hold, and `cacheBudgetBytes` is the cache
|
|
2241
|
+
budget that `storageEngine.cacheBytes` reports.
|
|
2242
|
+
`serverStatus.engineFigures.pinnedBytes` is the bytes in the storage's data
|
|
2243
|
+
segments that the engine keeps, where it would otherwise free them, only
|
|
2244
|
+
because a live snapshot, such as an open cursor's, can still read superseded
|
|
2245
|
+
data in them: the value its latest maintenance round found, and 0 when no
|
|
2246
|
+
snapshot keeps any. `allocator` states whether a
|
|
2247
|
+
check at run time found the shipped allocator (`verified`), and only then its
|
|
2248
|
+
arena count, its decay times in milliseconds, whether it runs background
|
|
2249
|
+
threads, and the bytes allocated through it and resident; while `verified` is
|
|
2250
|
+
false, those fields are null.
|
|
2251
|
+
|
|
2497
2252
|
### Read and Write Concerns and Wire Deadlines
|
|
2498
2253
|
|
|
2499
2254
|
NoSQLDB serves every database from exactly one voting node and states its
|
|
2500
2255
|
concern profile in those terms instead of ignoring or blanket-rejecting the
|
|
2501
2256
|
fields:
|
|
2502
2257
|
|
|
2503
|
-
- **Write concern.** Every acknowledged write is
|
|
2504
|
-
|
|
2258
|
+
- **Write concern.** Every acknowledged write is in the write-ahead log before
|
|
2259
|
+
the response is sent: fsynced there with file storage, while memory storage
|
|
2260
|
+
keeps its log in the process's memory. So `w: 1`, `w: "majority"`
|
|
2505
2261
|
(the majority of one voter is that voter), and `j: true` are all satisfied by
|
|
2506
|
-
the same
|
|
2262
|
+
the same commit. `w: 0` is acknowledged the same way whenever a
|
|
2507
2263
|
response is expected; the exact `writeConcern: { w: 0 }` `endSessions` and
|
|
2508
2264
|
`killSessions` envelopes still run without a transport response. `wtimeout`
|
|
2509
2265
|
bounds a replication wait that does not exist on one node and has no
|
|
@@ -2523,65 +2279,6 @@ fields:
|
|
|
2523
2279
|
and `aggregate` only, and `afterClusterTime`, `atClusterTime`, and
|
|
2524
2280
|
`afterOpTime` are rejected with `InvalidOptions` because a single node issues
|
|
2525
2281
|
no cluster time.
|
|
2526
|
-
- **Deadlines.** `maxTimeMS` attaches a cooperative deadline to the command,
|
|
2527
|
-
counted from when the server had the whole request, or from when the
|
|
2528
|
-
connection's previous command finished for a request sent while it ran. It
|
|
2529
|
-
is checked while the request waits for decode memory, at admission, during
|
|
2530
|
-
namespace-lock and maintenance-gate waits, before `find`, `count`, and
|
|
2531
|
-
`distinct` materialize documents, between
|
|
2532
|
-
documents in `insert` and `delete` batches, and immediately before a
|
|
2533
|
-
transaction commit is handed to storage; expiry fails with `MaxTimeMSExpired`
|
|
2534
|
-
(code 50). A single storage step or an in-progress `update` batch runs to completion
|
|
2535
|
-
before the next check, so a write that expires after its first documents
|
|
2536
|
-
were published reports the expiry while those documents remain durable,
|
|
2537
|
-
exactly like an interrupted MongoDB batch. The official driver transmits a
|
|
2538
|
-
transaction's `maxCommitTimeMS` as `maxTimeMS` on `commitTransaction`; a
|
|
2539
|
-
commit that expires before its batch is handed to storage publishes nothing,
|
|
2540
|
-
records the transaction as aborted, and carries the
|
|
2541
|
-
`UnknownTransactionCommitResult` label; the driver's commit retry then sees
|
|
2542
|
-
`NoSuchTransaction` with `TransientTransactionError` and restarts the whole
|
|
2543
|
-
transaction. `getMore` rejects `maxTimeMS` with `BadValue` because NoSQLDB
|
|
2544
|
-
has no awaitData cursors, and a literal `maxCommitTimeMS` field is rejected
|
|
2545
|
-
with `InvalidOptions`.
|
|
2546
|
-
- **Database waits.** DDL that drops or reshapes collections (`drop`,
|
|
2547
|
-
`dropIndexes`, `collMod`, `dropDatabase`, `renameCollection`,
|
|
2548
|
-
`$out`/`$merge`) holds its database exclusively; other commands on that
|
|
2549
|
-
database wait for it, as they wait for a MongoDB database lock. Creating a
|
|
2550
|
-
collection or its indexes (`create`, `createIndexes`) holds only that
|
|
2551
|
-
collection exclusively, as MongoDB locks the collection: it waits for the
|
|
2552
|
-
open transactions whose statements targeted that collection (a collection
|
|
2553
|
-
a transaction reads only through `$lookup` or `$unionWith` is not held; the
|
|
2554
|
-
transaction reads its snapshot of it), transaction statements on that
|
|
2555
|
-
collection that arrive after it queue behind it, and everything else
|
|
2556
|
-
proceeds. Commands outside a transaction take no collection
|
|
2557
|
-
gate; writes still queue for the storage writer, which an index build holds
|
|
2558
|
-
until its index is ready. A command outside a
|
|
2559
|
-
transaction waits first in, first out for as long as it may run: until
|
|
2560
|
-
`maxTimeMS` (`MaxTimeMSExpired`, code 50), until its client disconnects or
|
|
2561
|
-
shuts down its sending side (the wait ends with `ClientDisconnect`, code
|
|
2562
|
-
279, which a client that still reads receives before the connection closes)
|
|
2563
|
-
or until the server shuts down (`InterruptedAtShutdown`, code 11600). A
|
|
2564
|
-
database DDL command likewise waits for the open transactions on its
|
|
2565
|
-
database to commit or abort, and every command on that database that
|
|
2566
|
-
arrives after it queues behind it, including reads of other collections. An
|
|
2567
|
-
open transaction therefore delays DDL and the commands behind it at most
|
|
2568
|
-
until the transaction lifetime limit (60 seconds, MongoDB's
|
|
2569
|
-
`transactionLifetimeLimitSeconds`) aborts it.
|
|
2570
|
-
A transaction statement that needs a further database or collection keeps a
|
|
2571
|
-
fixed five-second limit and then fails with `LockTimeout` (code 24, labelled
|
|
2572
|
-
`TransientTransactionError`), so a transaction never waits without bound while it
|
|
2573
|
-
holds its databases and collections; like any failed statement, it aborts
|
|
2574
|
-
the transaction and releases them. One transaction targets at most 4,096
|
|
2575
|
-
distinct collections across all its databases; statements that revisit a
|
|
2576
|
-
collection retain no extra gate. This matches the process-wide collection-lock
|
|
2577
|
-
registry's 4,096-live-identity bound; concurrent transactions and collection
|
|
2578
|
-
DDL share that registry. A command that finds the shared registry full fails
|
|
2579
|
-
with `LockTimeout` (code 24; labelled `TransientTransactionError` in a
|
|
2580
|
-
transaction) and can be retried once in-flight operations complete. It bounds process-local locks, not document staging,
|
|
2581
|
-
WAL record size or atomic publication. A statement targeting one more fails
|
|
2582
|
-
with code 16500 (`Location16500`, MongoDB 8's code name for it) and aborts it. The service operations (export,
|
|
2583
|
-
import, tenant and publication fencing) keep the five-second gate-wait limit and report
|
|
2584
|
-
`EFENCE_BUSY`.
|
|
2585
2282
|
|
|
2586
2283
|
Callers may therefore rely on a successful NoSQLDB response as a durable,
|
|
2587
2284
|
single-node acknowledgement. They must not read it as a replica-backed
|
|
@@ -2615,7 +2312,7 @@ The Rust engine is organized as a Cargo workspace with 12 focused crates:
|
|
|
2615
2312
|
| `rustdb-wire` | Wire protocol parser/encoder (OP_MSG, OP_QUERY, OP_REPLY) |
|
|
2616
2313
|
| `rustdb-query` | Query matcher, update engine, aggregation, sort, projection |
|
|
2617
2314
|
| `rustdb-state` | Internal deterministic storage commands, stable retry identities, and durable receipts |
|
|
2618
|
-
| `rustdb-kernel` | Native
|
|
2315
|
+
| `rustdb-kernel` | Native storage format 3: a log of commits, a write buffer merged into a tree, checkpoints and a cleaner, with stable timelines, contiguous LSNs and strict recovery; and the format-2 reader of 10.x roots |
|
|
2619
2316
|
| `rustdb-replication` | Internal non-serving OpenRaft 0.9.25 one-voter fresh-root foundation; not linked into the released `rustdb` server |
|
|
2620
2317
|
| `rustdb-storage` | Native catalogs, documents, exact counts, staged imports, reclamation and bounded OpLog |
|
|
2621
2318
|
| `rustdb-index` | Native index records, query plans, uniqueness, bounded builds and TTL |
|
|
@@ -2626,57 +2323,35 @@ The Rust engine is organized as a Cargo workspace with 12 focused crates:
|
|
|
2626
2323
|
|
|
2627
2324
|
The native MVCC engine serves both memory and file storage.
|
|
2628
2325
|
Its Rust APIs use `mvcc` modules and `Mvcc` type names independently of npm
|
|
2629
|
-
versions and persisted format versions. The
|
|
2630
|
-
|
|
2631
|
-
|
|
2326
|
+
versions and persisted format versions. The format-3 store writes each
|
|
2327
|
+
publication as one checksummed `COMMIT` frame of typed BSON records to its
|
|
2328
|
+
log, keeps recent writes in an in-memory buffer, merges them into a
|
|
2329
|
+
copy-on-write tree in data segments and writes checkpoints from which
|
|
2330
|
+
recovery replays the log. Snapshots pin a published view, and maintenance
|
|
2331
|
+
frees nothing a pinned snapshot can reach. Descriptor-relative
|
|
2632
2332
|
access and the enclosing native file-root lease protect the entire lifecycle.
|
|
2633
2333
|
Ambiguous publication fences the owner until recovery. Confirmed close drains
|
|
2634
2334
|
requests, sessions, cursors and retained workers before releasing ownership.
|
|
2635
2335
|
|
|
2636
|
-
The
|
|
2637
|
-
|
|
2638
|
-
|
|
2639
|
-
|
|
2640
|
-
|
|
2641
|
-
A
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
|
|
2648
|
-
|
|
2649
|
-
|
|
2650
|
-
|
|
2651
|
-
|
|
2652
|
-
|
|
2653
|
-
|
|
2654
|
-
|
|
2655
|
-
probes, and the reads of validation, stopped
|
|
2656
|
-
inspection, TTL and authentication passes, including their single-record
|
|
2657
|
-
probes. A user listing and a write's own probes of unique owners, index
|
|
2658
|
-
entries and natural-order records admit nothing either. A scan, maintenance
|
|
2659
|
-
pass or listing therefore cannot flush hot catalog records and documents.
|
|
2660
|
-
A commit forgets the cached value of every record it replaces or deletes, so
|
|
2661
|
-
a write, however many documents it changes, leaves behind at most what its
|
|
2662
|
-
query would have cached as a find. Frames are immutable and append-only, and a
|
|
2663
|
-
snapshot reaches only the frames its own tree references: an older snapshot
|
|
2664
|
-
that still needs a forgotten value reads it from storage again, and a new
|
|
2665
|
-
version is cached once a read looks it up. As with tree pages, a value is
|
|
2666
|
-
verified once when it is cached;
|
|
2667
|
-
storage corruption of a cached frame is detected after eviction or generation
|
|
2668
|
-
retirement.
|
|
2669
|
-
|
|
2670
|
-
Both caches evict by CLOCK. Each cached node and value carries a referenced
|
|
2671
|
-
bit. Every read sets it on the inner tree nodes it passes through, since each
|
|
2672
|
-
lookup beneath such a node passes through it again. On leaves and values, point
|
|
2673
|
-
lookups, the range reads of point queries and value admission set it; scans,
|
|
2674
|
-
maintenance passes, generation copies and a commit's own reads leave it as they
|
|
2675
|
-
find it, and the leaves and values they cache start without it. To make room, a
|
|
2676
|
-
hand sweeps the entries in turn, clears each set bit it passes and evicts the
|
|
2677
|
-
first entry whose bit is clear. A leaf or value that point reads use therefore
|
|
2678
|
-
outlasts the scan entries cached after it by one turn of the hand, and a cache
|
|
2679
|
-
hit costs no reordering.
|
|
2336
|
+
The store keeps verified tree nodes in one node cache, half of its memory
|
|
2337
|
+
budget, evicted by CLOCK. The budget is `storageBudgetBytes` where that option
|
|
2338
|
+
of `NoSqlDbServer` is set, from 4 MiB to 1 TiB, and otherwise a sixteenth of
|
|
2339
|
+
the memory the process may use, measured as for the inbound budgets above, from
|
|
2340
|
+
16 MiB to 4 GiB; `serverStatus.storageEngine.cacheBytes` reports the cache's
|
|
2341
|
+
half. A root the server serves takes it when it is created or opened and keeps it
|
|
2342
|
+
until it closes; the stopped operations' reads of a stopped root, a start's
|
|
2343
|
+
checks of a root before it serves it and a 10.x conversion open the store
|
|
2344
|
+
with 64 MiB. Each cached node carries a referenced bit:
|
|
2345
|
+
inner nodes enter with it set and leaves without it, and a point read sets it
|
|
2346
|
+
on the nodes it uses. To make room, a hand sweeps the entries in turn, clears
|
|
2347
|
+
each set bit it passes and evicts the first entry whose bit is clear. A read
|
|
2348
|
+
that is one row of a pass over many records, such as a scan, a merge or a page
|
|
2349
|
+
of the commit journal, clears no bit and is cached only in place of an
|
|
2350
|
+
unreferenced entry at the hand, so such reads cannot displace the nodes point
|
|
2351
|
+
reads keep. A publication removes the nodes it supersedes from the cache.
|
|
2352
|
+
There is no value cache: a value stored outside its leaf is read from storage
|
|
2353
|
+
each time. A collection scan reads each page of documents as one range read of
|
|
2354
|
+
their bodies in natural order, with no lookup per document.
|
|
2680
2355
|
|
|
2681
2356
|
Database and collection lookups, absent names included, are remembered for the
|
|
2682
2357
|
newest committed root a read has used, up to 256 names and 1 MiB, so the
|
|
@@ -2690,7 +2365,7 @@ one root list its indexes once. The first listing on a root reads the catalog
|
|
|
2690
2365
|
whole; a catalog of more than 64 indexes, aliases included, and a listing that
|
|
2691
2366
|
fails are not kept.
|
|
2692
2367
|
|
|
2693
|
-
Document, catalog,
|
|
2368
|
+
Document, catalog, statistics, natural-order, index-entry, unique-owner,
|
|
2694
2369
|
authentication, fencing and retry-receipt records share atomic publications.
|
|
2695
2370
|
Transactions retain changed documents and immutable read views with byte and
|
|
2696
2371
|
concurrency admission; they do not copy a complete database snapshot or impose a
|
|
@@ -2701,15 +2376,25 @@ generated IDs and zero-match outcomes, across restart. Failed statements do not
|
|
|
2701
2376
|
receive success receipts. Session termination drains active work and retires its
|
|
2702
2377
|
cursors.
|
|
2703
2378
|
|
|
2704
|
-
Custom indexes build in
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
its
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2379
|
+
Custom indexes build in the background: `createIndexes` publishes them as
|
|
2380
|
+
building in one short commit and answers once they are ready, while other writes
|
|
2381
|
+
go on. A build sorts its entries off the writer, after reserving the space its
|
|
2382
|
+
sort runs and its output need, with its runs in a private scratch directory
|
|
2383
|
+
inside a file root and in memory on a memory root, and attaches the entries in
|
|
2384
|
+
one commit; it then applies the writes made meanwhile until the writers keep
|
|
2385
|
+
its indexes up to date in their own commits, and makes its indexes ready
|
|
2386
|
+
together in a commit that carries no entry. No read uses its indexes
|
|
2387
|
+
before then. A request for the same indexes joins a running build, and
|
|
2388
|
+
`serverStatus` reports builds under `indexBuilds`. A build that its collection's
|
|
2389
|
+
writes outpace, or that meets a resource limit, fails with an explicit refusal
|
|
2390
|
+
and leaves no index; a duplicate key fails a unique build with `DuplicateKey`
|
|
2391
|
+
(11000). Interrupted builds restart from the beginning under native ownership
|
|
2392
|
+
before serving, and run in the background, so serving does not wait for them.
|
|
2393
|
+
A restarted build that fails removes its indexes, restores a quarantined index
|
|
2394
|
+
it was rebuilding, logs a warning that names neither the index nor a key, and
|
|
2395
|
+
shows as failed in `indexBuilds` until the next build of that index; it is not
|
|
2396
|
+
retried by itself. Held databases remain closed and are skipped during
|
|
2397
|
+
recovery; releasing the hold restarts their interrupted builds.
|
|
2713
2398
|
|
|
2714
2399
|
Index names and aliases, sparse and unique constraints, empty collections,
|
|
2715
2400
|
restricted-view catalogs and exact BSON survive supported database export/import.
|
|
@@ -2728,7 +2413,7 @@ The serving profile is:
|
|
|
2728
2413
|
| Auth/sessions | SCRAM-SHA-256, logical sessions, snapshot transactions, commit/abort and durable retryable writes |
|
|
2729
2414
|
| Management | Health, tenants, grants, attestation, fencing, publication holds, export/import and content digest |
|
|
2730
2415
|
| Debug | Bounded current-session OpLog, atomic data revert, document/collection pages and dashboard |
|
|
2731
|
-
| Maintenance | Bounded TTL expiry, retired record reclamation, orphan import retirement and
|
|
2416
|
+
| Maintenance | Bounded TTL expiry, retired record reclamation, orphan import retirement, and the store's merges, checkpoints, tombstone pruning and cleaning |
|
|
2732
2417
|
| Stopped storage | Native inspection and relocation; isolated authenticated rehearsal on its qualified platform profile |
|
|
2733
2418
|
| Import/control admission | 100 MiB encoded import; bounded private staging and a small final catalog/receipt commit. Other control requests: 1 MiB |
|
|
2734
2419
|
| External cluster | Replication and HA remain unsupported; the one-voter OpenRaft foundation is not linked into serving |
|
|
@@ -2737,16 +2422,20 @@ These are operation and resource budgets, not total database/document limits.
|
|
|
2737
2422
|
Unsupported options and commands reject explicitly. Imports that cannot fit one
|
|
2738
2423
|
derived document or index entry within the native commit budget reject before
|
|
2739
2424
|
publishing the replacement. Reclamation preserves pinned roots and retires parent
|
|
2740
|
-
identities only after their children have been handled.
|
|
2741
|
-
|
|
2425
|
+
identities only after their children have been handled. The store's cleaner
|
|
2426
|
+
moves the live data out of a data segment of which at least a third of the
|
|
2427
|
+
written bytes is garbage, and a segment is removed once no checkpoint or
|
|
2428
|
+
pinned snapshot needs it.
|
|
2742
2429
|
|
|
2743
|
-
There is one serving engine and no Bitcask fallback.
|
|
2744
|
-
|
|
2745
|
-
|
|
2430
|
+
There is one serving engine and no Bitcask fallback. A start converts a root that
|
|
2431
|
+
a 10.x release wrote into format 3 (see **Storage format migration** under File
|
|
2432
|
+
startup admission); no other storage conversion and no authentication conversion
|
|
2433
|
+
runs during startup or rehearsal. Any other conversion code, when explicitly
|
|
2434
|
+
commissioned, belongs exclusively in top-level `ts_migration/`.
|
|
2746
2435
|
|
|
2747
2436
|
NoSQLDB makes no general MongoDB performance or feature-parity claim.
|
|
2748
2437
|
Versioned packages contain Linux amd64/arm64 and macOS amd64/arm64 native
|
|
2749
|
-
executables through [@git.zone/
|
|
2438
|
+
executables through [@git.zone/rsrepo](https://www.npmjs.com/package/@git.zone/rsrepo).
|
|
2750
2439
|
|
|
2751
2440
|
### Multi-host release artifacts
|
|
2752
2441
|
|
|
@@ -2768,7 +2457,7 @@ The native distribution terms and qualified library requirements are recorded in
|
|
|
2768
2457
|
extract and install these files independently of its compiled JavaScript bundle;
|
|
2769
2458
|
NoSQLDB does not download engines at startup.
|
|
2770
2459
|
|
|
2771
|
-
A plain `
|
|
2460
|
+
A plain `rsrepo` build, as run by `pnpm build` and before `pnpm test`, compiles only the configured targets of the host's own architecture (its GNU and musl engines on Linux); `rsrepo --configured-targets` builds the host's complete partition from `targetsByHost`. The development suite checks the packaged engines of the host architecture. Releases use `rsrepo matrix` to coordinate both partitions after the version commit exists, then publish only after native verification passes and every exact-commit binary has executed and passed strict provenance assembly.
|
|
2772
2461
|
|
|
2773
2462
|
The release coordinator needs Git, Node.js, pnpm and SSH access to the configured macOS builder. The local Linux amd64 builder needs `aarch64-linux-gnu-gcc`, `x86_64-linux-musl-gcc` and `aarch64-linux-musl-gcc` and a registered Linux arm64 `binfmt` interpreter. Linux file storage requires kernel 5.6 or newer with `openat2` enabled. Cross-architecture file qualification requires a full-system Linux guest or a native host. A linux-user executable probe does not qualify secure filesystem syscalls such as `openat2`. The built-package acceptance script `test/verify-alpine.mjs` exercises authenticated nested document persistence and restart through Node and Deno on Alpine; run it on both Linux architectures before publication. The Apple Silicon builder needs Rosetta. The matrix qualifies macOS first and then Linux; both partitions must pass before artifact assembly. Set these environment variables to authorized builders and their dedicated temporary roots:
|
|
2774
2463
|
|
|
@@ -2777,11 +2466,11 @@ export NOSQLDB_RELEASE_MACOS_SSH="release-user@mac-builder.example"
|
|
|
2777
2466
|
export NOSQLDB_RELEASE_MACOS_TEMP_ROOT="/absolute/dedicated/nosqldb-release"
|
|
2778
2467
|
```
|
|
2779
2468
|
|
|
2780
|
-
`gitzone release` first runs `pnpm run release:check-builders`, before creating a version commit or tag. It verifies the project-local
|
|
2469
|
+
`gitzone release` first runs `pnpm run release:check-builders`, before creating a version commit or tag. It verifies the project-local rsrepo version and rejects missing cross-toolchains or emulation, an unsuitable macOS host, missing tools, or an unsafe remote temporary root. The release build then:
|
|
2781
2470
|
|
|
2782
2471
|
1. Builds the TypeScript package and debug UI bundle from the version commit.
|
|
2783
2472
|
2. Transfers an exact Git bundle into isolated local Linux and SSH macOS worker workspaces and installs each frozen lockfile.
|
|
2784
|
-
3. Runs the configured `release:verify-native` command on each worker: the full Rust workspace, TypeScript test checks, a `
|
|
2473
|
+
3. Runs the configured `release:verify-native` command on each worker: the full Rust workspace, TypeScript test checks, a `rsrepo --configured-targets` build of the worker's partition, the public API suite with the packaged-engine checks for both architectures, and explicit-engine authentication/ownership/rehearsal tests through the worker's other packaged architecture. Tests use short, private disposable filesystem roots under `/var/tmp` on Linux and canonical `/tmp` on macOS. Linux builders must provide `/var/tmp` on a native-supported durable filesystem; tmpfs is rejected. Failures retain diagnostics and prevent assembly.
|
|
2785
2474
|
4. Builds and executes GNU and musl Linux amd64/arm64 plus macOS arm64/amd64 binaries on their assigned workers.
|
|
2786
2475
|
5. Retrieves bounded artifact sets and transactionally publishes the complete provenance-checked matrix into `dist_rust/`.
|
|
2787
2476
|
|
|
@@ -2793,13 +2482,38 @@ No host address or path is stored in the repository. A failed run retains its un
|
|
|
2793
2482
|
|
|
2794
2483
|
- Atomic native publications include data, indexes, authentication, fences and
|
|
2795
2484
|
durable outcomes. File writes are acknowledged after durable commit.
|
|
2796
|
-
-
|
|
2485
|
+
- Snapshots keep reads stable through concurrent writes and maintenance:
|
|
2486
|
+
merges, checkpoints and the cleaner free nothing a snapshot can reach.
|
|
2797
2487
|
- Checksums, frame identities and contiguous positions validate committed
|
|
2798
2488
|
history. Only a valid incomplete final append is repairable during owned
|
|
2799
2489
|
startup; complete or interior corruption is rejected.
|
|
2490
|
+
- A device that loses acknowledged bytes at the end of the log and reads
|
|
2491
|
+
them back as zeros cannot be detected: recovery takes the zeros for the end
|
|
2492
|
+
of the log. The loss reaches back at most to the newest durable
|
|
2493
|
+
checkpoint's redo start. The commits it can lose hold at most half the
|
|
2494
|
+
buffer budget plus two holds, counted as their keys and values plus 64
|
|
2495
|
+
bytes per record: one hold of commits, which is one commit or one group
|
|
2496
|
+
or batch of commits, and one background commit's claim on the buffer.
|
|
2497
|
+
The server sets the budget when it opens a root, to
|
|
2498
|
+
`storageBudgetBytes` where that option is set (4 MiB to 1 TiB), and
|
|
2499
|
+
otherwise to a sixteenth of the memory the process may use, from 16 MiB to
|
|
2500
|
+
4 GiB: 64 MiB plus the two holds can be lost in a 2 GiB container, and at
|
|
2501
|
+
most 2 GiB plus the two holds unless the option sets a larger budget.
|
|
2502
|
+
Each hold is at most the batch limit, 64 MiB by default.
|
|
2800
2503
|
- Uncertain publication fences the engine until confirmed recovery.
|
|
2801
|
-
- Bounded maintenance reclaims unreachable records and
|
|
2802
|
-
after their readers release them.
|
|
2504
|
+
- Bounded maintenance reclaims unreachable records, and segments that hold
|
|
2505
|
+
only superseded data, after their readers release them.
|
|
2506
|
+
- Disk use under live snapshots is unbounded: the store does not limit how
|
|
2507
|
+
long a snapshot stays open. A segment whose data has all been superseded
|
|
2508
|
+
stays on disk while a snapshot that can still read it is open, and
|
|
2509
|
+
`serverStatus` reports those bytes at
|
|
2510
|
+
`serverStatus.engineFigures.pinnedBytes`.
|
|
2511
|
+
- Writes are admitted at full speed until the free space falls to the
|
|
2512
|
+
store's floor: 64 MiB or two segments, whichever is more, beyond what the
|
|
2513
|
+
write buffer holds. There, every write but a delete or a drop is refused
|
|
2514
|
+
with the capacity code 16500 and writes nothing. Deletes and drops are
|
|
2515
|
+
still admitted, down to two segments, while no snapshot older than the
|
|
2516
|
+
newest write is open.
|
|
2803
2517
|
- A durable session whose records this version cannot read does not stop
|
|
2804
2518
|
maintenance. The obsolete-outcome sweep passes over it and leaves its records
|
|
2805
2519
|
in place. It logs an error at most once a minute and counts the session in
|
|
@@ -2823,22 +2537,48 @@ deadline, 100,000 filesystem-entry and 1 TiB byte limits, and bounded native
|
|
|
2823
2537
|
record/result admission. This is storage integrity evidence; it does not replace
|
|
2824
2538
|
the full authenticated rehearsal or production ownership protocol.
|
|
2825
2539
|
|
|
2540
|
+
A failure names a stable code (`NoSQLDB stopped inspection failed with code …`).
|
|
2541
|
+
Two codes describe states the tool deliberately does not validate:
|
|
2542
|
+
|
|
2543
|
+
- `RECOVERY_REQUIRED` while a stopped relocation or alignment has left its
|
|
2544
|
+
workspace in the root: finish that relocation or alignment, or abort the
|
|
2545
|
+
alignment, with its exact request, then validate again. A root of this
|
|
2546
|
+
release that a crash left needs no start first: the tool reads it as a start
|
|
2547
|
+
would recover it, writing nothing. A root that a 10.x process left without a
|
|
2548
|
+
clean stop also reports `RECOVERY_REQUIRED`; a start of this release converts
|
|
2549
|
+
it as of its last completed commit.
|
|
2550
|
+
- `UNSUPPORTED` while an index build is unfinished: an index that is building,
|
|
2551
|
+
quarantined, or importing into a collection that is not dropped. A start
|
|
2552
|
+
restarts interrupted builds from the beginning in the background, without
|
|
2553
|
+
waiting for them before it serves, so a root stopped before they publish
|
|
2554
|
+
still reports `UNSUPPORTED`, as does one whose database's publication hold
|
|
2555
|
+
keeps them back. A restarted build that fails, for example on duplicate
|
|
2556
|
+
keys, removes its indexes or restores the quarantined index it was
|
|
2557
|
+
rebuilding, which validation reports until that index is dropped or rebuilt.
|
|
2558
|
+
|
|
2826
2559
|
---
|
|
2827
2560
|
|
|
2828
2561
|
## Testing Example
|
|
2829
2562
|
|
|
2830
|
-
The offline regression `test/test.commit-journal-compatibility.node.ts`
|
|
2831
|
-
|
|
2832
|
-
|
|
2833
|
-
|
|
2834
|
-
|
|
2835
|
-
|
|
2836
|
-
|
|
2837
|
-
|
|
2838
|
-
|
|
2839
|
-
|
|
2840
|
-
|
|
2841
|
-
|
|
2563
|
+
The offline regression `test/test.commit-journal-compatibility.node.ts` reads
|
|
2564
|
+
`test/format-2/compatibility.tar`, a root that the published 10.8.1 release
|
|
2565
|
+
wrote once from the definition in `test/format-2/roots.ts`
|
|
2566
|
+
(`test/format-2/archives.md` records how). It checks every archive in
|
|
2567
|
+
`test/format-2/` against `SHA256SUMS`, and each of its tests that reads the root extracts that
|
|
2568
|
+
archive into a new directory that only its owner can access, so the root it
|
|
2569
|
+
reads is a copy. It qualifies that
|
|
2570
|
+
the stopped audit and the namespace and string inspections read the 10.x copy
|
|
2571
|
+
without changing a byte: the audit finds it foreign and ineligible, with
|
|
2572
|
+
`CONVERSION_PENDING`, `AUTH_USERS_PRESENT` and the definition's two users, and
|
|
2573
|
+
the inspections give the definition's databases, collections and values. An
|
|
2574
|
+
`existing-only` start converts the copy, leaving only this release's names, and
|
|
2575
|
+
is refused because a copied root is served only after alignment; a second start
|
|
2576
|
+
is refused the same way and changes nothing. The converted root keeps the 10.x
|
|
2577
|
+
root's provider, group, timeline and LSN, its two users and its databases,
|
|
2578
|
+
collections and values, and alignment refuses it with `UNSUPPORTED`, as a dry
|
|
2579
|
+
run and for real, because it holds auth users; it writes no receipt and
|
|
2580
|
+
changes nothing. The test starts only this release's engine; build it normally
|
|
2581
|
+
before running the test.
|
|
2842
2582
|
|
|
2843
2583
|
The commit-journal lifetime regression uses the existing test-support executable
|
|
2844
2584
|
and its three-second `NOSQLDB_TEST_TRANSACTION_LIFETIME_MS` setting. It compares
|
|
@@ -2848,7 +2588,7 @@ qualification executable through `test:before`; production keeps its unchanged
|
|
|
2848
2588
|
60-second transaction lifetime and the suite keeps its 60-second file timeout.
|
|
2849
2589
|
|
|
2850
2590
|
```typescript
|
|
2851
|
-
import { expect, tap } from '@git.zone/
|
|
2591
|
+
import { expect, tap } from '@git.zone/tsrepo/tapbundle';
|
|
2852
2592
|
import { NoSqlDbServer } from '@lossless.org/nosqldb';
|
|
2853
2593
|
import { MongoClient } from 'mongodb';
|
|
2854
2594
|
|