From 09f462f4672b4db2d49b8b7f810b55d0b31f8594 Mon Sep 17 00:00:00 2001 From: dimgigov Date: Thu, 30 Jul 2026 21:52:21 +0300 Subject: [PATCH] release: v1.2.0 Production GA (single-node) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - known-limitations, deployment runbook, release checklist - prod compose requires JWT secret; BARADB_ENV=production fail-closed - scripts/backup-restore-drill.sh (backup → wipe → restore → verify) - version bump 1.2.0 (nimble, Dockerfile, health, CHANGELOG dated) --- CHANGELOG.md | 10 +- Dockerfile | 2 +- PLAN.md | 2 +- README.md | 7 +- baradadb.nimble | 2 +- docker-compose.prod.yml | 81 +++-- docs/bg/deployment.md | 255 ++------------ docs/bg/known-limitations.md | 33 ++ docs/en/deployment.md | 311 +++++++++--------- docs/en/distributed.md | 2 +- docs/en/known-limitations.md | 55 ++++ docs/en/release-checklist.md | 57 ++++ .../specs/2026-07-30-production-ga-design.md | 2 +- scripts/backup-restore-drill.sh | 135 ++++++++ src/barabadb/core/config.nim | 22 ++ src/barabadb/core/httpserver.nim | 6 +- src/baradadb.nim | 9 +- tests/bugfix_test.nim | 23 ++ 18 files changed, 560 insertions(+), 454 deletions(-) create mode 100644 docs/bg/known-limitations.md create mode 100644 docs/en/known-limitations.md create mode 100644 docs/en/release-checklist.md create mode 100755 scripts/backup-restore-drill.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index aa35676..852b993 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,15 @@ All notable changes to BaraDB are documented in this file. -## [1.2.0] — Unreleased +## [1.2.0] — 2026-07-30 + +### Production GA (single-node) + +- **Scope** — single-node production tier; Raft multi-node documented as experimental ([known-limitations](docs/en/known-limitations.md)) +- **Prod compose** — `docker-compose.prod.yml` requires `BARADB_JWT_SECRET` and enables auth; `BARADB_ENV=production` fails closed without secret +- **Backup drill** — `scripts/backup-restore-drill.sh` (backup → wipe → restore → verify) +- **Runbook** — start/stop/backup/restore in [deployment](docs/en/deployment.md) +- **Release checklist** — [docs/en/release-checklist.md](docs/en/release-checklist.md) ### Raft cluster (C3a / C3b / ops) diff --git a/Dockerfile b/Dockerfile index 3c14f29..8105804 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,7 +19,7 @@ ARG VCS_REF LABEL maintainer="BaraDB Team" LABEL description="BaraDB — Multimodal Database Engine" -LABEL version="1.1.6" +LABEL version="1.2.0" # Инсталираме runtime зависимости # libpcre3 — нужна за Nim regex (зарежда се динамично) diff --git a/PLAN.md b/PLAN.md index d3f70b0..68b3028 100644 --- a/PLAN.md +++ b/PLAN.md @@ -156,7 +156,7 @@ | `PLAN_ID_GENERATORS.md` — AUTO_INCREMENT, Sequences, FK | ✅ Завършен | | **Този план** — Сесии 10, 11, 12 | ✅ Завършен | | Raft C3a/C3b + DDL/forward/compact/metrics (2026-07-30) | ✅ Завършен на `main` — `docs/superpowers/specs/2026-07-30-raft-cluster-status.md` | -| **Production GA v1.2.0** (single-node) | 📋 План — `docs/superpowers/plans/2026-07-30-production-ga.md` | +| **Production GA v1.2.0** (single-node) | ✅ `docs/superpowers/plans/2026-07-30-production-ga.md` | --- diff --git a/README.md b/README.md index 74e973e..bf42b19 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 | ✅ Cluster path | TCP election + failover; SQL DML/DDL via raft log; leader forwarding; safe log compact; `/metrics` + `/health` raft gauges. See `docs/en/distributed.md`. | +| Raft consensus | ⚡ Experimental cluster | TCP election + SQL/DDL via log; single-node is **Production GA**. 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,7 +1585,10 @@ reflects 100% completion across all major phases. ## Changelog -See [CHANGELOG.md](CHANGELOG.md) for full release history. Package version is **v1.1.8**. The **v1.2.0** line (Unreleased) adds core storage hardening, Unified Search Engine, **engine persistence** (FTS/HNSW/graphs/B-tree indexes across restart), **executor split**, and a full **Raft cluster path** (election, SQL/DDL replication, forwarding, log compact, metrics — see [docs/en/distributed.md](docs/en/distributed.md)). +See [CHANGELOG.md](CHANGELOG.md) for full release history. Package version is **v1.2.0**. + +- **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 44d636d..300557a 100644 --- a/baradadb.nimble +++ b/baradadb.nimble @@ -1,5 +1,5 @@ # Package -version = "1.1.8" +version = "1.2.0" author = "BaraDB Team" description = "BaraDB — Multimodal database written in Nim" license = "BSD-3-Clause" diff --git a/docker-compose.prod.yml b/docker-compose.prod.yml index 99d979e..ab0e545 100644 --- a/docker-compose.prod.yml +++ b/docker-compose.prod.yml @@ -1,82 +1,87 @@ -# BaraDB — Production Docker Compose -# Usage: docker compose -f docker-compose.prod.yml up -d +# BaraDB — Production Docker Compose (v1.2.0 GA) # -# Препоръчителни стъпки преди production deployment: -# 1. Създайте TLS сертификати в ./certs/ -# 2. Задайте силен BARADB_JWT_SECRET -# 3. Настройте firewall правила за портовете -# 4. Конфигурирайте регулярни backups +# Usage: +# export BARADB_JWT_SECRET="$(openssl rand -hex 32)" +# docker compose -f docker-compose.prod.yml up -d --build +# +# Required: +# BARADB_JWT_SECRET — strong secret (compose fails if unset) +# +# Ports (BARADB_PORT=9472): +# 9472 binary wire +# 9912 HTTP (= TCP + 440) +# 9913 WebSocket (= TCP + 441) +# +# Notes: +# - Auth is ON. Obtain a token via POST /auth before /query. +# - `deploy.resources` applies under Swarm; plain Compose ignores limits. +# - Raft is optional/experimental — not enabled here (single-node GA). services: baradb: build: context: . dockerfile: Dockerfile - image: baradb:latest + image: baradb:1.2.0 container_name: baradb hostname: baradb restart: always ports: - "9472:9472" # Binary protocol - - "9912:9912" # HTTP/REST API - - "9913:9913" # WebSocket + - "9912:9912" # HTTP REST (TCP+440) + - "9913:9913" # WebSocket (TCP+441) volumes: - baradb_data:/data - # TLS сертификати (read-only) - ./certs:/certs:ro - # Лог файлове на хоста - ./logs:/var/log/baradb environment: - # Network + - BARADB_ENV=production - BARADB_ADDRESS=0.0.0.0 - BARADB_PORT=9472 - # Storage - BARADB_DATA_DIR=/data - BARADB_MEMTABLE_SIZE_MB=256 - BARADB_CACHE_SIZE_MB=512 - # TLS (разкоментирайте когато имате сертификати) + # Security — fail closed without a real secret + - BARADB_AUTH_ENABLED=true + - BARADB_JWT_SECRET=${BARADB_JWT_SECRET:?Set BARADB_JWT_SECRET to a strong random value} + - BARADB_RATE_LIMIT_GLOBAL=10000 + - BARADB_RATE_LIMIT_PER_CLIENT=1000 + + # TLS (uncomment when certs exist under ./certs) # - BARADB_TLS_ENABLED=true # - BARADB_CERT_FILE=/certs/server.crt # - BARADB_KEY_FILE=/certs/server.key - # Security (ЗАДЪЛЖИТЕЛНО сменете в production!) - # - BARADB_AUTH_ENABLED=true - # - BARADB_JWT_SECRET=change-me-to-random-32-char-string - # - BARADB_RATE_LIMIT_GLOBAL=10000 - # - BARADB_RATE_LIMIT_PER_CLIENT=1000 - - # Logging - BARADB_LOG_LEVEL=warn - BARADB_LOG_FILE=/var/log/baradb/baradb.log - BARADB_LOG_FORMAT=json - # Performance - BARADB_COMPACTION_INTERVAL_MS=30000 - - BARADB_WAL_SYNC_INTERVAL_MS=10 + # Match config.nim env names + - BARADB_WAL_SYNC_MODE=group + - BARADB_WAL_GROUP_EVERY=64 healthcheck: - test: ["CMD", "sh", "-c", "wget -qO- http://localhost:9912/health >/dev/null 2>&1"] + test: ["CMD", "sh", "-c", "wget -qO- http://127.0.0.1:9912/health >/dev/null 2>&1"] interval: 15s timeout: 5s retries: 5 start_period: 30s - # Production resource limits deploy: resources: limits: - cpus: '4.0' + cpus: "4.0" memory: 8G reservations: - cpus: '1.0' + cpus: "1.0" memory: 1G - # Security hardening security_opt: - no-new-privileges:true read_only: true @@ -91,19 +96,19 @@ services: options: max-size: "100m" max-file: "5" - labels: "service_name" - # Опционален: Backup cron job + # Optional offline-style backup sidecar (shares data volume read-only) backup: - image: baradb:latest + image: baradb:1.2.0 container_name: baradb-backup restart: unless-stopped + profiles: ["backup"] command: > sh -c ' while true; do sleep 86400; - /app/backup backup --all-databases --data-root=/data/databases --output=/backups/baradb_$$(date +%Y%m%d_%H%M%S).tar.gz --level=6; - /app/backup cleanup --data-root=/data/databases --keep=7; + /app/backup backup --all-databases --data-root=/data/databases --output=/backups/baradb_$$(date +%Y%m%d_%H%M%S).tar.gz --level=6 || true; + /app/backup cleanup --data-root=/data/databases --keep=7 || true; done ' volumes: @@ -111,11 +116,6 @@ services: - ./backups:/backups networks: - baradb_net - deploy: - resources: - limits: - cpus: '0.5' - memory: 512M volumes: baradb_data: @@ -124,6 +124,3 @@ volumes: networks: baradb_net: driver: bridge - ipam: - config: - - subnet: 172.28.0.0/16 diff --git a/docs/bg/deployment.md b/docs/bg/deployment.md index fa8f641..b62dd30 100644 --- a/docs/bg/deployment.md +++ b/docs/bg/deployment.md @@ -1,250 +1,35 @@ -# Ръководство за Внедряване (Deployment) +# Deployment Guide (кратко) -## Docker +**Production GA v1.2.0** = **single-node**. Виж [known-limitations](known-limitations.md). +Пълен runbook: [en/deployment.md](../en/deployment.md). -За пълно ръководство за Docker deployment вижте [Docker Guide](docker.md). +## Портове -### Бърз старт +| Услуга | Порт | +|--------|------| +| Wire | `BARADB_PORT` (9472) | +| HTTP | `BARADB_PORT + 440` (9912) | +| WebSocket | `BARADB_PORT + 441` (9913) | + +Няма `BARADB_HTTP_PORT`. + +## Production Docker ```bash -docker build -t baradb:latest . -docker compose up -d +export BARADB_JWT_SECRET="$(openssl rand -hex 32)" +docker compose -f docker-compose.prod.yml up -d --build ``` -### Docker Compose файлове +Auth е включен; без secret compose **спира**. -| Файл | Назначение | -|------|-----------| -| `docker-compose.yml` | Development | -| `docker-compose.prod.yml` | Production | -| `docker-compose.override.yml` | Dev override (автоматично) | -| `docker-compose.test.yml` | Тестова среда | - -### Production +## Backup / restore drill ```bash -docker compose -f docker-compose.prod.yml up -d +./scripts/backup-restore-drill.sh ``` -### Docker Swarm +## Health ```bash -docker stack deploy -c docker-compose.prod.yml baradb -``` - -## systemd Услуга - -Създайте `/etc/systemd/system/baradb.service`: - -```ini -[Unit] -Description=BaraDB Multimodal Database -After=network.target - -[Service] -Type=simple -User=baradb -Group=baradb -WorkingDirectory=/var/lib/baradb -ExecStart=/usr/local/bin/baradadb -Restart=always -RestartSec=5 - -Environment=BARADB_PORT=9472 -Environment=BARADB_HTTP_PORT=9470 -Environment=BARADB_DATA_DIR=/var/lib/baradb/data -Environment=BARADB_LOG_LEVEL=info - -# Подсилване на сигурността -NoNewPrivileges=true -ProtectSystem=strict -ProtectHome=true -ReadWritePaths=/var/lib/baradb/data -ProtectKernelTunables=true -ProtectKernelModules=true -ProtectControlGroups=true - -[Install] -WantedBy=multi-user.target -``` - -Активиране и стартиране: - -```bash -sudo useradd -r -s /bin/false baradb -sudo mkdir -p /var/lib/baradb/data -sudo chown -R baradb:baradb /var/lib/baradb -sudo cp build/baradadb /usr/local/bin/ -sudo systemctl daemon-reload -sudo systemctl enable --now baradb -``` - -## Kubernetes - -### StatefulSet - -```yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: baradb -spec: - serviceName: baradb - replicas: 3 - selector: - matchLabels: - app: baradb - template: - metadata: - labels: - app: baradb - spec: - containers: - - name: baradb - image: baradb:latest - ports: - - containerPort: 9472 - name: binary - - containerPort: 9470 - name: http - - containerPort: 9471 - name: websocket - env: - - name: BARADB_DATA_DIR - value: /data - - name: BARADB_RAFT_NODE_ID - valueFrom: - fieldRef: - fieldPath: metadata.name - volumeMounts: - - name: data - mountPath: /data - volumeClaimTemplates: - - metadata: - name: data - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 100Gi ---- -apiVersion: v1 -kind: Service -metadata: - name: baradb -spec: - selector: - app: baradb - ports: - - port: 9472 - name: binary - - port: 9470 - name: http - - port: 9471 - name: websocket - clusterIP: None -``` - -## Reverse Proxy (nginx) - -```nginx -upstream baradb_http { - server 127.0.0.1:9470; -} - -upstream baradb_ws { - server 127.0.0.1:9471; -} - -server { - listen 80; - server_name db.example.com; - return 301 https://$server_name$request_uri; -} - -server { - listen 443 ssl http2; - server_name db.example.com; - - ssl_certificate /etc/letsencrypt/live/db.example.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/db.example.com/privkey.pem; - - location /api/ { - proxy_pass http://baradb_http/; - proxy_http_version 1.1; - proxy_set_header Host $host; - proxy_set_header X-Real-IP $remote_addr; - } - - location /ws/ { - proxy_pass http://baradb_ws/; - proxy_http_version 1.1; - proxy_set_header Upgrade $http_upgrade; - proxy_set_header Connection "upgrade"; - } -} -``` - -## Висока Достъпност (High Availability) - -### 3-Възел Raft Клъстер - -```bash -# Възел 1 -BARADB_RAFT_NODE_ID=node1 \ -BARADB_RAFT_PEERS=node2:9001,node3:9001 \ -./build/baradadb - -# Възел 2 -BARADB_RAFT_NODE_ID=node2 \ -BARADB_RAFT_PEERS=node1:9001,node3:9001 \ -./build/baradadb - -# Възел 3 -BARADB_RAFT_NODE_ID=node3 \ -BARADB_RAFT_PEERS=node1:9001,node2:9001 \ -./build/baradadb -``` - -## Облачно Внедряване - -### AWS EC2 - -Препоръчителна инстанция: `m6i.2xlarge` (8 vCPU, 32 GB RAM) - -```bash -# User data скрипт -#!/bin/bash -apt-get update -apt-get install -y nim -wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-amd64 -chmod +x baradadb-linux-amd64 -mv baradadb-linux-amd64 /usr/local/bin/baradadb - -mkdir -p /data/baradb -cat > /etc/systemd/system/baradb.service << 'EOF' -[Unit] -Description=BaraDB -After=network.target -[Service] -ExecStart=/usr/local/bin/baradadb -Environment=BARADB_DATA_DIR=/data/baradb -Restart=always -[Install] -WantedBy=multi-user.target -EOF - -systemctl daemon-reload -systemctl enable --now baradb -``` - -### GCP Cloud Run (само HTTP) - -```bash -gcloud run deploy baradb \ - --image gcr.io/PROJECT/baradb \ - --port 9470 \ - --memory 4Gi \ - --cpu 2 \ - --max-instances 10 +curl -s http://127.0.0.1:9912/health ``` diff --git a/docs/bg/known-limitations.md b/docs/bg/known-limitations.md new file mode 100644 index 0000000..cb17ccf --- /dev/null +++ b/docs/bg/known-limitations.md @@ -0,0 +1,33 @@ +# Известни ограничения — v1.2.0 Production GA + +| Ниво | Значение | +|------|----------| +| **Supported (GA)** | Документирано, тествано, подходящо за prod в обхвата | +| **Experimental** | Работи в тестове/demo; не е HA SLA | +| **Not supported** | Извън обхват | + +## Матрица + +| Област | GA (v1.2.0) | Experimental / по-късно | +|--------|-------------|-------------------------| +| Single-node SQL + LSM | **Supported** | — | +| Schema / FTS / HNSW / graphs persist | **Supported** | — | +| Auth + JWT (когато е конфигуриран) | **Supported** | — | +| Backup / restore | **Supported** | — | +| Multi-DB (без Raft) | **Supported** | — | +| Raft 3-node + SQL/DDL | **Experimental** | InstallSnapshot, membership | +| Raft multi-DB | **Not supported** | само `default` | +| Follower linearizable reads | **Not supported** | best-effort след apply | +| ORC multi-thread shared LSM | **Not supported** | ARC по подразбиране | + +## GA (single-node) + +Crash recovery с WAL, schema/index persist, `/health` + `/metrics`, offline backup/restore. + +## Raft + +Виж [distributed.md](distributed.md). Staging/ops, **не** v1.2.0 HA продукт. + +## Виж също + +- [Deployment](deployment.md) · [Backup](backup.md) · [en limitations](../en/known-limitations.md) diff --git a/docs/en/deployment.md b/docs/en/deployment.md index 7de7ab9..ceb825f 100644 --- a/docs/en/deployment.md +++ b/docs/en/deployment.md @@ -1,39 +1,145 @@ # Deployment Guide +**Production GA (v1.2.0)** is **single-node**. See [known limitations](known-limitations.md). +Raft multi-node is [documented](distributed.md) as **experimental**. + +## Ports + +| Service | Port | Notes | +|---------|------|--------| +| Binary wire | `BARADB_PORT` (default 9472) | Clients | +| HTTP REST | `BARADB_PORT + 440` (9912) | `/health`, `/query`, `/metrics` | +| WebSocket | `BARADB_PORT + 441` (9913) | | +| Raft | `BARADB_RAFT_PORT` | Experimental cluster only | + +There is **no** `BARADB_HTTP_PORT` — HTTP is always TCP+440. + ## Docker -За пълно ръководство за Docker deployment вижте [Docker Guide](docker.md). +See also [Docker Guide](docker.md). -### Бърз старт +### Development ```bash docker build -t baradb:latest . docker compose up -d ``` -### Docker Compose файлове +### Production (GA) -| Файл | Назначение | -|------|-----------| +```bash +export BARADB_JWT_SECRET="$(openssl rand -hex 32)" +docker compose -f docker-compose.prod.yml up -d --build +``` + +- Auth is **on**; compose **fails** if `BARADB_JWT_SECRET` is unset. +- Binary sets `BARADB_ENV=production` → process refuses empty/placeholder secrets. +- Image tag: `baradb:1.2.0`. + +Optional backup sidecar: + +```bash +docker compose -f docker-compose.prod.yml --profile backup up -d +``` + +| Compose file | Role | +|--------------|------| | `docker-compose.yml` | Development | -| `docker-compose.prod.yml` | Production | -| `docker-compose.override.yml` | Dev override (автоматично) | +| `docker-compose.prod.yml` | Production GA | +| `docker-compose.override.yml` | Local override | -### Production +> Note: `deploy.resources` limits apply under Docker Swarm; plain Compose may ignore them. + +## Production runbook + +### Start (binary) ```bash -docker compose -f docker-compose.prod.yml up -d +export BARADB_ENV=production +export BARADB_AUTH_ENABLED=true +export BARADB_JWT_SECRET="$(openssl rand -hex 32)" # store securely +export BARADB_PORT=9472 +export BARADB_DATA_DIR=/var/lib/baradb/data +export BARADB_LOG_LEVEL=warn +export BARADB_LOG_FILE=/var/log/baradb/baradb.log +./build/baradadb ``` -### Docker Swarm +### Stop ```bash -docker stack deploy -c docker-compose.prod.yml baradb +# systemd +sudo systemctl stop baradb +# docker +docker compose -f docker-compose.prod.yml down +# foreground: Ctrl+C / SIGTERM ``` -## systemd Service +### Health / metrics -Create `/etc/systemd/system/baradb.service`: +```bash +curl -s http://127.0.0.1:9912/health +curl -s -H "Authorization: Bearer $TOKEN" http://127.0.0.1:9912/metrics +``` + +### Auth token (prod) + +```bash +curl -s -X POST http://127.0.0.1:9912/auth \ + -H 'Content-Type: application/json' \ + -d "{\"username\":\"admin\",\"password\":\"$BARADB_JWT_SECRET\"}" +``` + +### Backup (server stopped or offline-consistent) + +Preferred offline / all-databases: + +```bash +./build/backup backup --all-databases \ + --data-root=/var/lib/baradb/data/databases \ + --output=/backups/baradb_$(date +%Y%m%d_%H%M%S).tar.gz +``` + +Copy archives off-host. Details: [backup.md](backup.md). + +### Restore + +1. **Stop** BaraDB. +2. Move aside or empty the data root (keep a copy of the broken dir). +3. Restore: + +```bash +./build/backup restore --input=/backups/baradb_YYYYMMDD.tar.gz \ + --all-databases --data-root=/var/lib/baradb/data/databases --force +``` + +4. Start BaraDB; verify with a known query. + +### Automated drill + +```bash +nim c -o:build/baradadb src/baradadb.nim +nim c -o:build/backup src/barabadb/core/backup.nim +./scripts/backup-restore-drill.sh +``` + +### Logs + +- File: `BARADB_LOG_FILE` (prod compose: `./logs` → `/var/log/baradb`) +- Docker: `docker logs baradb` + +### Data layout + +``` +$BARADB_DATA_DIR/ + databases/ + default/ # LSM + WAL + schema keys + raft/ # only if raft enabled +``` + +## systemd + +`/etc/systemd/system/baradb.service`: ```ini [Unit] @@ -49,16 +155,18 @@ ExecStart=/usr/local/bin/baradadb Restart=always RestartSec=5 +Environment=BARADB_ENV=production Environment=BARADB_PORT=9472 -Environment=BARADB_HTTP_PORT=9470 Environment=BARADB_DATA_DIR=/var/lib/baradb/data -Environment=BARADB_LOG_LEVEL=info +Environment=BARADB_LOG_LEVEL=warn +Environment=BARADB_AUTH_ENABLED=true +# Environment=BARADB_JWT_SECRET= # use EnvironmentFile +EnvironmentFile=-/etc/baradb/baradb.env -# Security hardening NoNewPrivileges=true ProtectSystem=strict ProtectHome=true -ReadWritePaths=/var/lib/baradb/data +ReadWritePaths=/var/lib/baradb/data /var/log/baradb ProtectKernelTunables=true ProtectKernelModules=true ProtectControlGroups=true @@ -67,114 +175,47 @@ ProtectControlGroups=true WantedBy=multi-user.target ``` -Enable and start: - ```bash sudo useradd -r -s /bin/false baradb -sudo mkdir -p /var/lib/baradb/data -sudo chown -R baradb:baradb /var/lib/baradb -sudo cp build/baradadb /usr/local/bin/ +sudo mkdir -p /var/lib/baradb/data /var/log/baradb /etc/baradb +# put BARADB_JWT_SECRET=... in /etc/baradb/baradb.env (mode 600) sudo systemctl daemon-reload sudo systemctl enable --now baradb ``` -## Kubernetes +## High Availability (experimental) -### StatefulSet +Raft multi-node is **not** the v1.2.0 GA tier. See [distributed.md](distributed.md) +and [known-limitations](known-limitations.md). Use `id@host:port` peer format: -```yaml -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: baradb -spec: - serviceName: baradb - replicas: 3 - selector: - matchLabels: - app: baradb - template: - metadata: - labels: - app: baradb - spec: - containers: - - name: baradb - image: baradb:latest - ports: - - containerPort: 9472 - name: binary - - containerPort: 9470 - name: http - - containerPort: 9471 - name: websocket - env: - - name: BARADB_DATA_DIR - value: /data - - name: BARADB_RAFT_NODE_ID - valueFrom: - fieldRef: - fieldPath: metadata.name - volumeMounts: - - name: data - mountPath: /data - volumeClaimTemplates: - - metadata: - name: data - spec: - accessModes: ["ReadWriteOnce"] - resources: - requests: - storage: 100Gi ---- -apiVersion: v1 -kind: Service -metadata: - name: baradb -spec: - selector: - app: baradb - ports: - - port: 9472 - name: binary - - port: 9470 - name: http - - port: 9471 - name: websocket - clusterIP: None +```bash +export BARADB_RAFT_ENABLED=true +export BARADB_RAFT_NODE_ID=n1 +export BARADB_RAFT_PORT=46101 +export BARADB_RAFT_PEERS=n1@127.0.0.1:46101,n2@127.0.0.1:46102,n3@127.0.0.1:46103 +export BARADB_RAFT_CLIENT_PEERS=n1@127.0.0.1:46010,n2@127.0.0.1:46020,n3@127.0.0.1:46030 ``` -## Reverse Proxy (nginx) +## Reverse proxy (nginx) + +Proxy to **HTTP = TCP+440** (9912 if TCP is 9472): ```nginx upstream baradb_http { - server 127.0.0.1:9470; + server 127.0.0.1:9912; } - upstream baradb_ws { - server 127.0.0.1:9471; + server 127.0.0.1:9913; } - -server { - listen 80; - server_name db.example.com; - return 301 https://$server_name$request_uri; -} - server { listen 443 ssl http2; server_name db.example.com; - - ssl_certificate /etc/letsencrypt/live/db.example.com/fullchain.pem; - ssl_certificate_key /etc/letsencrypt/live/db.example.com/privkey.pem; - - location /api/ { - proxy_pass http://baradb_http/; + location / { + proxy_pass http://baradb_http; proxy_http_version 1.1; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; } - location /ws/ { proxy_pass http://baradb_ws/; proxy_http_version 1.1; @@ -184,66 +225,8 @@ server { } ``` -## High Availability +## See also -### 3-Node Raft Cluster - -```bash -# Node 1 -BARADB_RAFT_NODE_ID=node1 \ -BARADB_RAFT_PEERS=node2:9001,node3:9001 \ -./build/baradadb - -# Node 2 -BARADB_RAFT_NODE_ID=node2 \ -BARADB_RAFT_PEERS=node1:9001,node3:9001 \ -./build/baradadb - -# Node 3 -BARADB_RAFT_NODE_ID=node3 \ -BARADB_RAFT_PEERS=node1:9001,node2:9001 \ -./build/baradadb -``` - -## Cloud Deployment - -### AWS EC2 - -Recommended instance: `m6i.2xlarge` (8 vCPU, 32 GB RAM) - -```bash -# User data script -#!/bin/bash -apt-get update -apt-get install -y nim -wget https://github.com/katehonz/barabaDB/releases/latest/download/baradadb-linux-amd64 -chmod +x baradadb-linux-amd64 -mv baradadb-linux-amd64 /usr/local/bin/baradadb - -mkdir -p /data/baradb -cat > /etc/systemd/system/baradb.service << 'EOF' -[Unit] -Description=BaraDB -After=network.target -[Service] -ExecStart=/usr/local/bin/baradadb -Environment=BARADB_DATA_DIR=/data/baradb -Restart=always -[Install] -WantedBy=multi-user.target -EOF - -systemctl daemon-reload -systemctl enable --now baradb -``` - -### GCP Cloud Run (HTTP only) - -```bash -gcloud run deploy baradb \ - --image gcr.io/PROJECT/baradb \ - --port 9470 \ - --memory 4Gi \ - --cpu 2 \ - --max-instances 10 -``` +- [Known limitations](known-limitations.md) +- [Release checklist](release-checklist.md) +- [Backup](backup.md) · [Monitoring](monitoring.md) · [Distributed / Raft](distributed.md) diff --git a/docs/en/distributed.md b/docs/en/distributed.md index a217114..8a05c98 100644 --- a/docs/en/distributed.md +++ b/docs/en/distributed.md @@ -5,7 +5,7 @@ BaraDB supports distributed deployment with Raft consensus, sharding, and replic > ⚠️ **Multi-Database Limitation** > The distributed modules (Raft, sharding, and replication) are currently wired to the **`default`** database only. If you use multiple databases (`CREATE DATABASE`, `USE DATABASE`), distributed features do not yet span across them. Each database would need its own cluster setup. -> **Status (2026-07-30):** Raft C3a (network election), C3b (SQL writes), DDL replication, leader forwarding, log compaction, and metrics are **shipped on `main`**. Design/history: `docs/superpowers/specs/2026-07-30-raft-cluster-status.md`. +> **Status (2026-07-30):** Raft C3a/C3b + DDL/forward/compact/metrics are **shipped**. Multi-node Raft is **experimental** for v1.2.0 GA (single-node is the production tier). See [known-limitations](known-limitations.md) and `docs/superpowers/specs/2026-07-30-raft-cluster-status.md`. ## Raft Consensus diff --git a/docs/en/known-limitations.md b/docs/en/known-limitations.md new file mode 100644 index 0000000..bf64708 --- /dev/null +++ b/docs/en/known-limitations.md @@ -0,0 +1,55 @@ +# Known Limitations — v1.2.0 Production GA + +This page defines **what BaraDB promises** in the v1.2.0 production cut. + +| Tier | Meaning | +|------|---------| +| **Supported (GA)** | Documented, tested, appropriate for production apps that fit the scope | +| **Experimental** | Works in tests/ops demos; not a reliability SLA target | +| **Not supported** | Out of scope; may fail or corrupt assumptions | + +## Support matrix + +| Area | GA (v1.2.0) | Experimental / later | +|------|-------------|----------------------| +| Single-node SQL + LSM storage | **Supported** | — | +| Schema persistence (tables, indexes) | **Supported** | — | +| FTS / HNSW / graphs across restart | **Supported** | — | +| Auth + JWT (when configured) | **Supported** | — | +| Backup / restore (offline, all-databases) | **Supported** | — | +| Multi-database (non-Raft) | **Supported** | — | +| Raft 3-node election + SQL/DDL | **Experimental** | InstallSnapshot SM payload, membership | +| Raft multi-database | **Not supported** | only `default` | +| Leader write forwarding | **Experimental** | needs `BARADB_RAFT_CLIENT_PEERS` | +| Follower linearizable reads | **Not supported** | best-effort after apply | +| ORC multi-threaded shared LSM | **Not supported** | default is ARC (`nim.cfg`) | +| Zero-downtime rolling upgrade | **Not supported** | stop → backup → upgrade | +| Postgres wire protocol | **Not supported** | Bara wire + HTTP | + +## Single-node GA (what you can rely on) + +- Process crash + WAL recovery for the default durability settings +- CREATE TABLE / indexes that survive restart (see engine-persistence work) +- HTTP `/health` and `/metrics` for process liveness +- Offline backup of `data/databases` and restore onto an empty data root + +## Raft (experimental ops) + +Documented in [distributed.md](distributed.md). Suitable for learning and careful staging; **not** the v1.2.0 HA product tier. + +- SQL DML/DDL on **`default` only** +- Safe log prefix compact (not full InstallSnapshot) +- Failover proven in process e2e tests + +## Operational requirements + +- Set a strong `BARADB_JWT_SECRET` and `BARADB_AUTH_ENABLED=true` in production (see prod compose) +- Test restores regularly (`scripts/backup-restore-drill.sh`) +- Do not share one data directory between two running processes + +## See also + +- [Deployment / runbook](deployment.md) +- [Backup](backup.md) +- [Raft cluster status](../superpowers/specs/2026-07-30-raft-cluster-status.md) +- [Production GA plan](../superpowers/plans/2026-07-30-production-ga.md) diff --git a/docs/en/release-checklist.md b/docs/en/release-checklist.md new file mode 100644 index 0000000..abce0ee --- /dev/null +++ b/docs/en/release-checklist.md @@ -0,0 +1,57 @@ +# Release checklist — v1.2.0 Production GA + +Use before tagging and publishing artifacts. + +## Pre-flight + +- [ ] Working tree clean on `main` +- [ ] [Known limitations](known-limitations.md) accurate +- [ ] `CHANGELOG.md` has dated `## [1.2.0]` (not Unreleased for shipped items) +- [ ] `baradadb.nimble` version `1.2.0` + +## Tests + +```bash +nim c -o:build/baradadb src/baradadb.nim +nim c -o:build/backup src/barabadb/core/backup.nim + +nim c -d:ssl --threads:on --path:src -r tests/test_all.nim +nim c -d:ssl --threads:on --path:src -r tests/bugfix_test.nim +nim c -d:ssl --threads:on --path:src -r tests/test_schema_persist.nim + +# Ops drill (twice) +./scripts/backup-restore-drill.sh +DRILL_PORT=19482 ./scripts/backup-restore-drill.sh + +# Optional cluster e2e (experimental tier) +./tests/raft_e2e_test +./tests/raft_writes_e2e_test +``` + +## Production compose + +```bash +export BARADB_JWT_SECRET="$(openssl rand -hex 32)" +docker compose -f docker-compose.prod.yml config >/dev/null +# must fail without secret: +# (unset BARADB_JWT_SECRET; docker compose -f docker-compose.prod.yml config) +``` + +## Artifacts + +```bash +nimble build_release # or: nim c -d:release -o:build/baradadb src/baradadb.nim +docker build -t baradb:1.2.0 -t baradb:latest . +``` + +## Tag + +```bash +git tag -a v1.2.0 -m "BaraDB v1.2.0 Production GA (single-node)" +git push origin main --tags +``` + +## Post-release + +- [ ] Smoke: start prod compose, `/health` → ok, auth required for `/query` +- [ ] Announce: single-node GA; Raft experimental (link known-limitations) diff --git a/docs/superpowers/specs/2026-07-30-production-ga-design.md b/docs/superpowers/specs/2026-07-30-production-ga-design.md index 0015f20..626c7ba 100644 --- a/docs/superpowers/specs/2026-07-30-production-ga-design.md +++ b/docs/superpowers/specs/2026-07-30-production-ga-design.md @@ -1,7 +1,7 @@ # Production GA (v1.2.0) — Design / cut line Date: 2026-07-30 -Status: **Approved direction** — implementation follows the plan +Status: **Done (v1.2.0 GA shipped)** — plan `docs/superpowers/plans/2026-07-30-production-ga.md`. ## Problem diff --git a/scripts/backup-restore-drill.sh b/scripts/backup-restore-drill.sh new file mode 100755 index 0000000..9b91691 --- /dev/null +++ b/scripts/backup-restore-drill.sh @@ -0,0 +1,135 @@ +#!/usr/bin/env bash +# Backup → wipe → restore → verify drill for BaraDB single-node GA. +# Usage: ./scripts/backup-restore-drill.sh +set -euo pipefail + +ROOT="$(cd "$(dirname "$0")/.." && pwd)" +cd "$ROOT" + +BIN="${BARADB_BIN:-./build/baradadb}" +BACKUP_BIN="${BARADB_BACKUP_BIN:-./build/backup}" +PORT="${DRILL_PORT:-19472}" +HTTP_PORT=$((PORT + 440)) +WORKDIR="${DRILL_WORKDIR:-/tmp/baradb_backup_drill_$$}" +DATA_DIR="$WORKDIR/data" +ARCHIVE="$WORKDIR/drill_backup.tar.gz" +MARKER="drill-row-$$" + +die() { echo "FAIL: $*" >&2; cleanup; exit 1; } +log() { echo "[drill] $*"; } + +cleanup() { + if [[ -n "${SERVER_PID:-}" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then + kill "$SERVER_PID" 2>/dev/null || true + wait "$SERVER_PID" 2>/dev/null || true + fi + if [[ "${DRILL_KEEP:-1}" == "0" ]]; then + rm -rf "$WORKDIR" + fi +} +trap cleanup EXIT + +[[ -x "$BIN" ]] || die "missing $BIN — build: nim c -o:build/baradadb src/baradadb.nim" +if [[ ! -x "$BACKUP_BIN" ]]; then + log "building backup tool..." + nim c -d:release -o:build/backup src/barabadb/core/backup.nim || die "cannot build backup tool" + BACKUP_BIN=./build/backup +fi + +rm -rf "$WORKDIR" +mkdir -p "$DATA_DIR" + +start_server() { + log "starting server port=$PORT data=$DATA_DIR" + # every = fsync each WAL write so kill/backup cannot lose recent puts + BARADB_PORT="$PORT" \ + BARADB_ADDRESS=127.0.0.1 \ + BARADB_DATA_DIR="$DATA_DIR" \ + BARADB_LOG_LEVEL=warn \ + BARADB_AUTH_ENABLED=false \ + BARADB_WAL_SYNC_MODE=every \ + "$BIN" >"$WORKDIR/server.log" 2>&1 & + SERVER_PID=$! + local i=0 + while (( i < 80 )); do + if curl -sf "http://127.0.0.1:${HTTP_PORT}/health" >/dev/null 2>&1; then + log "server ready pid=$SERVER_PID" + return 0 + fi + sleep 0.15 + i=$((i + 1)) + done + tail -50 "$WORKDIR/server.log" >&2 || true + die "server not healthy on :$HTTP_PORT" +} + +stop_server() { + if [[ -n "${SERVER_PID:-}" ]] && kill -0 "$SERVER_PID" 2>/dev/null; then + log "stopping pid=$SERVER_PID" + kill -TERM "$SERVER_PID" 2>/dev/null || true + local i=0 + while kill -0 "$SERVER_PID" 2>/dev/null && (( i < 50 )); do + sleep 0.1 + i=$((i + 1)) + done + if kill -0 "$SERVER_PID" 2>/dev/null; then + kill -KILL "$SERVER_PID" 2>/dev/null || true + fi + wait "$SERVER_PID" 2>/dev/null || true + fi + SERVER_PID="" + sleep 0.3 +} + +http_query() { + local sql="$1" + local payload + payload=$(python3 -c "import json,sys; print(json.dumps({'query': sys.argv[1]}))" "$sql") + curl -sf -H 'Content-Type: application/json' -d "$payload" \ + "http://127.0.0.1:${HTTP_PORT}/query" +} + +start_server + +log "CREATE + INSERT marker" +http_query "CREATE TABLE drill_t (id INT PRIMARY KEY, name STRING)" >/dev/null || die "CREATE failed" +http_query "INSERT INTO drill_t (id, name) VALUES (1, '$MARKER')" >/dev/null || die "INSERT failed" +body=$(http_query "SELECT name FROM drill_t WHERE id = 1") || die "SELECT before backup failed" +echo "$body" | grep -q "$MARKER" || die "marker missing before backup: $body" +# allow WAL group/fsync to settle +sleep 0.5 + +stop_server + +# Prefer multi-db layout used by the server registry +DB_ROOT="$DATA_DIR/databases" +[[ -d "$DB_ROOT" ]] || die "expected $DB_ROOT after server run" + +log "backup from $DB_ROOT" +"$BACKUP_BIN" backup --all-databases --data-root="$DB_ROOT" \ + --output="$ARCHIVE" --force || die "backup failed" +# Sanity: archive must not be tiny empty shell only +asize=$(stat -c%s "$ARCHIVE" 2>/dev/null || stat -f%z "$ARCHIVE") +(( asize > 200 )) || die "backup archive suspiciously small ($asize bytes)" + +log "wipe data" +rm -rf "$DATA_DIR" +mkdir -p "$DATA_DIR" + +log "restore multi-db archive into $DB_ROOT" +mkdir -p "$DATA_DIR" +# Archive layout is databases//... ; --data-root is the databases/ parent leaf +"$BACKUP_BIN" restore --input="$ARCHIVE" --all-databases \ + --data-root="$DB_ROOT" --force || die "restore failed" + +start_server + +log "verify after restore" +body=$(http_query "SELECT name FROM drill_t WHERE id = 1") || die "SELECT after restore failed" +echo "$body" | grep -q "$MARKER" || die "marker missing after restore: $body" + +log "PASS backup/restore drill OK marker=$MARKER archive=$ARCHIVE" +stop_server +trap - EXIT +[[ "${DRILL_KEEP:-1}" == "0" ]] && rm -rf "$WORKDIR" +exit 0 diff --git a/src/barabadb/core/config.nim b/src/barabadb/core/config.nim index 278a528..78e67ec 100644 --- a/src/barabadb/core/config.nim +++ b/src/barabadb/core/config.nim @@ -250,6 +250,28 @@ proc loadConfig*(): BaraConfig = # 2. Environment overrides (highest priority) loadConfigFromEnv(result) +proc isProductionEnv*(): bool = + ## True when BARADB_ENV=production (or prod) or BARADB_AUTH_REQUIRED=true. + let env = getEnv("BARADB_ENV", "").toLowerAscii() + if env == "production" or env == "prod": return true + parseEnvBool(getEnv("BARADB_AUTH_REQUIRED", ""), false) + +proc validateProductionConfig*(cfg: BaraConfig) = + ## Fail closed for production: auth on + non-empty JWT secret. + ## Call after loadConfig() from the main entrypoint. + if not isProductionEnv(): return + if not cfg.authEnabled: + raise newException(ValueError, + "Production refuses to start with auth disabled. " & + "Set BARADB_AUTH_ENABLED=true (or unset BARADB_ENV=production for local dev).") + if cfg.jwtSecret.len == 0: + raise newException(ValueError, + "Production refuses to start without BARADB_JWT_SECRET. " & + "Generate one: openssl rand -hex 32") + if cfg.jwtSecret in ["change-me", "change-me-to-random-32-char-string", "secret", "default"]: + raise newException(ValueError, + "Production refuses insecure JWT secret placeholder. Set a strong BARADB_JWT_SECRET.") + proc getEffectiveJwtSecret*(cfg: BaraConfig): string = if cfg.jwtSecret.len > 0: return cfg.jwtSecret diff --git a/src/barabadb/core/httpserver.nim b/src/barabadb/core/httpserver.nim index f142ed5..b9739a9 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.1.6" + "version": "1.2.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.1.6"}, + "info": {"title": "BaraDB API", "version": "1.2.0"}, "paths": { "/query": { "post": { @@ -906,7 +906,7 @@ function showTab(idx){ } setInterval(() => { if(document.querySelectorAll('.panel')[4].classList.contains('active')) loadMetrics() }, 5000) -
BaraDB v1.1.6 — Multimodal Database Engine
+
BaraDB v1.2.0 — Multimodal Database Engine
""" request.respond(200, @[("Content-Type", "text/html; charset=utf-8")], html) diff --git a/src/baradadb.nim b/src/baradadb.nim index 0df918c..5131d4c 100644 --- a/src/baradadb.nim +++ b/src/baradadb.nim @@ -278,15 +278,20 @@ proc main() = quit(0) var config = loadConfig() + try: + validateProductionConfig(config) + except ValueError as e: + stderr.writeLine("FATAL: " & e.msg) + quit(1) # Global exclusive gate for multi-thread storage (HTTP workers + TCP + compact) initStorageGate() # Init structured logger from config let logLvl = parseEnum[LogLevel]("ll" & capitalizeAscii(config.logLevel)) defaultLogger = newLogger(logLvl, config.logFile) - info("BaraDB v1.1.6 — Multimodal Database Engine") + info("BaraDB v1.2.0 — Multimodal Database Engine") info("Storage gate initialized (serializes HTTP/TCP/compaction access)") - # Security check: warn if JWT secret is not configured + # Security check: warn if JWT secret is not configured (non-production only) if config.jwtSecret.len == 0: warn("JWT secret not configured! Set BARADB_JWT_SECRET env var or jwt_secret in config. Using default (INSECURE).") diff --git a/tests/bugfix_test.nim b/tests/bugfix_test.nim index 45eadfd..b8df821 100644 --- a/tests/bugfix_test.nim +++ b/tests/bugfix_test.nim @@ -360,6 +360,29 @@ suite "Bug fixes — UNIQUE index enforcement": let c = executeQuery(ctx, parse("CREATE UNIQUE INDEX accts_email ON accts (email)")) check not c.success +suite "Production config gate": + + test "validateProductionConfig rejects missing secret": + putEnv("BARADB_ENV", "production") + defer: delEnv("BARADB_ENV") + var cfg = defaultConfig() + cfg.authEnabled = true + cfg.jwtSecret = "" + var msg = "" + try: + validateProductionConfig(cfg) + except ValueError as e: + msg = e.msg + check "JWT" in msg or "secret" in msg.toLower() + + test "validateProductionConfig accepts strong secret": + putEnv("BARADB_ENV", "production") + defer: delEnv("BARADB_ENV") + var cfg = defaultConfig() + cfg.authEnabled = true + cfg.jwtSecret = "a".repeat(32) + validateProductionConfig(cfg) # must not raise + suite "Raft peer address parsing": test "BARADB_RAFT_CLIENT_PEERS populates raftPeerClientAddrs":