nedb-engine 2.0.27__tar.gz → 2.0.28__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/PKG-INFO +51 -9
  2. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/README.md +50 -8
  3. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/pyproject.toml +1 -1
  4. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/__init__.py +1 -1
  5. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/.gitignore +0 -0
  6. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/LICENSE +0 -0
  7. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/client/node/README.md +0 -0
  8. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/client/python/README.md +0 -0
  9. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/autoindex.py +0 -0
  10. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/backends/__init__.py +0 -0
  11. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/backends/redis_backend.py +0 -0
  12. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/cascade.py +0 -0
  13. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/concurrent.py +0 -0
  14. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/crypto.py +0 -0
  15. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/engine.py +0 -0
  16. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/index.py +0 -0
  17. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/log.py +0 -0
  18. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/merkle.py +0 -0
  19. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/mongo.py +0 -0
  20. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/query.py +0 -0
  21. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/redis_compat.py +0 -0
  22. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/relations.py +0 -0
  23. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/resp2.py +0 -0
  24. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/server.py +0 -0
  25. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/snapshot.py +0 -0
  26. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/sql.py +0 -0
  27. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/store.py +0 -0
  28. {nedb_engine-2.0.27 → nedb_engine-2.0.28}/python/nedb/wrap_redis.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nedb-engine
3
- Version: 2.0.27
3
+ Version: 2.0.28
4
4
  Summary: NEDB — a versioned, self-compressing, time-traveling embedded database (replay-protected, idempotent, relational, searchable) with durable AOF persistence and a server daemon (nedbd).
5
5
  Project-URL: Homepage, https://github.com/Eth-Interchained/nedb
6
6
  Project-URL: Repository, https://github.com/Eth-Interchained/nedb
@@ -38,9 +38,9 @@ One Rust core → ships to **PyPI** and **npm** from a single source.
38
38
 
39
39
  ---
40
40
 
41
- ## v2 — The DAG Engine (new in 2.0.5)
41
+ ## v2 — The DAG Engine (current stable: 2.0.27)
42
42
 
43
- NEDB v2 replaces the append-only log (AOF) with a **content-addressed Merkle DAG**. Every document version is an immutable, BLAKE2b-verified object. Nothing is ever overwritten.
43
+ NEDB v2 replaces the append-only log (AOF) with a **content-addressed Merkle DAG**. Every document version is an immutable, BLAKE2b-verified object. Nothing is ever overwritten. As of **v2.0.27**, restarts after the first open are **O(1) warm starts** (driven by a `MANIFEST` of `seq` + Merkle head), the **cold scan is deferred** so the daemon accepts connections immediately, and a new **`GET /events` SSE endpoint** streams scan progress + per-write events live.
44
44
 
45
45
  ```bash
46
46
  # Run the v2 DAG engine — ships inside pip install nedb-engine
@@ -49,21 +49,34 @@ nedbd --dag --data ./data
49
49
  NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data ./data
50
50
 
51
51
  curl http://127.0.0.1:7070/health
52
- # {"ok":true,"version":"2.0.5","service":"nedbd","encrypted":true}
52
+ # {"ok":true,"version":"2.0.27","service":"nedbd","engine":"dag","startup_ready":true,"encrypted":true}
53
+
54
+ # Tail the live event stream (new in v2.0.27)
55
+ curl http://127.0.0.1:7070/events
56
+ # event: scan data: {"objects":730000,"of":1310703,"rate":21043,"eta_s":28}
57
+ # event: ready data: {"seq":1310703,"head":"b2:9c14e07a…"}
58
+ # event: write data: {"seq":1310704,"coll":"beliefs","head":"b2:7af3c11e…"}
53
59
  ```
54
60
 
55
61
  | Property | v2 DAG | v1 AOF |
56
62
  |---|:---:|:---:|
57
63
  | Uncorruptable (atomic writes, hash-verified reads) | ✅ | ⚠️ |
58
- | Instant cold start (no AOF replay) | ✅ | ❌ |
64
+ | O(1) warm start via MANIFEST (no scan, no replay) | ✅ | ❌ |
65
+ | Deferred cold scan (socket open immediately) | ✅ | ❌ |
66
+ | O(1) incremental Merkle head (never recomputed) | ✅ | ❌ |
59
67
  | Parallel writes (no global lock) | ✅ | ❌ |
60
68
  | BLAKE2b Merkle head on every response | ✅ | ❌ |
69
+ | IdIndex sharded across 256 subdirectories | ✅ | ❌ |
70
+ | TCP_NODELAY (no 40–200 ms loopback Nagle delay) | ✅ | ❌ |
71
+ | `GET /events` SSE log stream | ✅ | ❌ |
61
72
  | Tombstone deletes (history preserved) | ✅ | ✅ |
62
73
  | Auto-migrates v1 AOF → v2 DAG on startup | ✅ | — |
63
74
  | Same HTTP API — Vision, Studio, all clients unchanged | ✅ | ✅ |
64
75
 
65
76
  **v1 AOF engine is still shipped and unchanged** — `nedbd` (no flag) runs v1.
66
77
 
78
+ **Production status:** [vision.interchained.org](https://vision.interchained.org) is live on v2.0.27 — **1,310,703 sequences** indexed in the Vision database, AES-256-GCM encrypted at rest, at block height **620,989**.
79
+
67
80
  ---
68
81
 
69
82
  ## What makes NEDB different
@@ -222,11 +235,31 @@ db.seq(); // → BigInt
222
235
  nedbd runs NEDB as a long-lived process with an HTTP/JSON API and an optional RESP2 wire protocol. Built on a **single-writer group-commit sequencer** — parallel reads, batched durable writes, one hash-chain per database, zero write-write races.
223
236
 
224
237
  ```bash
225
- nedbd # :7070, data ./nedb-data
238
+ nedbd # :7070, data ./nedb-data (v1 AOF engine)
239
+ nedbd --dag --data ./data # v2 DAG engine (or NEDBD_DAG=1)
226
240
  NEDBD_RESP2_PORT=6380 nedbd # also speak RESP2 (redis-cli compatible)
227
241
  nedbd --log-level 2 # 0=errors 1=requests 2=deploy 3=verbose
242
+
243
+ # Live event stream (new in v2.0.27) — SSE: scan progress, ready, per-write head
244
+ curl http://127.0.0.1:7070/events
228
245
  ```
229
246
 
247
+ ### Startup modes (v2.0.27)
248
+
249
+ - **Warm start** — every restart after the first open reads the `MANIFEST` file and restores `seq` + Merkle `head` in **O(1)**. No scan, no replay, independent of dataset size. Boots in milliseconds.
250
+ - **Cold start** — first open of an existing dataset spawns the integrity scan in a background thread *and accepts connections immediately*. Reads serve instantly from the content-addressed DAG; writes return `HTTP 503 startup in progress` until the `startup_ready` gate flips. Progress (objects, rate, ETA) streams over `GET /events`.
251
+
252
+ ### Environment variables
253
+
254
+ | Variable | Default | Description |
255
+ |---|---|---|
256
+ | `NEDBD_DAG` | `0` | Set `1` to launch the v2 DAG engine (`nedbd-v2`). Same as `--dag`. |
257
+ | `NEDBD_HOST` | `127.0.0.1` | Bind address. **v2.0.27** defaults to loopback (was `0.0.0.0`) — security hardening fix. Set explicitly to `0.0.0.0` to expose. |
258
+ | `NEDBD_PORT` | `7070` | HTTP bind port. |
259
+ | `NEDBD_TOKEN` | unset | Optional bearer token; required on every `/v1/*` request when set. |
260
+ | `NEDB_TMK` | unset | 32-byte hex AES-256-GCM at-rest encryption key. |
261
+ | `NEDBD_DATA` | `./nedb-data` | Root directory. v2 creates `dag/`, IdIndex sharded across **256 subdirectories**, and a small `MANIFEST` file. |
262
+
230
263
  ```bash
231
264
  # Create a database with seed data and relations
232
265
  curl -X POST :7070/v1/databases -d '{
@@ -284,6 +317,18 @@ db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')
284
317
 
285
318
  ## Performance
286
319
 
320
+ **v2 DAG Rust server (v2.0.27, Intel iMac — 10k writes / 100k reads / 30k objects, AES-256-GCM on):**
321
+
322
+ | Operation | Throughput | p50 | p99 |
323
+ |---|---|---|---|
324
+ | Sequential writes | **418 ops/s** | 2.3 ms | 3.3 ms |
325
+ | Point-lookup reads | **478 ops/s** | 2.0 ms | 3.0 ms |
326
+ | ORDER BY queries | **489 ops/s** | 1.8 ms | 4.3 ms |
327
+ | Batch writes (500 ops/req) | **1,104 ops/s** | 0.9 ms | 1.2 ms |
328
+ | Tamper-verify (30k objects) | ~21,000 BLAKE2b/sec | — | 1.38 s total |
329
+
330
+ p99 latencies hold because of `TCP_NODELAY` on the axum listener — without it macOS loopback adds the Nagle algorithm's 40–200 ms delay on small writes.
331
+
287
332
  **v1 Python server (baseline — single-threaded AOF):**
288
333
 
289
334
  | Operation | Throughput | p99 latency |
@@ -295,9 +340,6 @@ db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')
295
340
  | Rust napi PUT (FFI) | ~70K/s | — |
296
341
  | Rust napi GET (FFI) | ~330K/s | — |
297
342
 
298
- **v2 DAG Rust server — tokio/axum, no GIL, lock-free per-doc writes:**
299
- > Benchmarks in progress — target 5,000–50,000 ops/s. Run `python3 tests/test_dag_perf.py` against your own instance.
300
-
301
343
  Reproduce with the included benchmark:
302
344
 
303
345
  ```bash
@@ -19,9 +19,9 @@ One Rust core → ships to **PyPI** and **npm** from a single source.
19
19
 
20
20
  ---
21
21
 
22
- ## v2 — The DAG Engine (new in 2.0.5)
22
+ ## v2 — The DAG Engine (current stable: 2.0.27)
23
23
 
24
- NEDB v2 replaces the append-only log (AOF) with a **content-addressed Merkle DAG**. Every document version is an immutable, BLAKE2b-verified object. Nothing is ever overwritten.
24
+ NEDB v2 replaces the append-only log (AOF) with a **content-addressed Merkle DAG**. Every document version is an immutable, BLAKE2b-verified object. Nothing is ever overwritten. As of **v2.0.27**, restarts after the first open are **O(1) warm starts** (driven by a `MANIFEST` of `seq` + Merkle head), the **cold scan is deferred** so the daemon accepts connections immediately, and a new **`GET /events` SSE endpoint** streams scan progress + per-write events live.
25
25
 
26
26
  ```bash
27
27
  # Run the v2 DAG engine — ships inside pip install nedb-engine
@@ -30,21 +30,34 @@ nedbd --dag --data ./data
30
30
  NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data ./data
31
31
 
32
32
  curl http://127.0.0.1:7070/health
33
- # {"ok":true,"version":"2.0.5","service":"nedbd","encrypted":true}
33
+ # {"ok":true,"version":"2.0.27","service":"nedbd","engine":"dag","startup_ready":true,"encrypted":true}
34
+
35
+ # Tail the live event stream (new in v2.0.27)
36
+ curl http://127.0.0.1:7070/events
37
+ # event: scan data: {"objects":730000,"of":1310703,"rate":21043,"eta_s":28}
38
+ # event: ready data: {"seq":1310703,"head":"b2:9c14e07a…"}
39
+ # event: write data: {"seq":1310704,"coll":"beliefs","head":"b2:7af3c11e…"}
34
40
  ```
35
41
 
36
42
  | Property | v2 DAG | v1 AOF |
37
43
  |---|:---:|:---:|
38
44
  | Uncorruptable (atomic writes, hash-verified reads) | ✅ | ⚠️ |
39
- | Instant cold start (no AOF replay) | ✅ | ❌ |
45
+ | O(1) warm start via MANIFEST (no scan, no replay) | ✅ | ❌ |
46
+ | Deferred cold scan (socket open immediately) | ✅ | ❌ |
47
+ | O(1) incremental Merkle head (never recomputed) | ✅ | ❌ |
40
48
  | Parallel writes (no global lock) | ✅ | ❌ |
41
49
  | BLAKE2b Merkle head on every response | ✅ | ❌ |
50
+ | IdIndex sharded across 256 subdirectories | ✅ | ❌ |
51
+ | TCP_NODELAY (no 40–200 ms loopback Nagle delay) | ✅ | ❌ |
52
+ | `GET /events` SSE log stream | ✅ | ❌ |
42
53
  | Tombstone deletes (history preserved) | ✅ | ✅ |
43
54
  | Auto-migrates v1 AOF → v2 DAG on startup | ✅ | — |
44
55
  | Same HTTP API — Vision, Studio, all clients unchanged | ✅ | ✅ |
45
56
 
46
57
  **v1 AOF engine is still shipped and unchanged** — `nedbd` (no flag) runs v1.
47
58
 
59
+ **Production status:** [vision.interchained.org](https://vision.interchained.org) is live on v2.0.27 — **1,310,703 sequences** indexed in the Vision database, AES-256-GCM encrypted at rest, at block height **620,989**.
60
+
48
61
  ---
49
62
 
50
63
  ## What makes NEDB different
@@ -203,11 +216,31 @@ db.seq(); // → BigInt
203
216
  nedbd runs NEDB as a long-lived process with an HTTP/JSON API and an optional RESP2 wire protocol. Built on a **single-writer group-commit sequencer** — parallel reads, batched durable writes, one hash-chain per database, zero write-write races.
204
217
 
205
218
  ```bash
206
- nedbd # :7070, data ./nedb-data
219
+ nedbd # :7070, data ./nedb-data (v1 AOF engine)
220
+ nedbd --dag --data ./data # v2 DAG engine (or NEDBD_DAG=1)
207
221
  NEDBD_RESP2_PORT=6380 nedbd # also speak RESP2 (redis-cli compatible)
208
222
  nedbd --log-level 2 # 0=errors 1=requests 2=deploy 3=verbose
223
+
224
+ # Live event stream (new in v2.0.27) — SSE: scan progress, ready, per-write head
225
+ curl http://127.0.0.1:7070/events
209
226
  ```
210
227
 
228
+ ### Startup modes (v2.0.27)
229
+
230
+ - **Warm start** — every restart after the first open reads the `MANIFEST` file and restores `seq` + Merkle `head` in **O(1)**. No scan, no replay, independent of dataset size. Boots in milliseconds.
231
+ - **Cold start** — first open of an existing dataset spawns the integrity scan in a background thread *and accepts connections immediately*. Reads serve instantly from the content-addressed DAG; writes return `HTTP 503 startup in progress` until the `startup_ready` gate flips. Progress (objects, rate, ETA) streams over `GET /events`.
232
+
233
+ ### Environment variables
234
+
235
+ | Variable | Default | Description |
236
+ |---|---|---|
237
+ | `NEDBD_DAG` | `0` | Set `1` to launch the v2 DAG engine (`nedbd-v2`). Same as `--dag`. |
238
+ | `NEDBD_HOST` | `127.0.0.1` | Bind address. **v2.0.27** defaults to loopback (was `0.0.0.0`) — security hardening fix. Set explicitly to `0.0.0.0` to expose. |
239
+ | `NEDBD_PORT` | `7070` | HTTP bind port. |
240
+ | `NEDBD_TOKEN` | unset | Optional bearer token; required on every `/v1/*` request when set. |
241
+ | `NEDB_TMK` | unset | 32-byte hex AES-256-GCM at-rest encryption key. |
242
+ | `NEDBD_DATA` | `./nedb-data` | Root directory. v2 creates `dag/`, IdIndex sharded across **256 subdirectories**, and a small `MANIFEST` file. |
243
+
211
244
  ```bash
212
245
  # Create a database with seed data and relations
213
246
  curl -X POST :7070/v1/databases -d '{
@@ -265,6 +298,18 @@ db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')
265
298
 
266
299
  ## Performance
267
300
 
301
+ **v2 DAG Rust server (v2.0.27, Intel iMac — 10k writes / 100k reads / 30k objects, AES-256-GCM on):**
302
+
303
+ | Operation | Throughput | p50 | p99 |
304
+ |---|---|---|---|
305
+ | Sequential writes | **418 ops/s** | 2.3 ms | 3.3 ms |
306
+ | Point-lookup reads | **478 ops/s** | 2.0 ms | 3.0 ms |
307
+ | ORDER BY queries | **489 ops/s** | 1.8 ms | 4.3 ms |
308
+ | Batch writes (500 ops/req) | **1,104 ops/s** | 0.9 ms | 1.2 ms |
309
+ | Tamper-verify (30k objects) | ~21,000 BLAKE2b/sec | — | 1.38 s total |
310
+
311
+ p99 latencies hold because of `TCP_NODELAY` on the axum listener — without it macOS loopback adds the Nagle algorithm's 40–200 ms delay on small writes.
312
+
268
313
  **v1 Python server (baseline — single-threaded AOF):**
269
314
 
270
315
  | Operation | Throughput | p99 latency |
@@ -276,9 +321,6 @@ db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')
276
321
  | Rust napi PUT (FFI) | ~70K/s | — |
277
322
  | Rust napi GET (FFI) | ~330K/s | — |
278
323
 
279
- **v2 DAG Rust server — tokio/axum, no GIL, lock-free per-doc writes:**
280
- > Benchmarks in progress — target 5,000–50,000 ops/s. Run `python3 tests/test_dag_perf.py` against your own instance.
281
-
282
324
  Reproduce with the included benchmark:
283
325
 
284
326
  ```bash
@@ -10,7 +10,7 @@ build-backend = "hatchling.build"
10
10
 
11
11
  [project]
12
12
  name = "nedb-engine"
13
- version = "2.0.27"
13
+ version = "2.0.28"
14
14
  description = "NEDB — a versioned, self-compressing, time-traveling embedded database (replay-protected, idempotent, relational, searchable) with durable AOF persistence and a server daemon (nedbd)."
15
15
  readme = "README.md"
16
16
  requires-python = ">=3.8"
@@ -47,4 +47,4 @@ __all__ = [
47
47
  "wrap_redis", "WrappedRedis",
48
48
  "_native", "__has_native__",
49
49
  ]
50
- __version__ = "2.0.27"
50
+ __version__ = "2.0.28"
File without changes
File without changes