kdb as unified data plane
Abstract
kdb-next (TiKV-backed, RFC-001 of kdb-next) evolui de "replacement relacional do kdb-Go atual" para substrate primário multi-model unificado da Koder Stack. Layers especializadas (relational, time-series, KV, vector, search, stream) compartilham o mesmo substrate distribuído (TiKV + MVCC + Raft + Percolator) e ganham ACID cross-model nativo. Componentes Koder param de manter Redis/ Timescale/Elasticsearch separados; passam a falar com kdb-next através de drivers SQL ou protocol-specific shims.
Três stores ficam legitimamente fora do kdb-next:
kdrive(object storage, S3-compat): blobs > 1 MB, pet-scale- Redis local em hot paths que provadamente exigem < 1ms p99
- CRDT layer (Yjs/Automerge) pra collaborative state com
consistência eventual
Pra tudo o resto (sessões, rate-limit counters, métricas, search index, audit logs, telemetry, vector embeddings, stream events), kdb-next é o destino.
Motivation
O problema atual
A Koder Stack hoje (2026-05-09) tem ~7 storage tiers diferentes em produção:
- Postgres (kdb 18.3) em LXC
kdb— relational principal - TimescaleDB em LXC
kbull— time-series - Redis em alguns LXCs (sessões kbot, hot caches)
- MinIO em LXC
kdrive— S3-compat blobs - kdb-next em desenvolvimento (LXCs futuros) — relational
hyperscale
- Filesystem em vários componentes — flat-file state
- In-memory maps em alguns serviços — não-persistent state
Cada tier tem:
- Backup separado, com falhas correlacionadas em incidentes
- Auth model próprio (Postgres roles, Redis ACLs, MinIO IAM)
- Monitoring/alerting separado
- Failure modes incompatíveis (Postgres ACID vs Redis eventually-
consistent vs filesystem unreliable)
- Migration path único por tier (mexer em schema → 7× operação)
Cross-tier transactions são impossíveis — uma operação que escreve credentials no kdb e invalida cache no Redis pode falhar no meio: cache stale + DB consistent. Cada componente reinventa saga / outbox pattern.
A vision
kdb-next (TiKV) substrate unificado, com layers específicas servindo cada padrão de acesso. Mesmo MVCC + Raft, mesmo backup, mesmo auth model, mesmo monitoring, transactions cross-model ACID nativo.
O pattern é TiDB ecosystem aplicado à Koder Stack:
TiDB ecosystem: Koder Stack equivalente:
TiKV ─── KV kdb-kv (KV nativo, expor TiKV)
TiDB ─── SQL kdb-next (relational SQL)
TiFlash─── OLAP kdb-flash (column-store, futuro)
TiCDC ─── change kdb-cdc (Raft log → consumers)Plus layers específicas Koder:
kdb-ts time-series (hypertable-style sobre TiKV ranges)
kdb-vec vector (ANN sobre TiKV-stored embeddings)
kdb-search full-text (Lucene-style index, derivado via CDC)
kdb-stream pub/sub (Raft log → fan-out subscribers)Result: um substrate, sete interfaces, ACID-cross-everything.
Exceção de substrato —
kdb-obj(proposto,stack-RFC-006): o object/blob plane (§Non-goals 1) é a única extensão da família kdb que não roda sobre o substrato transacional TiKV. Multi-model ≠ multi-substrate: as sete interfaces acima são projeções sobre um substrato;kdb-objcompartilha marcaAPIauthCLInamespace com o resto do kdb, mas é internamente um substrato object-native (erasure coding, sem consenso nos bytes imutáveis, streamingrange IO, GC ref-counted). É o substituto Koder-native do MinIO/kdrive (par self-hosted emregistries/self-hosted-pairs.md).
Goals
- Substrate único pra toda persistent data em apps Koder (com 3
exceções listadas em §Non-goals)
- Multi-model (relational, KV, time-series, vector, search, stream)
sobre o mesmo TiKV
- ACID cross-model nativo (escrever em time-series + atualizar
contador KV + emitir evento no stream = uma transação)
- Multi-tenant-by-default (alinhado com `policies/multi-tenant-
by-default.kmd`)
- Multi-cluster + multi-region + active-active (alinhado com
kdb-RFC-008+kdb-RFC-009) - HA (Raft replication, auto-failover)
- Read replicas (alinhado com
kdb-RFC-011) - Hyperscale (100M+ tenants target, alinhado com
kdb-RFC-001) - Operações simplificadas: 1 backup process, 1 monitoring stack,
1 auth model
Non-goals (stay outside kdb-next)
Estas três categorias não convergem pro substrate, por design:
1. Object/blob storage
Bytes grandes (release artifacts > 1 MB, attachments, build outputs, user uploads, container images, video frames, audio waveforms) ficam em kdrive (MinIO ou sucessor S3-compat).
Razão técnica: TiKV não é otimizado pra blobs. Replicação Raft de um blob 100MB satura I/O sem ganho — Raft consensus pra dado que não muda é overkill. GC de blobs MVCC é complexo. Object store dedicado é pattern correto.
Coordenação: kdb-next armazena metadata + ACL + signed-URL config; kdrive serve bytes. É o pattern S3 + Postgres bem estabelecido.
Direção (proposto, stack-RFC-006): esta exceção evolui de "MinIO/S3-compat pra sempre" para um object store Koder-native sob a marca kdb — kdb-obj, uma layer da família kdb sobre um substrato object-native (não o core transacional kdb-next). Object storage como model da família é trivial na API (PUT/GET), mas exige um substrato próprio — não roteie bytes pelo WALMVCCRaft do kdb-next. Enquanto kdb-obj não shipa, MinIO/kdrive permanece o backend; kdb-obj é adição de marcaAPIsubstrato, não reescrita do core. O par self-hosted MinIO ↔ kdb-obj é rastreado em registries/self-hosted-pairs.md; consumidores aguardando o slot de object-store: Hub depot (Storage iface), kdrive, e os git objects do Flow (FLOW-147).
2. Edge cache (sub-ms hot paths)
Onde p99 < 1ms é requisito real e não negociável:
- Auth token → user_id resolution (toda request passa por aí)
- Rate-limit counters (incrementar em < 100µs)
- Presence indicators (collaborative cursors, online users)
Estes ficam em Redis local in-process do componente, com keys prefixadas por tenant (tenant:<uid>:<key>).
kdb-next é source of truth; Redis é read-through cache + write queue, com TTL agressivo (segundos) e invalidação pelo CDC quando kdb-next muda.
Critério: hot path deve ser provadamente > 100× mais frequente que cold path e atender p99 < 5ms para justificar Redis. Caso contrário, kdb-next direto basta.
3. CRDT collaborative state
Live presence, OT cursors, real-time editing — modelo de consistência é eventual + merge functions. Forçar transações ACID adiciona latência demais e quebra o pattern.
Solução: Yjs/Automerge layer (in-process per-component, sincroniza P2P entre clients via WebSocket). Snapshot persistido em kdb-next periodicamente (cada N segundos ou no save explícito do user).
High-level architecture
┌──────────────────────────────────────────────────────────────────────┐
│ Application APIs │
│ (kode, hub, flow, kortex, gateway, jet, id, kbot, talk, drive, …) │
└────────────────────────────┬─────────────────────────────────────────┘
│
┌────────────────┼────────────────┐
▼ ▼ ▼
SQL driver KV driver TS driver
(relational (cache, hot (metrics,
+ transac- counters) audit, logs)
tions)
│ │ │
└────────────────┼────────────────┘
▼
┌────────────────────────────────────┐
│ kdb-next adapter layer │
│ - SQL parser/planner │
│ - KV expose │
│ - TS hypertable layer │
│ - Vec ANN layer │
│ - Search index (derived via CDC) │
│ - Stream (Raft log → fan-out) │
└────────────────┬───────────────────┘
▼
┌────────────────────────────────────┐
│ TiKV substrate │
│ - MVCC │
│ - Raft replication (3+ nodes) │
│ - Percolator distributed txn │
│ - PD (placement driver, multi- │
│ region geo-aware sharding) │
└────────────────────────────────────┘
│
▼
┌─────────────────┐ ┌─────────────────┐
│ kdrive │ │ Redis local │
│ (S3-compat blob)│ │ (hot cache, │
│ │ │ source-of- │
│ kdb-next holds │ │ truth in │
│ metadata + ACL │ │ kdb-next) │
└─────────────────┘ └─────────────────┘Layers detalhadas
Layer 1 — kdb-next SQL (relational)
Status: em desenvolvimento (RFC-001 do kdb-next; shadow-active).
Surface: SQL completo, drivers Postgres-compatible (apps falam SQL via lib pgxpqetc. sem mudanças).
Use cases:
- User accounts (Koder ID)
- Repositories (Flow)
- Workspaces, memberships
- Credentials (FLOW-003 hierarchy)
- Permissions, ACLs
- Tudo que tem queries com JOIN, GROUP BY, transações
ACID: full MVCC, distributed transactions via Percolator.
Layer 2 — kdb-kv (KV nativo)
Status: planejado (este RFC).
Surface: API estilo Redis (GET/SET/DEL/EXPIRE/HGET/SADD) ou native TiKV client.
Use cases (que hoje usam Redis):
- Sessões SSO
- Rate-limit counters
- Background job queues (com transações pra garantir delivery)
- Distributed locks (TiKV native suporta isso)
- Session-presence (tracked com TTL)
Diferença vs Redis local: persistência + cross-region replication + ACID com outras layers. Latência: ~1-2ms vs ~50µs do Redis local.
Quando usar kdb-kv vs Redis local: ver §Non-goals item 2.
Layer 3 — kdb-ts (time-series)
Status: planejado (parcial em kdb-RFC-007).
Surface: hypertable-style API, similar ao TimescaleDB. Particionamento automático por tempo + tenant.
Use cases:
- Token usage tracking (rate_limit observations) — *aso direto que
motiva esta RFC*
- Métricas Prometheus (component health, request rates)
- Audit logs com retention
- Telemetry de apps clients (kode/app, kbot)
- Activity feeds (Koder ID profile activity)
Features:
- Continuous aggregates (rollups)
- Compression de chunks > 7 dias (factor ~10×)
- Retention policies per-tenant
- TimescaleDB-compat query syntax (gradual migration de kbull)
Layer 4 — kdb-vec (vector / ANN)
Status: planejado (futuro, sem RFC ainda).
Surface: API similar a pgvector ou Milvus.
Use cases:
- Embeddings de docs pra search semantic (Krep, Kortex)
- Embeddings de prompts/responses pra Claude history search
- Recommendations (Hub package similarity)
Trade-off: layer dedicada (Milvus) é mais performática em escala massive (>100M vectors); kdb-vec basta pra <10M. Reavalia quando hit.
Layer 5 — kdb-search (full-text + structured)
Status: planejado (depois de kdb-vec).
Surface: query API similar a Elasticsearch.
Implementação: index Lucene-style derivado de tabelas relational via CDC. Quando uma row no Flow muda, índice atualiza. Latência de indexação: ~ms (single-region) ou ~100ms (cross-region).
Use cases:
- Full-text search no Hub (apps, skills)
- Code search complementar ao
koder-krep(que cobre on-disk grep) - Search em issues, PRs, comments do Flow
Layer 6 — kdb-stream (pub/sub + change capture)
Status: planejado.
Surface:
- Producer: TiKV Raft log → topic (toda mutation cabe em event)
- Consumer: subscribe a topic via gRPC streaming
Use cases:
- CDC pra fora (cliente integrações, ETL pra warehouse)
- Webhooks (Flow webhooks ficam aqui em vez de table polling)
- Notification fan-out (Hub release → notify followers)
- Build trigger queues (commit → CI runner)
Substitui Kafka pra cargas internas. Externos (clientes B2B com Kafka existente) podem consumir via mirror.
Multi-tenancy alignment
Esta RFC e policies/multi-tenant-by-default.kmd são complementares:
- A policy diz "todo dado tem
koder_user_id/workspace_id" - Esta RFC diz "e o substrate único onde cada layer implementa
isolamento na sua linguagem nativa é kdb-next/TiKV"
Cada layer implementa isolation conforme spec specs/multi-tenancy/contract.kmd:
| Layer | Isolation |
|---|---|
| kdb-next SQL | Postgres-style RLS via current_setting('koder.uid') |
| kdb-kv | Key prefix obrigatório (tenant:<uid>:) |
| kdb-ts | Hypertable index (koder_user_id, time) + RLS |
| kdb-vec | Filter por metadata {tenant: <uid>} em ANN |
| kdb-search | Index per-tenant ou global com filter mandatory |
| kdb-stream | Topic naming tenant.<uid>.<resource> |
Cross-tenant queries: error by default. Admin path explícito com audit.
Multi-region
Alinhado com kdb-RFC-008 (multi-region replication) e kdb-RFC-009 (active-active).
Topology esperada (long-term):
- 3+ regiões geográficas (ex: SP, EU, US)
- Cada tenant tem home region (storage primary)
- Replicação assíncrona pra outras regiões (read-only cross-region)
- Active-active permite escrever em qualquer região; conflitos
resolvidos via Lamport timestamps + LWW per-key (ou CRDT-like merges).
PD (Placement Driver) gerencia placement consciente de tenant home region.
HA
Alinhado com kdb-RFC-001 + kdb-RFC-011:
- 3+ Raft replicas por shard (default 3, configurável 5 pra crítico)
- Auto-failover < 5s
- Read replicas standby (kdb-RFC-011) pra cargas pesadas de read
- Backup contínuo via incremental WAL streaming pra object storage
Roadmap fasings
Fase atual = 0 (consolidação). kdb-next em shadow-active, RFC-001 do kdb-next + RFC-007 (timeseries) + RFC-008 (multi-region) + RFC-009 (active-active) + RFC-011 (read replicas) já desenhados. Layers KV, vector, search, stream ainda em conceito.
Fase 0 — hoje (consolidação relational)
- kdb-next em shadow-active no monorepo
- Postgres
kdbcorrente serve produção - 5 RFCs do kdb-next ratificadas (architecture, timeseries,
multi-region, active-active, read-replicas)
Fase 1 — próximos 6 meses (kdb-next primary relational)
- Flip kdb-next pra primary em todos componentes que usam Postgres
- Foundation/id já tá nele; flow, hub, kortex seguem
- Aposentar kdb (Postgres) — preservado pra recovery, não rota ativa
Fase 2 — kdb-ts (time-series unified)
services/ai/gatewaytoken-usage tracker (proposta inicial desteRFC) é o caso piloto
- Migrar kbull (TimescaleDB) pra kdb-ts
- Métricas Prometheus passam por kdb-ts
- Activity feeds, audit logs
Fase 3 — kdb-kv + Redis aposentadoria
- Sessions, rate-limit counters, queues migram pra kdb-kv
- Redis fica só onde provadamente justificado por p99 < 1ms
(auth token resolution, hot rate-limit) — com kdb-next como source- of-truth
Fase 4 — kdb-search + kdb-stream + kdb-vec
- Search Hub passa pra kdb-search
- Webhook delivery passa pra kdb-stream
- Vector search semantic passa pra kdb-vec (ou Milvus se escala
exige)
Fase 5 — full convergência
- Único storage stack: kdb-next + kdrive + Redis-em-edge-cases-
específicos
- 1 backup process, 1 monitoring stack, 1 auth model
- Cross-model transactions usadas livremente
Tempo estimado realista: 3-5 anos com team focado. Comparáveis:
- TiDB ecosystem maturou em ~7 anos (2015 → 2022)
- Spanner ~5 anos antes do paper (2007 → 2012)
- FoundationDB ainda evolui após 2015
Promotion gates (per-layer)
Cada layer (kdb-kv, kdb-ts, etc.) só vira default na Stack quando passar pelos 5 gates de policies/self-hosted-first.kmd:
- Feature parity: cobre todos use cases do incumbent
- Performance: bench a la
koder-benchmostrakdb-<layer>≥incumbent em workload representativo
- Stability: 30 dias em prd shadow-active sem regressão
- Capability: features novas que o incumbent não tem (ACID
cross-model, multi-region native)
- Critical-path readiness: smoke tests em todos consumers
passam
Gate fails → layer fica shadow, incumbent permanece. Não force flip.
Costs / risks
Risk 1 — engenharia massiva
Implementar 6 layers sobre TiKV é trabalho de anos. Time pequeno.
Mitigação:
- Adoção incremental (RFC-001 do kdb-next já é essa direção)
- Reuso de tech upstream (TiKV é battle-tested)
- Algumas layers (kdb-vec, kdb-stream) podem ser deferred 2-3 anos
- Spec aceita usar terceiros temporariamente quando layer ainda
não existe (Milvus pra vector enquanto kdb-vec não rola)
Risk 2 — vendor lock-in técnico
Stack Koder fica fortemente acoplada a TiKV. Se TiKV (PingCAP) sair do business, Koder herda manutenção do upstream.
Mitigação:
- TiKV é Apache 2.0 license (não vendor-lock estrito)
- TiKV é CNCF Graduated (community-managed)
- Apesar disso, alternativas pro substrate (FoundationDB, etc.)
ficam documented em
policies/self-hosted-first.kmdcomo fallback option.
Risk 3 — performance ceiling local
Substrate distribuído > engine local em algumas cargas (Redis in-process). Forçar tudo em kdb-next degrada hot paths.
Mitigação:
- §Non-goals lista as 3 categorias que ficam fora
- Promotion gate #2 exige bench parity antes de flip
Risk 4 — operational complexity migration
Cada migration de layer requer dados-shadow + cutover + rollback plan. 6 migrations × 50 componentes = matrix big.
Mitigação:
- RFC-001 do kdb-next já provou o padrão (shadow + diff + flip)
- Migration tooling reusable (incus snapshots, mig-runners) já
existe na stack
- Faseamento conservador (1 layer por trimestre)
Open questions
kdb-vecvs Milvus dedicated: ANN em scale > 100M vectorsé genuinamente domínio especializado. Vale construir kdb-vec ou preservar Milvus como exception permanente igual kdrive?
- Active-active conflict resolution: LWW (last-write-wins)
per-key é suficiente, ou precisamos CRDTs serializáveis pra alguns dados (ex: contadores)?
- Consumer migration tax: cada componente flip de Postgres →
kdb-next, Redis → kdb-kv, Timescale → kdb-ts custa horas de refactor. Faseamento detalhado per-componente exige sub-RFCs.
- Backup story: cross-model snapshot consistente é trivial
no TiKV (single MVCC timestamp). Mas restore pra um ponto-no- tempo cross-cluster precisa coordination — RFC dedicada provavelmente.
- Authn/authz unified: hoje cada storage tier tem seu auth.
Kdb-next deveria delegar pra Koder ID nativamente? Quanto acoplamento?
Decision
Status: draft até owner ratificar. Após ratificação:
- Vira referência canonical pra qualquer decisão de storage na Stack
- IAs e engs novos consultam-no antes de propor "vamos botar Redis
pra isso"
- Sub-RFCs detalham cada layer (kdb-kv, kdb-ts, kdb-vec, kdb-search,
kdb-stream) — abertas conforme prioridade
- Roadmap entra em
meta/docs/stack/registries/storage-convergence.mdpra tracking de migration debt per-componente
Related work
infra/data/kdb/docs/rfcs/kdb-RFC-001— substrate architectureinfra/data/kdb/docs/rfcs/kdb-RFC-007— time-series storageinfra/data/kdb/docs/rfcs/kdb-RFC-008— multi-regioninfra/data/kdb/docs/rfcs/kdb-RFC-009— active-activeinfra/data/kdb/docs/rfcs/kdb-RFC-011— read replicaspolicies/multi-tenant-by-default.kmd— tenant isolationpolicies/hyperscale-first.kmd— implementation principlepolicies/self-hosted-first.kmd— promotion gate framework
Significance
Sem RFC, cada novo módulo da Stack escolhe storage isoladamente, acumulando débito técnico de divergência. Em 5 anos seriam 20+ storage tiers diferentes, multiplicando custo operacional e inviabilizando ACID cross-module.
Com RFC, a direção está pinada e qualquer escolha contrária precisa de justificativa explícita (e exception path em §Non-goals).
Esta é a equivalente da "always RDBMS" decision tomada por gigantes nos anos 2000 — exceto que hoje a apostamos é "always TiKV substrate" e ganhamos hyperscale + multi-model nativo.