El problema fundamental que turbopuffer v3 aborda es la limitación impuesta por una arquitectura de almacenamiento centrada en un índice de vecinos más cercanos aproximados (ANN) para un sistema de búsqueda multimodal. Si bien el enfoque original de turbopuffer v1 y v2, que utilizaba un índice ANN jerárquico (SPANN, luego SPFresh) como clave primaria para el almacenamiento en object storage, demostró ser extremadamente eficiente para búsquedas vectoriales a gran escala y bajo costo, se convirtió en un cuello de botella para la evolución del sistema hacia capacidades de búsqueda de texto completo, filtrado de atributos y agregaciones.

La decisión de hacer del índice ANN la clave primaria para la organización de los datos (documentos, atributos, índices invertidos) en el almacenamiento llevó a ineficiencias significativas. Específicamente, generó amplificación de almacenamiento al duplicar datos de documentos para representaciones multi-vectoriales, y amplificación de escritura debido a la reubicación en cascada de grandes bloques de datos cada vez que el índice ANN se rebalanceaba. Además, impuso un tamaño de bloque subóptimo para la vectorización de consultas no vectoriales, limitando el aprovechamiento de las capacidades modernas de CPU (SIMD) y el rendimiento general del motor de consultas. La tesis de v3 es que desacoplar la organización física de los datos del índice ANN es esencial para construir un motor de búsqueda verdaderamente unificado y de alto rendimiento que escale eficientemente en múltiples tipos de consultas.

Arquitectura del Sistema

La arquitectura original de turbopuffer v1 y v2 se basaba en un almacenamiento clave-valor con claves ordenadas y únicas, donde la clave primaria era la 'ANN address' (ClusterId y LocalId). Los vectores se organizaban en un índice jerárquico (SPANN, luego SPFresh) que agrupaba vectores en clústeres, y los centroides de estos clústeres se agrupaban recursivamente. Cada documento, compuesto por un ID y un vector, se almacenaba directamente bajo su ANN address.

Con la evolución hacia v2, se introdujeron índices secundarios para soportar filtrado de atributos y búsqueda de texto completo (BM25). El filtrado de atributos se implementó con un índice invertido que mapeaba valores de atributos a listas de ANN addresses. Para proyecciones, los atributos del documento se almacenaban junto al vector bajo la misma ANN address. La búsqueda de texto completo utilizaba un índice FTS similar, almacenando 'postings' (listas de ANN addresses con metadatos de conteo de términos y longitud de documento) para cada término. A pesar de estas adiciones, la organización subyacente de los datos seguía siendo dictada por el índice ANN, lo que significaba que cualquier rebalanceo de SPFresh resultaba en la reubicación de los datos completos del documento y sus referencias en los índices invertidos, causando una alta amplificación de escritura. Además, el tamaño de bloque óptimo para el índice ANN (100-200 documentos por clúster) limitaba la vectorización de otras consultas, que preferirían bloques de miles de documentos para maximizar la eficiencia de la CPU. turbopuffer v3 propone una nueva fundación donde el ANN ya no es la clave primaria, sino un índice secundario más, permitiendo una organización de datos más flexible y optimizada para todos los tipos de consultas.

CapaTecnologíaJustificación
storage Object Storage Fuente de verdad para la persistencia de datos, ofreciendo economía y escalabilidad. La arquitectura original de turbopuffer v1 y v2 se construyó sobre este tipo de almacenamiento.
cache NVMe SSD/Memory Caches Proporcionar rendimiento de baja latencia para lecturas, complementando la economía del object storage.
data-processing SPANN / SPFresh Algoritmo de indexación jerárquica para Approximate Nearest Neighbor (ANN) search, utilizado como índice primario en v1 y v2. SPFresh añadió soporte para indexación incremental. vs Graph-based vector indexes
data-processing BM25 Algoritmo de scoring para búsqueda de texto completo (Full-Text Search), implementado como índice secundario en v2.

Trade-offs

Ganancias
Costes
  • ▲ Complejidad de la arquitectura de almacenamiento
  • ▲ Riesgo de regresiones en rendimiento de ANN search
K::Vector(C0L0) = vec![0.45, 0.32, ...]
K::Id(C0L0) = 7
K::Attr(C0L0, "family") = "Alcidae"
K::FTS("description", "Atlantic") -> vec![(C0L0, 2, 37), (C9L4, 1, 42), ...]
Ejemplo de cómo los datos (vectores, IDs, atributos) se almacenaban bajo claves compuestas que incluían el ClusterId y LocalId (ANN address) en turbopuffer v1/v2.

Fundamentos Teóricos

El problema de la amplificación de escritura y la gestión de índices en sistemas de almacenamiento de alto rendimiento ha sido ampliamente estudiado en la literatura académica, especialmente en el contexto de bases de datos NoSQL y sistemas de almacenamiento basados en LSM-trees (Log-Structured Merge-trees). El concepto de separar la organización física de los datos de los índices secundarios es un principio fundamental en el diseño de bases de datos para optimizar las operaciones de escritura y lectura. Por ejemplo, los LSM-trees, popularizados por sistemas como Bigtable (Chang et al., 2006) y Cassandra, abordan la amplificación de escritura mediante la acumulación de escrituras en memoria y su posterior compactación en discos, pero requieren una cuidadosa gestión de los índices secundarios para evitar penalizaciones de rendimiento.

La necesidad de diferentes tamaños de bloque para la vectorización de consultas resuena con principios de diseño de motores de ejecución de bases de datos, como los utilizados en sistemas OLAP como MonetDB (Boncz et al., 2005) o DuckDB, que emplean procesamiento vectorial para explotar la jerarquía de memoria y las instrucciones SIMD de la CPU. La optimización del tamaño de bloque para diferentes tipos de operaciones es un aspecto crítico para maximizar el throughput y minimizar la latencia, un concepto que se explora en papers sobre optimización de consultas y diseño de hardware-conscious databases. La transición de turbopuffer v3 hacia un desacoplamiento del índice ANN como primario refleja una maduración en el diseño de sistemas distribuidos, donde la flexibilidad y la optimización para cargas de trabajo heterogéneas superan la simplicidad de un único índice primario.