nedb-engine 2.0.36__tar.gz → 2.2.0__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.36 → nedb_engine-2.2.0}/PKG-INFO +14 -12
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/README.md +11 -9
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/pyproject.toml +3 -3
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/__init__.py +1 -1
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/wrap_redis.py +64 -17
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/.gitignore +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/LICENSE +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/client/node/README.md +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/client/python/README.md +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/autoindex.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/backends/__init__.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/backends/redis_backend.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/cascade.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/concurrent.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/crypto.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/engine.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/index.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/log.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/merkle.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/mongo.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/query.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/redis_compat.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/relations.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/resp2.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/server.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/snapshot.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/sql.py +0 -0
- {nedb_engine-2.0.36 → nedb_engine-2.2.0}/python/nedb/store.py +0 -0
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: nedb-engine
|
|
3
|
-
Version: 2.0
|
|
3
|
+
Version: 2.2.0
|
|
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
|
-
Project-URL: Homepage, https://github.com/
|
|
6
|
-
Project-URL: Repository, https://github.com/
|
|
5
|
+
Project-URL: Homepage, https://github.com/aiassistsecure/nedb
|
|
6
|
+
Project-URL: Repository, https://github.com/aiassistsecure/nedb
|
|
7
7
|
Author: Eth-Interchained
|
|
8
8
|
License: GPL-3.0-or-later
|
|
9
9
|
License-File: LICENSE
|
|
@@ -38,9 +38,11 @@ One Rust core → ships to **PyPI** and **npm** from a single source.
|
|
|
38
38
|
|
|
39
39
|
---
|
|
40
40
|
|
|
41
|
-
## v2 —
|
|
41
|
+
## NEDB v2.0.36 — Production Stable
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
**Current stable: 2.0.36** — Cross-platform native wheels shipping `nedbd-v2` binary inside `pip install nedb-engine`. Linux + Windows wheels built on GitHub Actions; macOS arm64 + x86_64 wheels built on Codemagic (M2 Mac Minis). All four platforms publish from one `v*` tag.
|
|
44
|
+
|
|
45
|
+
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.36**, 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
46
|
|
|
45
47
|
```bash
|
|
46
48
|
# Run the v2 DAG engine — ships inside pip install nedb-engine
|
|
@@ -49,9 +51,9 @@ nedbd --dag --data ./data
|
|
|
49
51
|
NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data ./data
|
|
50
52
|
|
|
51
53
|
curl http://127.0.0.1:7070/health
|
|
52
|
-
# {"ok":true,"version":"2.0.
|
|
54
|
+
# {"ok":true,"version":"2.0.36","service":"nedbd","engine":"dag","startup_ready":true,"encrypted":true}
|
|
53
55
|
|
|
54
|
-
# Tail the live event stream (new in v2.0.
|
|
56
|
+
# Tail the live event stream (new in v2.0.36)
|
|
55
57
|
curl http://127.0.0.1:7070/events
|
|
56
58
|
# event: scan data: {"objects":730000,"of":1310703,"rate":21043,"eta_s":28}
|
|
57
59
|
# event: ready data: {"seq":1310703,"head":"b2:9c14e07a…"}
|
|
@@ -75,7 +77,7 @@ curl http://127.0.0.1:7070/events
|
|
|
75
77
|
|
|
76
78
|
**v1 AOF engine is still shipped and unchanged** — `nedbd` (no flag) runs v1.
|
|
77
79
|
|
|
78
|
-
**Production status:** [vision.interchained.org](https://vision.interchained.org) is live on v2.0.
|
|
80
|
+
**Production status:** [vision.interchained.org](https://vision.interchained.org) is live on v2.0.36 — **1,310,703 sequences** indexed in the Vision database, AES-256-GCM encrypted at rest, at block height **620,989**.
|
|
79
81
|
|
|
80
82
|
---
|
|
81
83
|
|
|
@@ -240,11 +242,11 @@ nedbd --dag --data ./data # v2 DAG engine (or NEDBD_DAG=1)
|
|
|
240
242
|
NEDBD_RESP2_PORT=6380 nedbd # also speak RESP2 (redis-cli compatible)
|
|
241
243
|
nedbd --log-level 2 # 0=errors 1=requests 2=deploy 3=verbose
|
|
242
244
|
|
|
243
|
-
# Live event stream (new in v2.0.
|
|
245
|
+
# Live event stream (new in v2.0.36) — SSE: scan progress, ready, per-write head
|
|
244
246
|
curl http://127.0.0.1:7070/events
|
|
245
247
|
```
|
|
246
248
|
|
|
247
|
-
### Startup modes (v2.0.
|
|
249
|
+
### Startup modes (v2.0.36)
|
|
248
250
|
|
|
249
251
|
- **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
252
|
- **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`.
|
|
@@ -254,7 +256,7 @@ curl http://127.0.0.1:7070/events
|
|
|
254
256
|
| Variable | Default | Description |
|
|
255
257
|
|---|---|---|
|
|
256
258
|
| `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.
|
|
259
|
+
| `NEDBD_HOST` | `127.0.0.1` | Bind address. **v2.0.36** defaults to loopback (was `0.0.0.0`) — security hardening fix. Set explicitly to `0.0.0.0` to expose. |
|
|
258
260
|
| `NEDBD_PORT` | `7070` | HTTP bind port. |
|
|
259
261
|
| `NEDBD_TOKEN` | unset | Optional bearer token; required on every `/v1/*` request when set. |
|
|
260
262
|
| `NEDB_TMK` | unset | 32-byte hex AES-256-GCM at-rest encryption key. |
|
|
@@ -317,7 +319,7 @@ db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')
|
|
|
317
319
|
|
|
318
320
|
## Performance
|
|
319
321
|
|
|
320
|
-
**v2 DAG Rust server (v2.0.
|
|
322
|
+
**v2 DAG Rust server (v2.0.36, Intel iMac — 10k writes / 100k reads / 30k objects, AES-256-GCM on):**
|
|
321
323
|
|
|
322
324
|
| Operation | Throughput | p50 | p99 |
|
|
323
325
|
|---|---|---|---|
|
|
@@ -19,9 +19,11 @@ One Rust core → ships to **PyPI** and **npm** from a single source.
|
|
|
19
19
|
|
|
20
20
|
---
|
|
21
21
|
|
|
22
|
-
## v2 —
|
|
22
|
+
## NEDB v2.0.36 — Production Stable
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
**Current stable: 2.0.36** — Cross-platform native wheels shipping `nedbd-v2` binary inside `pip install nedb-engine`. Linux + Windows wheels built on GitHub Actions; macOS arm64 + x86_64 wheels built on Codemagic (M2 Mac Minis). All four platforms publish from one `v*` tag.
|
|
25
|
+
|
|
26
|
+
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.36**, 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
27
|
|
|
26
28
|
```bash
|
|
27
29
|
# Run the v2 DAG engine — ships inside pip install nedb-engine
|
|
@@ -30,9 +32,9 @@ nedbd --dag --data ./data
|
|
|
30
32
|
NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data ./data
|
|
31
33
|
|
|
32
34
|
curl http://127.0.0.1:7070/health
|
|
33
|
-
# {"ok":true,"version":"2.0.
|
|
35
|
+
# {"ok":true,"version":"2.0.36","service":"nedbd","engine":"dag","startup_ready":true,"encrypted":true}
|
|
34
36
|
|
|
35
|
-
# Tail the live event stream (new in v2.0.
|
|
37
|
+
# Tail the live event stream (new in v2.0.36)
|
|
36
38
|
curl http://127.0.0.1:7070/events
|
|
37
39
|
# event: scan data: {"objects":730000,"of":1310703,"rate":21043,"eta_s":28}
|
|
38
40
|
# event: ready data: {"seq":1310703,"head":"b2:9c14e07a…"}
|
|
@@ -56,7 +58,7 @@ curl http://127.0.0.1:7070/events
|
|
|
56
58
|
|
|
57
59
|
**v1 AOF engine is still shipped and unchanged** — `nedbd` (no flag) runs v1.
|
|
58
60
|
|
|
59
|
-
**Production status:** [vision.interchained.org](https://vision.interchained.org) is live on v2.0.
|
|
61
|
+
**Production status:** [vision.interchained.org](https://vision.interchained.org) is live on v2.0.36 — **1,310,703 sequences** indexed in the Vision database, AES-256-GCM encrypted at rest, at block height **620,989**.
|
|
60
62
|
|
|
61
63
|
---
|
|
62
64
|
|
|
@@ -221,11 +223,11 @@ nedbd --dag --data ./data # v2 DAG engine (or NEDBD_DAG=1)
|
|
|
221
223
|
NEDBD_RESP2_PORT=6380 nedbd # also speak RESP2 (redis-cli compatible)
|
|
222
224
|
nedbd --log-level 2 # 0=errors 1=requests 2=deploy 3=verbose
|
|
223
225
|
|
|
224
|
-
# Live event stream (new in v2.0.
|
|
226
|
+
# Live event stream (new in v2.0.36) — SSE: scan progress, ready, per-write head
|
|
225
227
|
curl http://127.0.0.1:7070/events
|
|
226
228
|
```
|
|
227
229
|
|
|
228
|
-
### Startup modes (v2.0.
|
|
230
|
+
### Startup modes (v2.0.36)
|
|
229
231
|
|
|
230
232
|
- **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
233
|
- **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`.
|
|
@@ -235,7 +237,7 @@ curl http://127.0.0.1:7070/events
|
|
|
235
237
|
| Variable | Default | Description |
|
|
236
238
|
|---|---|---|
|
|
237
239
|
| `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.
|
|
240
|
+
| `NEDBD_HOST` | `127.0.0.1` | Bind address. **v2.0.36** defaults to loopback (was `0.0.0.0`) — security hardening fix. Set explicitly to `0.0.0.0` to expose. |
|
|
239
241
|
| `NEDBD_PORT` | `7070` | HTTP bind port. |
|
|
240
242
|
| `NEDBD_TOKEN` | unset | Optional bearer token; required on every `/v1/*` request when set. |
|
|
241
243
|
| `NEDB_TMK` | unset | 32-byte hex AES-256-GCM at-rest encryption key. |
|
|
@@ -298,7 +300,7 @@ db.query('FROM policy AS OF 200 VALID AS OF "2024-02-15"')
|
|
|
298
300
|
|
|
299
301
|
## Performance
|
|
300
302
|
|
|
301
|
-
**v2 DAG Rust server (v2.0.
|
|
303
|
+
**v2 DAG Rust server (v2.0.36, Intel iMac — 10k writes / 100k reads / 30k objects, AES-256-GCM on):**
|
|
302
304
|
|
|
303
305
|
| Operation | Throughput | p50 | p99 |
|
|
304
306
|
|---|---|---|---|
|
|
@@ -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.2.0"
|
|
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"
|
|
@@ -34,8 +34,8 @@ dependencies = [
|
|
|
34
34
|
nedbd = "nedb.server:main"
|
|
35
35
|
|
|
36
36
|
[project.urls]
|
|
37
|
-
Homepage = "https://github.com/
|
|
38
|
-
Repository = "https://github.com/
|
|
37
|
+
Homepage = "https://github.com/aiassistsecure/nedb"
|
|
38
|
+
Repository = "https://github.com/aiassistsecure/nedb"
|
|
39
39
|
|
|
40
40
|
[tool.hatch.build.targets.wheel]
|
|
41
41
|
packages = ["python/nedb"]
|
|
@@ -151,12 +151,31 @@ class NedBdProxy:
|
|
|
151
151
|
self._req("DELETE", f"/v1/databases/{self._name}/rows/{coll}/{id}")
|
|
152
152
|
|
|
153
153
|
def link(self, frm: str, rel: str, to: str, **_kw) -> None:
|
|
154
|
-
|
|
154
|
+
# v1 nedbd has POST /link; v2 DAG stores relations as NQL-queryable docs
|
|
155
|
+
# in a __links__ collection — compatible with TRAVERSE queries.
|
|
156
|
+
try:
|
|
157
|
+
self._req("POST", self._db("/link"), {"frm": frm, "rel": rel, "to": to})
|
|
158
|
+
except RuntimeError as e:
|
|
159
|
+
if "404" in str(e) or "not found" in str(e).lower():
|
|
160
|
+
# v2 DAG: store as a document so NQL can traverse it
|
|
161
|
+
self._req("POST", self._db("/put"), {
|
|
162
|
+
"coll": "__links__",
|
|
163
|
+
"id": f"{frm}|{rel}|{to}",
|
|
164
|
+
"doc": {"_from": frm, "_rel": rel, "_to": to},
|
|
165
|
+
})
|
|
166
|
+
else:
|
|
167
|
+
raise
|
|
155
168
|
|
|
156
169
|
def unlink(self, frm: str, rel: str, to: str, **_kw) -> None:
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
170
|
+
try:
|
|
171
|
+
self._req("DELETE", f"/v1/databases/{self._name}/links/{frm}/{rel}/{to}")
|
|
172
|
+
except RuntimeError:
|
|
173
|
+
# v2 fallback: tombstone the __links__ doc
|
|
174
|
+
try:
|
|
175
|
+
self._req("DELETE",
|
|
176
|
+
f"/v1/databases/{self._name}/rows/__links__/{frm}|{rel}|{to}")
|
|
177
|
+
except RuntimeError:
|
|
178
|
+
pass
|
|
160
179
|
|
|
161
180
|
def neighbors(self, frm: str, rel: str, as_of: Optional[int] = None) -> List[str]:
|
|
162
181
|
frm_coll, frm_id = (frm.split(":", 1) + [""])[:2]
|
|
@@ -423,9 +442,28 @@ class NEDBSurface:
|
|
|
423
442
|
return total
|
|
424
443
|
|
|
425
444
|
def _backfill_one(self, mapping: CollectionMapping, batch_size: int) -> int:
|
|
426
|
-
"""Import all keys matching one mapping from Redis into NEDB.
|
|
427
|
-
|
|
428
|
-
|
|
445
|
+
"""Import all keys matching one mapping from Redis into NEDB.
|
|
446
|
+
|
|
447
|
+
In nedbd mode uses /batch for high throughput (v2 DAG: ~4000 ops/s).
|
|
448
|
+
In-process mode falls back to individual puts.
|
|
449
|
+
"""
|
|
450
|
+
count = 0
|
|
451
|
+
cursor = 0
|
|
452
|
+
pending: List[dict] = []
|
|
453
|
+
|
|
454
|
+
def _flush() -> int:
|
|
455
|
+
if not pending or not self._nedbd_mode:
|
|
456
|
+
return 0
|
|
457
|
+
proxy: NedBdProxy = self._db # type: ignore[assignment]
|
|
458
|
+
try:
|
|
459
|
+
r = proxy._req("POST", proxy._db("/batch"), {"ops": list(pending)})
|
|
460
|
+
n = r.get("count", len(pending))
|
|
461
|
+
pending.clear()
|
|
462
|
+
return n
|
|
463
|
+
except Exception:
|
|
464
|
+
pending.clear()
|
|
465
|
+
return 0
|
|
466
|
+
|
|
429
467
|
while True:
|
|
430
468
|
cursor, keys = self._r.scan(cursor, match=mapping.pattern,
|
|
431
469
|
count=batch_size)
|
|
@@ -441,16 +479,24 @@ class NEDBSurface:
|
|
|
441
479
|
continue
|
|
442
480
|
doc = mapping.parse_value(raw_val)
|
|
443
481
|
doc.setdefault("_source", "backfill")
|
|
444
|
-
self.
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
482
|
+
if self._nedbd_mode:
|
|
483
|
+
pending.append({"op": "put", "coll": mapping.collection,
|
|
484
|
+
"id": doc_id, "doc": doc})
|
|
485
|
+
if len(pending) >= 500:
|
|
486
|
+
count += _flush()
|
|
487
|
+
else:
|
|
488
|
+
self._db.put(mapping.collection, doc_id, doc,
|
|
489
|
+
client="__backfill__",
|
|
490
|
+
evidence="backfill",
|
|
491
|
+
confidence=1.0)
|
|
492
|
+
self._persist_last_op()
|
|
493
|
+
count += 1
|
|
450
494
|
except Exception:
|
|
451
495
|
pass # skip unreadable keys
|
|
452
496
|
if cursor == 0:
|
|
453
497
|
break
|
|
498
|
+
|
|
499
|
+
count += _flush() # flush remainder
|
|
454
500
|
return count
|
|
455
501
|
|
|
456
502
|
# ── Write shadowing ───────────────────────────────────────────────────────
|
|
@@ -641,10 +687,11 @@ def wrap_redis(r: Any, db_name: str = "default",
|
|
|
641
687
|
Args:
|
|
642
688
|
r: An existing ``redis.Redis`` (or compatible) connection.
|
|
643
689
|
db_name: Logical database name. NEDB uses ``nedb:{db_name}:*``.
|
|
644
|
-
nedbd_url: Optional URL of a running nedbd server
|
|
645
|
-
``"http://localhost:
|
|
646
|
-
|
|
647
|
-
|
|
690
|
+
nedbd_url: Optional URL of a running nedbd server.
|
|
691
|
+
v1 AOF: ``"http://localhost:7070"`` (nedbd, no flag)
|
|
692
|
+
v2 DAG: ``"http://localhost:7070"`` (nedbd --dag)
|
|
693
|
+
When set, all r.nedb.* calls go to nedbd over HTTP.
|
|
694
|
+
v2 DAG backfill uses /batch for ~4000 ops/s throughput.
|
|
648
695
|
nedbd_token: Optional bearer token for nedbd authentication
|
|
649
696
|
(set via ``NEDBD_TOKEN`` env on the server).
|
|
650
697
|
|
|
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
|