Saltearse al contenido

Servidor HTTP

El binario namidb-server abre un namespace de NamiDB y lo expone sobre una API REST pequeña. Es el mismo motor que la librería embedded; todo lo que agrega este binario es el límite HTTP, autenticación por bearer token, un loop de flush periódico, y un listener Bolt opcional.

Consulta Instalación para el comando de instalación (cargo install --path crates/namidb-server) o la imagen Docker.

Ejecutar

Ventana de terminal
namidb-server \
--store "s3://my-bucket/data?ns=prod&region=us-east-1" \
--listen 0.0.0.0:8080 \
--auth-token "$NAMIDB_AUTH_TOKEN" \
--flush-interval 30s

Cada flag también se puede setear vía env vars (NAMIDB_STORE, NAMIDB_LISTEN, NAMIDB_AUTH_TOKEN, NAMIDB_FLUSH_INTERVAL, NAMIDB_BOLT_LISTEN). La URI de --store sigue la misma gramática que el cliente Python y la CLI — consulta Backends de almacenamiento.

Si no pasas --auth-token, el servidor arranca en modo sin autenticación e imprime un warning ruidoso. No expongas ese puerto al internet público.

Endpoints (v0)

MétodoPathAuthDescripción
GET/v0/healthpúblicoLiveness + versión del manifest + epoch
GET/v0/versionpúblicoVersión del build del servidor
POST/v0/cypherbearerEjecuta una consulta Cypher (lectura o escritura)
POST/v0/admin/flushbearerFuerza un flush del memtable a SSTs L0

POST /v0/cypher

Request:

{
"query": "MATCH (p:Person) WHERE p.age >= $min RETURN p.name AS name",
"params": {"min": 18}
}

Response (lectura):

{
"columns": ["name"],
"rows": [{"name": "Alice"}, {"name": "Bob"}]
}

Response (escritura):

{
"columns": ["a"],
"rows": [{"a": {"_kind": "node", "id": "...", "label": "Person", "properties": {}}}],
"write_outcome": {
"nodes_created": 1,
"edges_created": 0,
"nodes_deleted": 0,
"edges_deleted": 0,
"properties_set": 0
}
}

Round-trip end-to-end con curl

Ventana de terminal
TOKEN=$(openssl rand -hex 32)
namidb-server --store memory://demo --listen 127.0.0.1:8080 --auth-token "$TOKEN" &
curl -s http://127.0.0.1:8080/v0/health | jq .
curl -s -X POST http://127.0.0.1:8080/v0/cypher \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"query": "CREATE (a:Person {name: \"Alice\", age: 30}) RETURN a.name AS name"}' \
| jq .
curl -s -X POST http://127.0.0.1:8080/v0/cypher \
-H "Authorization: Bearer $TOKEN" \
-H 'Content-Type: application/json' \
-d '{"query": "MATCH (p:Person) RETURN p.name AS name, p.age AS age"}' \
| jq .

Concurrencia

namidb-server mantiene un WriterSession por proceso. Las escrituras se serializan detrás del invariante single-writer-per-namespace (RFC-001): a lo sumo un statement de escritura está en vuelo contra el namespace a la vez. Las lecturas ya no toman el mutex del writer — una snapshot cell (RFC-021) publica la última vista persistente, varios lectores la comparten vía un Arc, y una escritura refresca la cell después de cada commit_batch / flush. El test de integración (crates/namidb-server/tests/concurrent_reads.rs) mide un fan-out de ~7x con 8 lectores en una máquina de 4 cores.

Si hoy necesitas escala horizontal, apunta varios procesos namidb-server a la misma URI de --store. Cada uno sirve lecturas desde la misma versión del manifest, y solo a uno se le permite hacer commit de escrituras (los demás quedan fenced vía epoch CAS).

Flush periódico

--flush-interval (default 30s) controla cada cuánto la tarea en background convierte el memtable en SSTs L0. Ponlo en 0s para desactivar el loop y llamar POST /v0/admin/flush desde cron o un sidecar.

Protocolo Bolt

Pasa --bolt-listen 0.0.0.0:7687 (o NAMIDB_BOLT_LISTEN) para exponer un listener Bolt 4.4 / 5.0 / 5.4 al lado de la API HTTP. Ambos protocolos comparten el mismo WriterSession, el mismo auth token, y el mismo invariante single-writer-per-namespace.

Ventana de terminal
namidb-server \
--store memory://demo \
--listen 0.0.0.0:8080 \
--bolt-listen 0.0.0.0:7687 \
--auth-token "$NAMIDB_AUTH_TOKEN"
from neo4j import GraphDatabase
driver = GraphDatabase.driver("bolt://localhost:7687",
auth=("namidb", "$NAMIDB_AUTH_TOKEN"))
with driver.session() as s:
s.run("CREATE (:Person {name: 'Alice'})")
for r in s.run("MATCH (p:Person) RETURN p.name AS name"):
print(r["name"])

El usuario de Bolt es el string literal namidb; la contraseña es el mismo bearer token. Consulta Bolt (drivers Neo4j) para la matriz de drivers y RFC-022 para el diseño wire-level.

En el roadmap

El README del servidor lista estos endpoints como planeados pero aún sin aterrizar:

  • /v0/cypher/stream — streaming NDJSON para result sets grandes.
  • /v0/cypher/arrow — body Arrow IPC para ingesta zero-copy en DataFrames.
  • /v0/metrics — exposición Prometheus (counters, histograma de latencia, tasas de hit del caché).

Siguientes pasos

  • API HTTP — referencia del mapeo de tipos y detalles del envelope JSON.
  • Bolt (drivers Neo4j) — matriz de compatibilidad de drivers y snippets.
  • Docker + MinIO — un stack local completo en un solo docker-compose.yml.
  • Configuración — cada env var que el servidor lee al arrancar.