Saltearse al contenido

Embedded (Rust)

El crate fachada namidb es la API paraguas estable. Re-exporta los tipos que vas a tocar más seguido de namidb-core, namidb-storage, namidb-graph y namidb-query, así que el código aguas abajo solo necesita una línea en Cargo.toml.

Agregar la dependencia

Cargo.toml
[dependencies]
namidb = "0.4"
tokio = { version = "1", features = ["full"] }
object_store = "0.13"
anyhow = "1"

También dependes directamente de object_store porque ese es el trait que toma la capa de almacenamiento — elegir un backend (S3, GCS, Azure, disco local, memoria) es instanciar un object_store.

Ejemplo mínimo

use std::sync::Arc;
use namidb::core::id::NamespaceId;
use namidb::query::{execute, lower, parse, Params};
use namidb::storage::{NamespacePaths, WriterSession};
use object_store::{memory::InMemory, ObjectStore};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// 1. Elige un backend. `InMemory` para el demo; en producción usas
// object_store::aws::AmazonS3, GoogleCloudStorage, etc.
let store: Arc<dyn ObjectStore> = Arc::new(InMemory::new());
// 2. Resuelve el layout en el bucket para un namespace.
let paths = NamespacePaths::new("tenants", NamespaceId::new("demo")?);
// 3. Abre un writer para el namespace (un solo writer por namespace).
let mut writer = WriterSession::open(store, paths).await?;
// ... upsert de nodos / edges, después commit_batch + flush ...
// 4. Fija un snapshot de lectura, parsea, baja y ejecuta una consulta.
let snap = writer.snapshot();
let q = parse("MATCH (a:Person) RETURN count(*) AS n")?;
let plan = lower(&q)?;
let rows = execute(&plan, &snap, &Params::new()).await?;
println!("{rows:?}");
Ok(())
}

Forma de la API

El crate paraguas namidb re-exporta cuatro namespaces. Los ítems más usados por namespace:

Re-exportLo que obtienes
namidb::coreid::NamespaceId, runtime values, tipos de schema
namidb::storageWriterSession, Snapshot, NamespacePaths, parser de URI
namidb::graphcolumnas de propiedades + adyacencia CSR (helpers de lectura)
namidb::queryparse, lower, execute, Params, LogicalPlan

La lista completa de re-exports está en crates/namidb/src/lib.rs. Los crates individuales siguen en crates.io si necesitas una superficie de dependencias más ajustada, pero enlazar solo contra namidb es la ruta soportada.

Aperturas estilo URI

Si prefieres manejar el writer desde una URI string (la misma forma que usan el cliente Python, la CLI y el servidor), usa el helper parse_uri del crate de storage en vez de construir el ObjectStore a mano:

use namidb::storage::{parse_uri, WriterSession};
let (store, paths) = parse_uri("s3://my-bucket/data?ns=prod&region=us-east-1")?;
let mut writer = WriterSession::open(store, paths).await?;

Todos los esquemas de URI documentados en Backends de almacenamiento funcionan aquí.

Camino de escritura

El WriterSession es dueño de las mutaciones en stage. Dos formas de escribir:

  • Escrituras Cypher vía execute_writeCREATE, MERGE, SET, DELETE, REMOVE. Auto-commit al final de cada statement.
  • API de staging tipadawriter.upsert_node, writer.upsert_edge, writer.tombstone_node, writer.tombstone_edge. Acumulas las mutaciones que quieras y llamas writer.commit_batch().await para hacerlas persistentes (WAL append + manifest CAS), después writer.flush().await para empujar el memtable a SSTs L0.

La superficie de la API de staging está en crates/namidb-storage/src/lib.rs.

Single-writer-per-namespace

Dos procesos pueden abrir WriterSession::open(...) contra el mismo namespace. El primero que emita un commit_batch gana; commits posteriores desde una época vieja se rechazan vía manifest CAS (If-Match en object stores, flock + rename atómico en file://). Sin servicio externo de locks.

El namespace actual queda completamente descrito por la URI del bucket más el nombre del namespace, así que los backups son aws s3 sync y un tenant es una carpeta.

Siguientes pasos