Skip to content

NEDB v2.1.0 — Sprint Release

Choose a tag to compare

@ethgr0wth ethgr0wth released this 17 Jun 19:43
69ae9a1

"Batch writes tripled. TRACE and AS OF fixed. Live query subscriptions. 28/28 tests green."


Install

pip install --upgrade nedb-engine        # 2.1.0
npm install nedb-engine                   # 2.1.0
pip install --upgrade nedb-engine-client  # 1.1.0
npm install nedb-engine-client            # 1.1.0

Start the v2 DAG engine:

NEDBD_DAG=1 NEDB_TMK=<32-byte-hex> nedbd --data ./data
curl http://127.0.0.1:7070/health
# {"ok":true,"version":"2.1.0","engine":"dag","service":"nedbd","encrypted":true}

What's new

🔥 Batch writes +185% — 1,106/s → 3,153/s

POST /v1/databases/:name/batch now processes all put ops in parallel via Rayon. Object writes and id-index updates run concurrently across all cores. Sequential ordering preserved via pre-allocated seq numbers.

Batch writes (500/req):  1,106/s @ 1.25ms  →  3,153/s @ 0.42ms

📈 Point reads +33% — 392/s → 523/s

Fixed a silent bug: node.hash was serialized as empty (skipped by skip_serializing_if) so every read-back returned a node with hash = "". This broke TRACE, AS OF, and caused unnecessary read failures. Now restored from the lookup path on every objects.read().

🔧 TRACE and AS OF were broken — now fixed

TRACE caused_by and AS OF seq both rely on following hash chains through the graph and object store. With node.hash = "" they silently returned empty results. Both are now fully functional:

# Causal trace — follow the chain backward
curl -X POST .../query -d '{"nql":"FROM ops WHERE _id = \"c\" TRACE caused_by LIMIT 10"}'

# Time-travel — state at a specific sequence number
curl -X POST .../query -d '{"nql":"FROM docs AS OF 42"}'

📡 Live query subscriptions — POST /subscribe

Subscribe to any NQL query and receive real-time diffs as SSE:

curl -N -X POST http://127.0.0.1:7070/v1/databases/vision/subscribe \
  -H 'content-type: application/json' \
  -d '{"nql":"FROM blocks ORDER BY height DESC LIMIT 5"}'

# Initial result sent immediately
# data: {"sub_id":1,"rows":[...],"event":"initial"}

# After every PUT/batch that changes the result:
# data: {"sub_id":1,"rows":[...],"count":5}

Unsubscribe: DELETE /v1/databases/:name/subscribe/:sub_id

⏰ Hourly checkpoint + graceful shutdown

  • Hourly: MANIFEST flushed at top of every clock hour (system-clock aligned)
  • SIGTERM (systemctl stop, Docker): caught, flushes all MANIFESTs, clean exit
  • SIGINT (Ctrl+C): same
  • Background ticker: MANIFEST dirty-flag flushed every 1s — removed per-write I/O from the hot path

🔬 DAG test suite — 28/28

python3 tests/test_dag.py — auto-spawns nedbd if nothing on port 7070, cleans up after. Covers:

  • CRUD, tombstone deletes, query (NQL WHERE/ORDER BY/GROUP BY/SEARCH)
  • Batch (parallel puts, mixed put/del, monotonic seq)
  • Integrity (BLAKE2b head, verify, tamper evidence)
  • Timeline (VALID AS OF, AS OF seq time-travel)
  • Causal (TRACE caused_by)
  • SSE (/events endpoint)

🖥️ Studio DAG engine badge

NEDB Studio now shows a DAG ◆ badge (green) in the nav bar when connected to the v2 Rust engine, AOF (amber) when on v1. Read-only — Studio observes the engine, never controls it. Tooltip: "start nedbd with --dag to use v2 DAG engine".

📊 engine: "dag" in /health

The health endpoint now reports the engine type:

{
  "ok": true,
  "service": "nedbd",
  "version": "2.1.0",
  "engine": "dag",
  "databases": ["vision"],
  "encrypted": true
}

Benchmark (Intel iMac, 10k writes / 100k reads)

Operation v2.0.36 v2.1.0 Change
Sequential writes 352/s @ 6.4ms 399/s @ 5.6ms +13%
Batch writes (500/req) 1,106/s @ 1.25ms 3,153/s @ 0.42ms +185% 🔥
Point-lookup reads 392/s @ 5.9ms 523/s @ 4.7ms +33%
ORDER BY queries 457/s @ 3.8ms 572/s @ 2.4ms +25%
Verify 30k objects 1.15s (~26k/s)
Concurrent writes (16) 248/s @ 230ms 245/s @ 215ms ≈ flat

Concurrent single-PUT bottleneck remains — targeted in v2.2.


Full changelog

  • perf(db): parallel batch writes via put_batch() — Rayon object + index writes
  • perf(db): decouple flush_manifest from write path — background ticker every 1s
  • feat(server): hourly clock-aligned checkpoint + SIGTERM/SIGINT graceful shutdown
  • feat(server): live query subscriptions — POST /v1/databases/:name/subscribe
  • feat(server): engine: "dag" field in /health response
  • fix(store): restore node.hash after deserialization — fixes TRACE and AS OF
  • fix(store): guard empty hash in ObjectStore::read() — no more panic on empty string
  • test: DAG correctness suite — 28 tests, auto-spawn, tests/test_dag.py
  • feat(studio): engine status badge (DAG ◆ / AOF) in Studio Nav bar

Links


© INTERCHAINED, LLC × Vex (Claude Sonnet 4.6)