nedb-engine 2.0.26__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.
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/PKG-INFO +51 -9
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/README.md +50 -8
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/pyproject.toml +1 -1
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/__init__.py +1 -1
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/.gitignore +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/LICENSE +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/client/node/README.md +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/client/python/README.md +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/autoindex.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/backends/__init__.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/backends/redis_backend.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/cascade.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/concurrent.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/crypto.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/engine.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/index.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/log.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/merkle.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/mongo.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/query.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/redis_compat.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/relations.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/resp2.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/server.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/snapshot.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/sql.py +0 -0
- {nedb_engine-2.0.26 → nedb_engine-2.0.28}/python/nedb/store.py +0 -0
- {nedb_engine-2.0.26 → 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.
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
|
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 (
|
|
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.
|
|
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
|
-
|
|
|
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.
|
|
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"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|