diff --git a/CHANGELOG.md b/CHANGELOG.md index 852b993..a601d75 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,31 @@ All notable changes to BaraDB are documented in this file. +## [1.3.0] — 2026-07-30 + +### Raft cluster — Supported (single `default` DB scope) + +Raft 3-node moves from **Experimental** to **Supported** for the covered scope; single-node remains Production GA. Spec/plan: `docs/superpowers/specs/2026-07-30-raft-supported-design.md`, `docs/superpowers/plans/2026-07-30-v1.3.0-raft-supported.md`. + +- **Failover under load proven** — `tests/raft_failover_load_e2e_test.nim`: sustained INSERT load, leader killed at ≥ 50 acked writes; every **acknowledged** write survives on both survivors. Client contract: in-flight writes during failover fail fast with an error — clients must retry ([distributed.md](docs/en/distributed.md)) +- **Mandatory CI gate** — dedicated `raft-e2e` GitHub Actions job runs all five raft e2e suites; a missing server binary is a hard FAIL under CI (no silent skip) +- **Raft-port TLS** — `BARADB_RAFT_TLS_ENABLED` + `BARADB_RAFT_TLS_CERT_FILE` / `BARADB_RAFT_TLS_KEY_FILE` / `BARADB_RAFT_TLS_CA_FILE` / `BARADB_RAFT_TLS_VERIFY_PEER`; fail-closed startup when cert/key is missing; optional mutual auth; follower→leader SQL forwarding is TLS-wrapped when the client port is. E2E `tests/raft_tls_e2e_test.nim` (full-TLS cluster works; plaintext node excluded) +- **InstallSnapshot cold-node recovery** — backward-compatible wire protocol (`RaftProtoVersion` stays 1); the leader streams a `tar.gz` snapshot of the default DB in chunks of `BARADB_RAFT_SNAP_CHUNK_KB` KiB (default 256) to peers whose lag is unrecoverable; the follower restores via the backup/restore path and resumes from the snapshot base. Leader compaction unpins from peers stale beyond `BARADB_RAFT_PEER_STALE_MS` (default 30000). E2E `tests/raft_coldnode_e2e_test.nim` (returning node and wiped node converge automatically) + +### Fixes + +- **Raft put/delete encoding** — `ExecResult.keyValuePairs` carries an explicit `deleted` flag; an INSERT into a PK-only table (empty value) is no longer encoded as a `delete` and erased on apply; regression tests in `tests/bugfix_test.nim` +- **Rejoin livelock** — on leadership acquisition the leader drops cached peer sockets; half-dead sockets to a restarted peer previously never errored, so no redial ever happened and the cluster livelocked without heartbeats +- **Post-restore ctx repoint** — after an InstallSnapshot restore, the TCP serving ctx/db is repointed at the reopened database (queries previously read the closed pre-restore LSM and served 0 rows). Remaining limitation for startup-captured HTTP ctx: see [known-limitations](docs/en/known-limitations.md) +- **Intermediate InstallSnapshot chunk replies ignored** — the leader acts only on the final chunk reply + +### Release + +- `baradadb.nimble` → `1.3.0`; `/health` and startup version strings updated +- Docs: [distributed](docs/en/distributed.md) (failover contract, raft TLS setup, snapshot tunables), [known-limitations](docs/en/known-limitations.md) (raft supported scope + two newly documented limitations), [release-checklist](docs/en/release-checklist.md) + +--- + ## [1.2.0] — 2026-07-30 ### Production GA (single-node) diff --git a/README.md b/README.md index bf42b19..12da63b 100644 --- a/README.md +++ b/README.md @@ -1576,7 +1576,7 @@ features are still being refined: | LSM-Tree SSTable reads | ✅ Implemented | Full disk I/O with compaction, WAL, and bloom filters. | | HNSW vector search | ✅ Implemented | Hierarchical graph navigation with SIMD-optimized distance metrics. | | TCP server execution | ✅ Implemented | Full binary wire protocol parsing and BaraQL query execution. | -| Raft consensus | ⚡ Experimental cluster | TCP election + SQL/DDL via log; single-node is **Production GA**. See `docs/en/known-limitations.md`. | +| Raft consensus | ✅ Supported (3-node, `default` DB) | TCP election + SQL/DDL via log; failover under load, raft TLS, InstallSnapshot recovery e2e-proven. See `docs/en/known-limitations.md`. | | Graph / FTS / Columnar | ✅ Implemented | In-memory engines with serialization; FTS/vector/graph indexes persist across restarts. | | Query codegen | ✅ Implemented | IR plans compile to storage engine operations with optimization passes. | @@ -1585,10 +1585,10 @@ reflects 100% completion across all major phases. ## Changelog -See [CHANGELOG.md](CHANGELOG.md) for full release history. Package version is **v1.2.0**. +See [CHANGELOG.md](CHANGELOG.md) for full release history. Package version is **v1.3.0**. +- **Raft multi-node supported (v1.3.0):** failover under load, mandatory CI gate, raft TLS, InstallSnapshot cold-node recovery — [distributed.md](docs/en/distributed.md), [known-limitations](docs/en/known-limitations.md) - **Production GA (single-node):** auth-on prod compose, backup/restore drill, runbook — [known-limitations](docs/en/known-limitations.md), [deployment](docs/en/deployment.md) -- **Raft multi-node:** experimental — [distributed.md](docs/en/distributed.md) ## License diff --git a/baradadb.nimble b/baradadb.nimble index a4c3713..82d3bed 100644 --- a/baradadb.nimble +++ b/baradadb.nimble @@ -1,5 +1,5 @@ # Package -version = "1.2.0" +version = "1.3.0" author = "BaraDB Team" description = "BaraDB — Multimodal database written in Nim" license = "BSD-3-Clause" diff --git a/src/barabadb/core/httpserver.nim b/src/barabadb/core/httpserver.nim index b9739a9..09c72cf 100644 --- a/src/barabadb/core/httpserver.nim +++ b/src/barabadb/core/httpserver.nim @@ -262,7 +262,7 @@ proc healthHandler(server: HttpServer): RequestHandler = let ctx = newContext(request) var body = %*{ "status": "ok", - "version": "1.2.0" + "version": "1.3.0" } if server.raftNode != nil: let n = server.raftNode @@ -368,7 +368,7 @@ proc openApiHandler(): RequestHandler = let ctx = newContext(request) ctx.json(%*{ "openapi": "3.0.0", - "info": {"title": "BaraDB API", "version": "1.2.0"}, + "info": {"title": "BaraDB API", "version": "1.3.0"}, "paths": { "/query": { "post": { @@ -906,7 +906,7 @@ function showTab(idx){ } setInterval(() => { if(document.querySelectorAll('.panel')[4].classList.contains('active')) loadMetrics() }, 5000) -