¿Por Qué Netflix Construyó Su Propia Abstracción de Grafos?
Netflix tiene un montón de casos de uso de grafos — desde grafos sociales en gaming hasta análisis de topología de servicios en tiempo real. Se dividen en dos categorías: OLAP (analítico, consultas abiertas) y OLTP (alto rendimiento, baja latencia). El OLTP exige millones de operaciones por segundo con respuestas en milisegundos — algo que las bases de datos de grafos listas para usar no pueden entregar sin hacer concesiones pesadas.
Este post es el primero de una serie que detalla la arquitectura de la Graph Abstraction personalizada de Netflix, que actualmente maneja ~10 millones de ops/seg en 650 TB de datos de grafos con latencias p99 de un solo dígito.
Fuente: Netflix Tech Blog – High-Throughput Graph Abstraction at Netflix: Part I

Arquitectura: Construyendo Sobre Abstracciones Existentes
En lugar de reinventar la capa de almacenamiento, Netflix construyó la Graph Abstraction sobre su Key-Value (KV) Abstraction para índices en tiempo real y TimeSeries (TS) Abstraction para vistas históricas. EVCache proporciona caché de baja latencia, y el Data Gateway Control Plane gestiona esquemas y aprovisionamiento.
Modelo Property Graph con Esquemas Fuertemente Tipados
Los nodos y aristas se almacenan como property graphs con esquemas explícitos definidos en el control plane. Ejemplo de configuración de mapeo de aristas:
{
"edgeConfig": {
"edgeMappings": [
{
"edgeMappingKey": {
"fromNodeType": "account",
"edgeType": "owns",
"toNodeType": "profile"
},
"directionType": "UNIDIRECTIONAL"
},
{
"edgeMappingKey": {
"fromNodeType": "profile",
"edgeType": "linked_to",
"toNodeType": "device"
},
"directionType": "BIDIRECTIONAL"
}
]
}
}
Los esquemas de propiedad extienden los mapeos con nombres y tipos permitidos:
{
"edgeMappingKey": {
"fromNodeType": "profile",
"edgeType": "linked_to",
"toNodeType": "device"
},
"propertySchema": {
"propertyMappings": [
{ "propertyKey": "registration_time", "propertyValueType": "TIMESTAMP" },
{ "propertyKey": "status", "propertyValueType": "STRING" }
]
}
}
El esquema permite validación de calidad de datos, planificación de consultas, deduplicación de aristas recorridas y eliminación de caminos imposibles.
Índice en Tiempo Real: Almacenamiento Key-Value
Los nodos se almacenan por tipo en namespaces KV dedicados, permitiendo búsquedas eficientes en una sola partición. Las aristas usan dos índices separados:
- Índice de links: lista de adyacencia que mapea nodos fuente a vecinos.
- Índice de propiedades: almacena propiedades de las aristas por separado.
Esta separación permite upserts eficientes de propiedades sin filas anchas, a costa de escrituras no atómicas entre namespaces.
Estrategias de Caché
- Caché write-aside de links de aristas reduce amplificación de escritura.
- Caché read-aside de propiedades (vía EVCache) reduce amplificación de lectura.
- Caché write-through (en desarrollo) organizará índices por diferentes órdenes de clasificación.
Garantía de Consistencia
La replicación multi-región es asíncrona, resultando en consistencia eventual. Un mecanismo de reparo de entropía usa Kafka para reintentar escritas fallidas entre índices. Las eliminaciones de nodos son asíncronas con resolución de conflictos LWW.
API de Traversal
La API gRPC personalizada (inspirada en Gremlin) soporta traversals encadenados, filtros, ordenamiento y limitación. Ejemplo de consulta hipotética para recomendar series en un dispositivo compartido:
TraversalRequest.newBuilder()
.setNamespace("")
.setTraversalQuery(
TraversalQuery.newBuilder()
.setStartNode(node("device", "my-device-id"))
.setTraversal(
Traversal.newBuilder()
.setEdgeLimit(5)
.setDirectionTraversal(
DirectionTraversal.newBuilder()
.setDirection(IN)
.addNodePropertiesSelections(propSelection("account", "created_at"))
.addNodePropertiesSelections(propSelection("profile", "last_active"))
.setDirectionFilter(
DirectionFilter.newBuilder()
.setTypeMatchingStrategy(EXCLUDE_NON_TARGETED)
.addAllNodeFilters(typeFilters("account", "profile"))))
.addNextTraversals(
Traversal.newBuilder()
.setOrder(LATEST)
.setEdgeLimit(200)
.setDirectionTraversal(
DirectionTraversal.newBuilder()
.setDirection(OUT)
.addEdgePropertiesSelections(propSelection("watched", "view_time"))
.addEdgePropertiesSelections(propSelection("has_plan", "active"))
.setDirectionFilter(
DirectionFilter.newBuilder()
.setTypeMatchingStrategy(EXCLUDE_NON_TARGETED)
.addAllNodeFilters(typeFilters("title", "plan"))))))
.build();

Rendimiento y Métricas Reales
- Rendimiento: hasta 10 millones de ops/seg en pico.
- Latencia: un solo dígito para lecturas puntuales y traversals de 1 salto (p99).
- Traversals de 2 saltos (usados por RDG): p90 por debajo de 50ms.
- Volumen de datos: ~650 TB globalmente.
Limitaciones y Precauciones
- Consistencia eventual es un intercambio deliberado por alta disponibilidad. Las aplicaciones deben tolerar lecturas desactualizadas de corta duración.
- Escrituras no atómicas entre múltiples namespaces requieren lógica de reintento cuidadosa.
- Profundidad de traversal limitada para mantener latencia predecible; traversals profundos (3+ saltos) pueden requerir estrategias diferentes.
Próximos Pasos
La Parte II de esta serie se sumergirá en la planificación y ejecución de traversals, además de mecanismos de conteo. La Parte III cubrirá el índice temporal y la integración con Time Series. Si estás construyendo un sistema de grafos de alto rendimiento, empieza con una separación clara entre almacenamiento de links y propiedades, e invierte en un planificador de consultas orientado a esquemas.
Lectura Relacionada
- Maximizing GPU Utilization for LLM Inference: A Deep Dive into NVIDIA Runai & NIM
- How the Python Official Blog Moved from Blogger to Git: A Case Study in Open Source Infrastructure

Conclusión
La Graph Abstraction de Netflix es una clase magistral de cómo construir una base de datos de grafos OLTP a medida sobre infraestructura existente. Separando índices de links y propiedades, usando optimizaciones orientadas a esquemas y empleando caché en capas, logran un rendimiento y latencia impresionantes a escala. Los intercambios — consistencia eventual, escrituras no atómicas, profundidad de traversal limitada — están claramente documentados y son aceptables para sus casos de uso.
Si estás arquitectando un sistema de grafos para cargas de trabajo OLTP de alto rendimiento, inspírate en el enfoque de Netflix: no construyas todo desde cero; compón abstracciones existentes sabiamente.