DocumentService
Collections documentaires : écriture, lecture, recherche exacte, requête multi-filtres et énumération.
- PutDoc
- GetDoc
- DeleteDoc
- FindByField
- ListDoc
- QueryDoc
- ListCollections


RociaDB permet de structurer des données métier en documents, de relier des entités via un graphe, puis d’associer des fichiers au même contexte. Cette page pose une première base de documentation publique : concepts, intégration, clients, sécurité et bonnes pratiques d’usage.
Un schéma de lecture rapide pour comprendre comment une application SaaS dialogue avec la plateforme et organise ses données.
Collections documentaires : écriture, lecture, recherche exacte, requête multi-filtres et énumération.
Nœuds, arêtes et traversées paginées, avec des IDs fournis par le client.
Fichiers chunkés, métadonnées et visibilité atomique via commit marker.
Le seul service qui ne soit pas scopé à un tenant : ListTenants énumère l’ensemble du déploiement. Il est séparé des trois services de modèle pour pouvoir porter sa propre politique d’autorisation sans toucher au chemin des données.
Quatre étapes pour passer d’un compte vide à un premier document écrit, relu, puis à un parcours d’exploration complet.
Un échange OAuth2 client_credentials contre le fournisseur d’identité. Le jeton vaut 600 secondes.
curl -X POST https://<votre-idp>/token \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "grant_type=client_credentials" \
-d "client_id=<client_id>" \
-d "client_secret=<client_secret>"
# { "access_token": "...", "token_type": "Bearer", "expires_in": 600 }Le jeton voyage en métadonnée gRPC. Le champ json est du JSON UTF-8 sérialisé, donc encodé en base64 par la transcription JSON de grpcurl.
# {"status":"active"} encodé en base64
grpcurl -H "authorization: Bearer $TOKEN" \
-proto rocia/v1/rocia.proto \
-d '{
"tenant_id": "tenant-a",
"collection": "users",
"id": "user-123",
"json": "eyJzdGF0dXMiOiJhY3RpdmUifQ=="
}' \
<hôte>:<port> rocia.v1.DocumentService/PutDocGetDoc rend le même JSON sérialisé. Un UNAUTHENTICATED ici signifie simplement que le jeton a expiré.
grpcurl -H "authorization: Bearer $TOKEN" \
-proto rocia/v1/rocia.proto \
-d '{
"tenant_id": "tenant-a",
"collection": "users",
"id": "user-123"
}' \
<hôte>:<port> rocia.v1.DocumentService/GetDocLes RPC d’énumération suffisent à parcourir un tenant de bout en bout. Un client en scope read peut faire tout ce parcours.
# Documents
ListCollections → ListDoc → GetDoc
# Graphe
ListGraphs → ListNodes → GetNode → NeighborsOut
# Fichiers
ListBuckets → ListFiles → Stat → DownloadLa plateforme sépare volontairement document, graphe et fichier. Leur combinaison est explicite et portée par le client.
Quand un objet doit aussi être traversé dans le graphe, le client crée également un nœud correspondant.
Il n’y a pas de suppression de nœud exposée dans le proto v1. Le nettoyage des arêtes reste explicite côté client.
Un fichier n’est visible qu’après écriture du commit final. Les uploads partiels orphelins sont éligibles au GC.
bytesjson et values_json ne sont pas des chaînes : ce sont des bytes portant du JSON UTF-8 sérialisé. Le client encode avant l’envoi et décode à la réception.
Quatre services : trois orientés modèle — document, graphe, fichier — et un service transverse aux tenants. Tous partagent les mêmes conventions de pagination, d’idempotence et d’erreur.
Écriture, lecture, suppression, recherche et énumération des collections, avec pagination et total_count.
PutDoc · GetDoc · DeleteDoc · FindByField · ListDoc · QueryDoc · ListCollectionsNœuds et arêtes, traversée paginée dans les deux sens, énumération des graphes et de leurs nœuds.
PutNode · GetNode · AddEdge · DeleteEdge · NeighborsOut · NeighborsIn · ListGraphs · ListNodesUpload et téléchargement streamés, métadonnées, suppression, énumération des buckets et des fichiers.
Upload · Download · Stat · Delete · ListBuckets · ListFilesPageRequest(limit, cursor) avec limit de 1 à 200, 50 par défaut. La réponse rend un next_cursor opaque.
curseur de sessionQueryDoc complète ListDoc et FindByField pour les cas où plusieurs filtres, un tri stable et une pagination sur résultat filtré sont nécessaires.
EQ, IN et CONTAINS, combinés en AND.
Tri explicite multi-champs, ASC ou DESC, avec tie-breaker stable id asc si absent.
Opaque, à rejouer tel quel — mais valable le temps d’une session de pagination, pas à conserver entre deux visites.
Le serveur renvoie total_count avant pagination.
QueryDocRequest {
tenant_id: "tenant-a",
collection: "users",
filters: [
{
field: "organization_id",
operator: QUERY_OPERATOR_EQ,
values_json: ["\"org-1\""]
},
{
field: "status",
operator: QUERY_OPERATOR_IN,
values_json: ["\"active\"", "\"draft\""]
},
{
field: "name",
operator: QUERY_OPERATOR_CONTAINS,
values_json: ["\"prod\""]
}
],
sort: [
{ field: "name", direction: SORT_DIRECTION_ASC }
],
page: { limit: 50, cursor: "" }
} Chaque statut gRPC porte une métadonnée reason qui précise la cause. C’est sur elle qu’un client branche sa logique de reprise, pas sur le seul code.
| Code gRPC | reason | Quand |
|---|---|---|
INVALID_ARGUMENT | invalid_argument | Champ manquant, limit hors bornes, curseur illisible, JSON invalide |
NOT_FOUND | not_found | Document, nœud, arête ou fichier absent |
ALREADY_EXISTS | already_exists | Conflit d’unicité |
PERMISSION_DENIED | permission_denied | Écriture demandée par un client en lecture seule |
UNAUTHENTICATED | unauthenticated | Jeton absent, expiré, mal signé ou d’un autre émetteur |
INTERNAL | internal | Défaillance du stockage |
Cette section rassemble les éléments publics utiles pour exploiter la plateforme proprement dans un contexte SaaS, sans entrer dans les détails internes d’implémentation.
Échange client_credentials contre le fournisseur d’identité, puis authorization: Bearer <jwt> en métadonnée gRPC.
Requis : iss, sub, exp. Le jeton porte aussi iat, scope et cv. Le tenant_id voyage dans la requête, pas dans le jeton.
ES256, kid obligatoire dans l’en-tête, résolu contre un JWKS mis en cache — le fournisseur d’identité reste hors du chemin de requête.
L’issuer est épinglé côté service : un jeton valide émis ailleurs est refusé.
read, read write et admin. Une écriture demandée en lecture seule répond PERMISSION_DENIED. admin ne contient pas write.
600 secondes. Renouvelez avant l’échéance, ou dès qu’un appel répond UNAUTHENTICATED.
Le tenant_id fait partie du contrat applicatif et structure l’isolation des données.
Le client_secret vit dans le backend appelant. Il ne doit jamais atteindre un navigateur ni un dépôt de code.
Regrouper les appels et paginer plutôt qu’enchaîner des requêtes unitaires évite de saturer les quotas et la latence côté client.
Sans filtre à appliquer, ListDoc coûte le prix de la page demandée et son total_count est gratuit. QueryDoc, lui, doit charger tous les candidats pour le calculer — ne bouclez donc jamais dessus pour compter.
Les sept RPC d’écriture acceptent un request_id : un rejeu portant le même identifiant et la même opération est ignoré. Les marqueurs expirent au bout de 24 h.
La rotation se fait côté fournisseur d’identité et incrémente le cv du client. Elle prend effet aussitôt sur l’administration, et sous 10 minutes sur le plan de données — le temps qu’expirent les jetons déjà émis.
Un service qui ne fait que lire reçoit read. Réserver read write aux appelants qui écrivent vraiment limite la portée d’un secret qui fuite.
Éviter d’empiler des couches de code applicatif au-dessus du client généré : cela alourdit la maintenance et masque le comportement réel des appels.
Si un besoin n’est pas couvert, ouvrir une pull request plutôt que de surcharger son propre code : les dépôts sont publics et les PR sont examinées régulièrement.
S’appuyer sur les mécanismes de sauvegarde et de restauration fournis par la plateforme plutôt que de reconstruire son propre outillage à coups d’appels API.
Les plafonds ci-dessous sont appliqués à la frontière de l’API. Pour les capacités et les tarifs par nœud, voir les tarifs publics, qui font seuls autorité sur la grille en vigueur.
Destiné à l’expérimentation, la validation d’un modèle de données ou d’une intégration. Pensé pour itérer avant de passer en exploitation, sans exigence de continuité de service.
Destiné à l’exploitation réelle, avec des exigences plus élevées de continuité et de supervision. Le multi-node y répond à des besoins de résilience et de montée en charge.
| Plafond | Valeur | Portée |
|---|---|---|
| Taille d’une page | 1 à 200 | limit hors bornes ⇒ INVALID_ARGUMENT ; 50 par défaut |
| Document JSON | 2 Mio | Par document, sur PutDoc et PutNode |
| Fichier | 5 Gio | Taille totale déclarée à l’upload |
| Chunk d’upload | 1 Mio | Taille maximale d’un message du flux Upload |
| Durée d’un jeton | 600 s | Renouvellement à la charge du client |
| Marqueur d’idempotence | 24 h | Au-delà, un rejeu de request_id est réexécuté |
Tendance illustrative du point où la latence p95 se dégrade selon le débit. Ces courbes ne sont pas des mesures : elles montrent la forme du décrochage, à affiner avec de vrais relevés.
Chaque nœud dispose de ses propres CPU, mémoire et stockage. La topologie retenue multiplie donc la capacité — et le coût.
La plupart des projets démarrent en single-node, puis évoluent vers du multi-node quand la disponibilité ou la charge deviennent critiques.
Le support standard est inclus par défaut. Le support premium accompagne les architectures multi-node lorsque la disponibilité devient critique.
Métriques, logs et alertes sont consultables directement depuis le tableau de bord de chaque environnement, sans intégration externe à configurer.
Servies au format Prometheus sur un écouteur dédié. Les quatre premières familles couvrent le trafic et les flux ; les quatre suivantes instrumentent QueryDoc, dont le coût dépend de la requête écrite par le client.
Chaque appel produit une ligne structurée, qui relie une erreur, une requête, un tenant et le client authentifié qui l’a émise.
Des règles évaluées en continu déclenchent des alertes que le tableau de bord présente comme actives tant qu’elles ne sont pas retombées, avec l’historique de leurs déclenchements.
Les alertes sont routées vers des canaux configurables, testables avant mise en service pour vérifier qu’un destinataire reçoit bien ce qu’on lui adresse.
Construire des tableaux de bord d’usage, suivre les SLO, identifier les régressions et investiguer plus vite sans exposer les détails d’implémentation aux clients finaux.
Aucun SDK n’est publié à ce jour. Le fichier .proto est la surface de référence : chaque langage y génère son client avec son outillage standard, et un SDK publié plus tard n’en changera pas le contrat. Les commandes ci-dessous supposent le fichier rangé en proto/rocia/v1/rocia.proto.
grpcio-tools
python -m grpc_tools.protoc \
-I proto \
--python_out=. \
--grpc_python_out=. \
proto/rocia/v1/rocia.protobuf + ts-proto
protoc \
-I proto \
--plugin=protoc-gen-ts_proto \
--ts_proto_out=src/generated \
--ts_proto_opt=outputServices=grpc-js \
proto/rocia/v1/rocia.prototonic-build
// build.rs
fn main() -> Result<(), Box<dyn std::error::Error>> {
tonic_build::configure()
.build_server(false)
.compile_protos(
&["proto/rocia/v1/rocia.proto"],
&["proto"],
)?;
Ok(())
}Les points qui structurent aujourd’hui l’usage réel de la plateforme.
Les stores restent séparés au niveau du proto. Si un objet doit exister dans les deux modèles, le client écrit le document puis le nœud, en partageant l’identifiant ou en portant un lien explicite dans le JSON.
Non en v1. La cohérence entre les représentations est à la charge du client ou du workflow applicatif.
EQ, IN et CONTAINS, combinés en AND, avec un tri multi-champs. Les opérateurs numériques et de plage, ainsi que le OR, restent hors périmètre à ce stade.
Le client appelle avec un scope read. Seul un scope read write autorise PutDoc, DeleteDoc, PutNode, AddEdge, DeleteEdge, Upload et Delete.
Pas encore. Le proto est la surface de référence, et chaque langage génère son client à partir de lui. Les SDK publiés viendront ensuite, sans changer cette surface.