Skip to content
KitploitKITPLOIT
HerramientasBlog
Enviar
HerramientasBlog
Enviar

¡Herramientas de Hacking, PenTest y Ciberseguridad para tu Arsenal de Seguridad!

Kitploit es un directorio de herramientas de hacking, ciberseguridad y pentesting. Descubre las últimas actualizaciones de proyectos para encontrar vulnerabilidades, analizar sistemas, automatizar pruebas y fortalecer tu seguridad.

··Feeds·Contacto·Privacidad·© 2026 Kitploit

Directorio de Herramientas

Categorías

Ver todas las categorías
Loading categories
turbolite — SQLite VFS con consultas JOIN en frío de menos de 100 ms desde S3 + compresión y cifrado a nivel de página | Kitploit
Herramientas/GitHubGitHub/russellromney/turbolite
Herramientas de Cifrado/DescifradoCriptografíaSeguridad en la NubeUtilidades y FrameworksSeguridad de Bases de Datos
GitHubrussellromney/turbolite

turbolite

SQLite VFS con consultas JOIN en frío de menos de 100 ms desde S3 + compresión y cifrado a nivel de página

Ver Repositorio
48012hace 2 mesesRevisado por Kitploit

Más Populares

Ver todos →

Descubre las herramientas más usadas por nuestra comunidad.

Explora todas las herramientas

Explora nuestra colección de herramientas

Ver todas las herramientas →
Compartir

turbolite

turbolite es un VFS de SQLite en Rust que sirve búsquedas puntuales y uniones directamente desde S3 con una latencia en frío inferior a 250 ms.

Este repositorio es un espacio de trabajo de Cargo con dos crates:

  • turbolite — Biblioteca pura de Rust. VFS de SQLite con compresión a nivel de página, cifrado y escalonamiento a S3.
  • turbolite-ffi — FFI C / extensión cargable + enlaces de idiomas (Python, Node.js, Go).

También ofrece compresión a nivel de página (zstd) y cifrado (AES-256) para eficiencia y seguridad en reposo, que pueden usarse por separado de S3.

Experimental. turbolite está en desarrollo activo y contiene errores. Ten cuidado.

El almacenamiento de objetos se está volviendo rápido. S3 Express One Zone ofrece GETs de un solo dígito en milisegundos y Tigris también es extremadamente rápido. La brecha entre el disco local y el almacenamiento en la nube se está reduciendo, y turbolite lo explota.

El diseño y el nombre están inspirados en el enfoque de turbopuffer de arquitectura despiadada en torno a las limitaciones del almacenamiento en la nube. El objetivo inicial del proyecto era superar los arranques en frío de más de 500 ms de Neon. Objetivo cumplido.

Si tienes una base de datos por servidor, usa un volumen. turbolite explora cómo tener cientos o miles de bases de datos (una por inquilino, una por espacio de trabajo, una por dispositivo), no quieres un volumen para cada una, y estás de acuerdo con una única fuente de escritura.

turbolite se distribuye como biblioteca de Rust, una extensión cargable de SQLite (.so/.dylib), y paquetes de idiomas para Python y Node.js, además de dependencias de Github para Go. Cualquier almacenamiento compatible con S3 funciona (AWS S3, Tigris, R2, MinIO, etc.). Es un VFS de SQLite estándar que opera a nivel de página, por lo que la mayoría de las funciones de SQLite deberían funcionar: FTS, R-tree, JSON, modo WAL, etc.

turbolite es parte del ecosistema más amplio de hadb. turbolite independiente es un VFS de almacenamiento con un escritor seguro; si deseas elección de líder HA más replicación continua de WAL, úsalo a través de haqlite-turbolite, que agrega HaQLite y walrust encima. Ese camino HA es aún muy experimental.

Si deseas contribuir a turbolite o encontrar errores, por favor crea una solicitud de extracción o abre un problema.

Rendimiento

1 M publicaciones / 100 K usuarios (~1.5 GB almacenados) sin nada en caché, cada byte desde S3. EC2 c5.2xlarge + S3 Express One Zone (misma AZ, ~4 ms de latencia GET). Fly performance-8x + Tigris (~25 ms de latencia GET). Ambos: 8 vCPU dedicados, 16 GB RAM, 7 hilos de trabajo de precarga. Consulta Evaluación comparativa y El backend de almacenamiento importa.

Los puntos de referencia están organizados por nivel de caché (qué está ya en disco local cuando se ejecuta la consulta):

interior es el punto de referencia en frío más realista: las páginas interiores se cargan con avidez al abrir la conexión, por lo que cuando ejecutas tu primera consulta, ya están en caché. Las páginas de índice se precargan agresivamente en segundo plano en el primer acceso y puede que no estén listas aún.

Caché caliente (sobrecarga del VFS vs. SQLite simple)

100 K filas, Fly.io performance-2x (vCPU dedicado, NVMe, IAD):

Las búsquedas puntuales tienen la mayor sobrecarga por página (~2x). Todo lo demás se acerca o supera la paridad. La arquitectura de caché libre de bloqueos significa que las lecturas concurrentes nunca bloquean las escrituras.

Costo de punto de control

DespuésLocalS3 (RustFS en la misma región)
1 K inserciones19 ms38 ms
Lote de 10 K17 ms114 ms
1 K actualizaciones9 ms36 ms

Las escrituras siempre son a velocidad local. El costo de S3 solo ocurre en el punto de control. Números con RustFS en la misma región de Fly (~2 ms RTT). S3 Express One Zone sería comparable.

Inicio rápido

Python```bash

pip install turbolite

root@kitploit:~
- [Comandos esenciales](#core-commands)
- [Mejoras](#improvements)
- [Configuración](#configuration)
- [Opciones](#options)
  - [Opciones de prioridad y severidad](#priority-and-severity-options)
  - [Ejecución de múltiples comandos en secuencia](#multi-command-execution-in-sequence)
  - [Uso de IA](#using-ai)
- [Demostración](#demo)
- [Instalación](#installation)
- [Requisitos](#requirements)
- [Licencia](#license)```python
import turbolite

conn = turbolite.connect("my.db", mode="s3",
    bucket="my-bucket",
    endpoint="https://t3.storage.dev")

conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT)")
conn.execute("INSERT INTO users VALUES (1, 'alice', '[email protected]')")
conn.commit()

alice = conn.cursor().execute("SELECT * FROM users").fetchone()
print(alice[1])
>>> "alice"

Vea Instalación para Node, Go, Rust, modo solo local, y usando la extensión cargable .so directamente

Diseño

turbolite está diseñado para las restricciones de S3 en lugar de las restricciones del sistema de archivos. Cada decisión fluye de este modelo:

Arquitectura

turbolite añade capas de introspección e indirección entre SQLite y S3 que agrupan, comprimen, rastrean y obtienen páginas de manera eficiente.

SQLite usa un índice de árbol B y solicita una página a la vez. Sabe que la página N está en el desplazamiento de bytes N * tamaño_de_página. Y esas páginas se distribuyen aleatoriamente en el mapa de páginas para un acceso aleatorio eficiente. Pero en S3, obtener una página por solicitud significaría miles de GETs potencialmente aleatorios por consulta.

Pero las páginas no se crean de la misma manera. SQLite tiene diferentes tipos de páginas. turbolite separa los grupos de páginas por tipo: páginas de árbol B interiores, páginas de hoja de índice y páginas de hoja de datos.

Las páginas interiores se tocan en cada consulta para enrutar las búsquedas a las páginas hoja. turbolite las detecta, las almacena en lotes comprimidos en S3 y las carga con avidez al abrir el VFS. Después de eso, cada recorrido del árbol B es un acierto de caché.

Las páginas de hoja de índice reciben el mismo tratamiento: lotes separados, precarga en segundo plano perezosa, fijadas contra desalojo. Las consultas en frío solo necesitan obtener las páginas de datos.

turbolite aprovecha la introspección del árbol B para entender de qué árbol (una tabla o un índice) forma parte una página, y almacena inteligentemente esas páginas juntas en S3 como grupos de páginas: muchas páginas agrupadas en un solo objeto de S3. Lo suficientemente grandes para saturar el ancho de banda en la precarga, lo suficientemente pequeños para consultas puntuales. Por defecto: 256 páginas por grupo, ~16MB con páginas de 64KB.

Almacenar la misma tabla/índice juntos significa que hacemos la menor cantidad posible de GETs para consultas en frío.

turbolite indirecciona las búsquedas de páginas con un archivo de manifiesto que es la fuente de verdad sobre dónde vive cada página. Reemplaza el implícito offset = página * tamaño de SQLite con punteros explícitos. Las versiones antiguas de grupos de páginas nunca se sobrescriben; el PUT del manifiesto es el punto de confirmación atómico. Las versiones antiguas se convierten en basura, limpiada por gc().

SQLite utiliza páginas de 4KB por defecto para coincidir con el tamaño de página del disco del sistema de archivos. En S3, el tamaño de página del disco es irrelevante. Lo que importa es minimizar el número de solicitudes y maximizar la ramificación del árbol B. La respuesta son páginas grandes: turbolite utiliza páginas de 64KB por defecto. Menos páginas = menos viajes de ida y vuelta a S3 para llegar a una hoja.

Para hacer rápidas las consultas puntuales, turbolite utiliza compresión buscable: cada grupo de páginas se codifica como múltiples tramas zstd (~4 páginas por trama). El manifiesto almacena los desplazamientos de bytes por trama, por lo que en un fallo de caché se obtiene solo el subfragmento de ~256KB con la página necesaria mediante un GET de rango de S3, no todo el grupo.

La precarga tiene dos capas: proactiva (adelantarse al plan de consulta) y reactiva (adaptativa basada en fallos).

El adelantamiento al plan de consulta se ejecuta primero. Antes de que se ejecute una consulta, turbolite intercepta el plan de consulta de SQLite mediante EXPLAIN QUERY PLAN, extrae las tablas e índices exactos que la consulta tocará, y envía todos sus grupos de páginas al pool de precarga antes de que se lea siquiera la primera página. Una unión de cinco tablas que de otro modo desencadenaría cinco ciclos secuenciales de fallo-y-luego-extracción, en su lugar dispara las cinco obtenciones en paralelo al inicio de la consulta. Para consultas SCAN, esto significa que toda la tabla se precarga por adelantado.

Advertencia: SQLite admite una función de callback de traza por conexión. Si otra extensión reclama la ranura primero, el adelantamiento retrocede silenciosamente a la precarga reactiva.

La precarga reactiva maneja lo que el adelantamiento no cubre y actúa como respaldo. En un fallo de caché, ocurren dos cosas simultáneamente:

  1. GET de rango en línea: obtener el subfragmento específico que contiene la página necesaria, devolverlo a SQLite inmediatamente.
  2. Precarga en segundo plano: enviar los grupos hermanos de ese árbol al pool de precarga según una programación.

Los contadores de fallos se rastrean por árbol B, no globalmente. Una consulta de perfil que golpea usuarios (fallo 1) luego publicaciones (fallo 1) rastrea correctamente cada árbol en 1, no 2. Esto evita que una unión de múltiples tablas escale accidentalmente la precarga en cada árbol solo porque toca varios.

Cada fallo consecutivo avanza a través de una programación de precarga que controla qué fracción de grupos del mismo árbol precargar. turbolite selecciona una programación automáticamente según el plan de consulta:

  • Programación de búsqueda [0.3, 0.3, 0.4]: para consultas SEARCH ... USING INDEX que escanean porciones desconocidas de índices. Agresiva desde el primer fallo porque no sabemos cuánto del índice se escaneará.
  • Programación de consulta puntual [0.0, 0.0, 0.0]: para consultas puntuales y búsquedas de índice que golpean 1-2 páginas por árbol. Tres oportunidades gratuitas antes de cualquier precarga. Las programaciones con predominancia de cero superan a las de rampa temprana tanto en S3 Express como en Tigris.

Puede ajustar la programación de precarga en el momento de abrir estableciendo prefetch.search / prefetch.lookup en TurboliteConfig — usted conoce la forma esperada de la carga de trabajo, por lo que el VFS no tiene que adivinarlo. Consulte Configuración de precarga.

Ambas programaciones aprovechan la introspección del árbol B: cada grupo precargado garantiza contener páginas del árbol correcto. Un ejemplo: si SQLite solicita una página de la tabla usuarios, luego solicita otra de la misma tabla, turbolite asume que se acerca un escaneo y precarga el resto de la tabla usuarios en segundo plano, y nada más. Sin la introspección del árbol B, accidentalmente obtendría la mitad de la tabla usuarios y la mitad de la tabla publicaciones solo porque los datos viven uno al lado del otro en el disco.

La anticipación de hoja de índice hace lo mismo para SEARCH indexado. Una hoja de índice ya enumera los rowids de la tabla que SQLite está a punto de solicitar — por lo que turbolite los resuelve a través de las páginas interiores en caché y precarga esas tramas de tabla en un lote en lugar de una por una, reduciendo el número de solicitudes.

Caché de páginas en memoria

turbolite tiene su propio caché de páginas en memoria que reemplaza el caché de páginas integrado de SQLite. El paginador de SQLite almacena páginas internamente en caché y nunca vuelve a leer del VFS para páginas en caché. Esto está bien para bases de datos de un solo escritor, pero para réplicas de solo lectura (seguidores HA, lectores que sondean el manifiesto), el caché de SQLite se vuelve obsoleto cuando los datos subyacentes cambian mediante replicación.

El caché de turbolite es consciente del manifiesto: cuando se dispara set_manifest() (nuevos datos de replicación), invalida las páginas afectadas tanto en el caché de disco como en el caché en memoria. Las escrituras también invalidan sus páginas en el caché en memoria. Esto garantiza lecturas frescas después de la replicación o las escrituras.

Arquitectura:``` SQLite (PRAGMA cache_size=0) -> turbolite VFS xRead -> in-memory page cache (64MB default, AtomicPtr, zero-lock reads) -> disk cache (NVMe pread) -> S3 (on miss)

root@kitploit:~
**Configuración:**
- `cache.mem_budget` en `TurboliteConfig` (bytes). Por defecto: 64MB.
- Variable de entorno `TURBOLITE_MEM_CACHE_BUDGET` (p.ej., `128MB`, `1GB`).
- Establécelo en `0` para deshabilitar completamente la caché en memoria.

`turbolite.connect()` (Python/Go/TypeScript) deshabilita automáticamente la caché de páginas de SQLite y utiliza la de turbolite en su lugar. Los consumidores de Rust que usan directamente `Connection::open_with_flags_and_vfs` deben establecer `PRAGMA cache_size=0` para obtener el mismo comportamiento.

### Cifrado y Compresión

#### Compresión

Todos los datos se comprimen con zstd antes del almacenamiento. Los grupos de páginas usan codificación multiframe seekable que comprime independientemente cada frame (~4 páginas, ~256KB), por lo que una búsqueda puntual descomprime solo el frame relevante en lugar de todo el grupo de páginas. Los diccionarios zstd personalizados pueden mejorar aún más las tasas de compresión.

El modo local (no S3) también comprime a nivel de página con zstd. Consulta la CLI para las herramientas de entrenamiento de diccionarios.

#### Cifrado

Si el cifrado está habilitado, turbolite cifra todo: objetos S3, caché local, WAL, metadatos. Los datos de S3 usan AES-256-GCM con nonces aleatorios por frame (autenticado, detección de manipulaciones). Los datos locales usan AES-256-CTR con sobrecarga de tamaño cero. El cifrado ocurre después de la compresión: `plaintext → zstd → encrypt → S3`.

**Rotación de claves:** `rotate_encryption_key(config, new_key)` vuelve a cifrar, agrega o elimina el cifrado en todos los datos S3 sin descomprimir. `Some` a `Some` rota las claves, `Some` a `None` elimina el cifrado, `None` a `Some` lo agrega. A prueba de fallos: los objetos antiguos nunca se sobrescriben, la subida del manifiesto es el punto de confirmación atómico, y un paso de verificación confirma que los nuevos datos son legibles antes de confirmar. Los huérfanos de ejecuciones parciales se limpian con `gc()`.

## Fortalezas y Limitaciones

### Dónde turbolite es rápido

**Las búsquedas puntuales son el punto ideal.** En el nivel de caché `index`, una búsqueda puntual obtiene 1-2 sub-chunks mediante S3 range GET (~100KB cada uno). Las páginas interiores y de índice ya están en caché. En el nivel de caché `none`, añade ~120ms para la reobtención de la interior + primera página de datos. Esto funciona en cualquier tamaño de máquina.

**Escaneos con suficientes núcleos.** El pool de precarga satura el ancho de banda de S3 con programación adaptativa por árbol. Las consultas de búsqueda aumentan la precarga agresivamente desde el primer fallo; las consultas SCAN conscientes del plan precargan en bloque toda la tabla por adelantado. Suficientes hilos pueden sincronizar bases de datos de varios GB en segundos con 2-3 lotes de precarga.

### Dónde turbolite es lento

**Escaneos en máquinas pequeñas.** Con 1 hilo de precarga, un escaneo de 1.46GB toma segundos, no milisegundos. El cuello de botella son los viajes de ida y vuelta a S3: cada salto obtiene grupos en serie. Si tu primera consulta es un escaneo completo en una máquina de 1 vCPU, espera que el inicio sea doloroso.

**Mala sintonización de hilos.** Demasiado pocos hilos de precarga y los escaneos se detienen esperando a S3. Demasiados y el trabajo de SQLite en primer plano comienza a competir con las descargas. El valor predeterminado (`max(num_cpus - 1, 1)`) deja un núcleo para el trabajo en primer plano, pero las cargas de trabajo intensivas en escaneos en bases de datos grandes aún necesitan suficientes CPUs.

**Penalización de primera consulta.** La primera consulta en el nivel de caché `none` paga ~50-200ms por la carga de la página interior más al menos una obtención de datos. Si la consulta necesita una página de índice antes de que finalice la precarga en segundo plano, recurre a un GET de rango en línea.

### Limitaciones actuales

- **Turbolite independiente es de un solo escritor.** Dos máquinas escribiendo directamente en el mismo prefijo corromperán el manifiesto.
- **El modo HA/failover es experimental y se encuentra en `haqlite-turbolite`.** Esa pila combina las concesiones de HaQLite, la segmentación de páginas de turbolite y la replicación continua de WAL de walrust. Es la ruta prevista para despliegues multinodo, no el acceso directo de múltiples escritores a un prefijo de turbolite.
- **El envío de WAL es experimental.** Requiere el flag de feature `wal` + walrust. Consulta [Durabilidad](#durability).

Características de SQLite que **sí** funcionan: FTS, R-tree, JSON, modo WAL, modo de journal DELETE, VACUUM, autovacuum.

## Ajuste

### Parámetros generales

| Parámetro | Qué controla | Por defecto |
|-----------|-------------|---------|
| `prefetch.threads` | Hilos de trabajo para obtenciones S3 paralelas | max(num_cpus - 1, 1) |
| `cache.pages_per_group` | Páginas por objeto S3, más grande = menos PUTs, más bytes por obtención | 256 |
| `cache.gc_enabled` | Eliminar versiones antiguas de grupos de páginas después del checkpoint | true |
| `sync_mode` | Durabilidad del checkpoint: `Durable` (subida a S3 en el checkpoint) o `LocalThenFlush` (aplazar subida) | Durable |

### Programaciones de precarga

El frontrunning del plan de consulta (ver Arquitectura) es el mecanismo principal de precarga. Las programaciones reactivas a continuación actúan como respaldo cuando el frontrunning no está disponible o cuando las consultas acceden a páginas que no estaban en el plan.

| Estrategia | Cuándo | Programación por defecto | Qué sucede |
|----------|------|-----------------|--------------|
| **SCAN** (frontrun) | EQP dice `SCAN table` | Todos los grupos por adelantado | Precarga masiva de toda la tabla antes de la primera lectura. No se necesita programación de saltos. |
| **SEARCH** (reactivo) | EQP dice `SEARCH ... USING INDEX` | `[0.3, 0.3, 0.4]` | Precarga agresiva desde el primer fallo; escanea porciones de índice desconocidas. |
| **Lookup** (reactivo) | Consultas puntuales, sin información de EQP | `[0.0, 0.0, 0.0]` | Tres saltos libres, cero precarga. Las consultas puntuales rara vez se benefician de la precarga. |

Cada elemento es la fracción de grupos hermanos a precargar en el Nth fallo consecutivo de caché por árbol. Cuando los fallos superan la longitud del array, fracción=1.0 (todos los restantes).

**¿Por qué dos programaciones reactivas?** Las consultas SEARCH escanean porciones desconocidas de índices/tablas y necesitan un calentamiento agresivo. Las Lookups alcanzan 1-2 páginas por árbol y apenas necesitan precarga. Los contadores de fallos por árbol aseguran un seguimiento independiente: una consulta de perfil que alcanza usuarios (fallo 1) y luego posts (fallo 1) sigue cada árbol por separado.

### Configurando la precarga

Establece `prefetch.search` y `prefetch.lookup` en `TurboliteConfig` durante la construcción del VFS:```rust
use turbolite::tiered::{TurboliteConfig, PrefetchConfig};

let config = TurboliteConfig {
    prefetch: PrefetchConfig {
        search: vec![0.4, 0.3, 0.3],
        lookup: vec![0.0, 0.0, 0.2],
        query_plan: true,
        ..Default::default()
    },
    ..Default::default()
};

Para el reajuste por consulta sin reabrir la conexión, usa la función SQL turbolite_config_set (Phase Cirrus c). Cada envío está limitado al identificador de la conexión solicitante y permanece en vigor hasta que lo cambies de nuevo:```sql SELECT turbolite_config_set('prefetch_search', '0.5,0.5,0.0'); SELECT turbolite_config_set('prefetch_lookup', '0.0,0.0,0.0'); SELECT * FROM posts WHERE created_at > ?; -- runs with the new schedule

root@kitploit:~
### Index-leaf lookahead

Cuando una consulta utiliza un índice para encontrar filas en una tabla (`SEARCH ... USING INDEX`), la hoja del índice que SQLite lee ya nombra los rowids de la tabla que está a punto de recuperar. El lookahead analiza esos rowids, los resuelve a sus correspondientes frames de hoja de tabla a través de las páginas interiores en caché, y precarga los frames en un solo lote — de modo que las filas de la tabla llegan juntas en lugar de una ronda de ida y vuelta de S3 a la vez.

Está **activado por defecto** y solo se activa para los `SEARCH` indexados que persiguen una tabla. Los escaneos, las lecturas punto por rowid y las consultas completamente en caliente siguen la ruta normal sin alteraciones, por lo que rara vez hay motivo para desactivarlo. Necesita la precarga del plan de consulta (`plan_aware`, por defecto true).

El único caso para desactivarlo es una carga de trabajo completamente en caliente y sensible a la CPU, donde analizar cada hoja de índice cuesta un poco y no precarga nada porque las páginas ya están en caché:```sql
SELECT turbolite_config_set('lookahead', 'false');

O establece lookahead en TurboliteConfig / la variable de entorno TURBOLITE_LOOKAHEAD al momento de apertura.

Los llamadores Rust pueden invocar la misma ruta mediante turbolite::tiered::settings::set.

Configuraciones recomendadas

Nota: el prefetch es por conexión. Cada nueva conexión comienza con contadores de fallos por árbol en frío. El caché es compartido, por lo que una segunda conexión se beneficia de las páginas cacheadas por la primera.

La importancia del backend de almacenamiento

Los schedules óptimos de prefetch dependen del equilibrio latencia-ancho de banda de tu backend S3. Probamos 10 pares de schedules en 6 consultas tanto en S3 Express (~4ms GET) como en Tigris (~25ms GET):

En S3 Express, off/off (sin prefetch en absoluto) es sorprendentemente competitivo para consultas puntuales porque cada GET de rango de sub-chunk es solo ~4ms. La brecha entre "sin prefetch" y "prefetch óptimo" es pequeña (23% para búsquedas puntuales) porque los GET individuales son baratos. En Tigris, la misma consulta se beneficia mucho más del prefetch (hasta 39% en idx-filter) porque cada viaje de ida y vuelta desperdiciado cuesta 25ms.

El efecto práctico: en backends de alta latencia, empuja los schedules de búsqueda más agresivamente y mantén los schedules de búsqueda puntual con más ceros iniciales. En S3 Express, los valores predeterminados funcionan bien y el ajuste proporciona ganancias menores. El rendimiento de escaneo completo es insensible al schedule en ambos backends porque el frontrunning del plan de consulta realiza un prefetch masivo de toda la tabla de antemano.

Usa tiered-tune (ver abajo) para encontrar schedules óptimos para tu backend y consultas específicos.

Herramienta de ajuste

tiered-tune se conecta a una base de datos turbolite existente y barre schedules de prefetch contra tus consultas reales. En lugar de adivinar schedules, ejecuta tu carga de trabajo real y deja que la herramienta encuentre el mejor par:```bash

Connect to existing database, test your queries

cargo run --release --features cloud,zstd --bin tiered-tune --
--prefix "databases/tenant-123"
--query "SELECT * FROM users WHERE id = ?1"
--query "SELECT p.*, u.name FROM posts p JOIN users u ON p.user_id = u.id WHERE p.id = ?1"
--iterations 10

Custom schedule grid

cargo run --release --features cloud,zstd --bin tiered-tune --
--prefix "databases/tenant-123"
--query "SELECT * FROM orders WHERE user_id = ?1 ORDER BY created_at DESC LIMIT 20"
--search-schedules "0.3,0.3,0.4;0.5,0.5;1.0"
--lookup-schedules "0;0,0,0.1;0,0,0,0.1,0.2"
--iterations 10

root@kitploit:~
El resultado es una tabla de comparación por consulta (como `tiered-bench --matrix`) que muestra p50, p90, recuento de GET y bytes por cada par de planes. La herramienta recomienda un plan e imprime la asignación `TurboliteConfig` para aplicarlo.

## Durabilidad

turbolite es una capa de almacenamiento, no un sistema de replicación. La durabilidad depende de cuándo los datos llegan a S3.

**Después del punto de control (checkpoint)**: grupos de páginas + manifiesto están en S3. S3 proporciona 11 nueves de durabilidad. Estos datos sobreviven a la pérdida de la máquina.

**Entre puntos de control**: las escrituras solo viven en el WAL local, en el disco local. Si la máquina falla antes del siguiente punto de control, esas escrituras se pierden.

La frecuencia del punto de control controla la compensación: puntos de control más frecuentes = ventana de datos en riesgo más pequeña, pero más PUTs a S3. El valor predeterminado es el punto de control automático de SQLite (cada 1000 marcos WAL).

### Modos de punto de control

turbolite admite dos modos de punto de control mediante `sync_mode` en `TurboliteConfig`:

**`SyncMode::Durable`** (predeterminado). El punto de control sube grupos de páginas a S3 mientras mantiene el bloqueo EXCLUSIVO de SQLite. Simple, completamente duradero en cada punto de control. Ninguna escritura o lectura puede continuar hasta que la subida se complete. Adecuado para la mayoría de las cargas de trabajo.

**`SyncMode::LocalThenFlush`**. El punto de control escribe solo en la caché de disco local (~1 ms de retención de bloqueo), luego libera el bloqueo. La persona que llama sube a S3 por separado mediante `flush_to_s3()`, durante lo cual las lecturas y escrituras continúan normalmente. Esto es útil para cargas de trabajo con muchas escrituras donde bloquear a los lectores durante la duración de una subida a S3 es inaceptable.

Entre el punto de control y el vaciado (flush), los datos existen solo en la caché de disco local. Un fallo del proceso no es problema (los datos están en el disco local y los registros de preparación capturan el contenido exacto de las páginas para subirlas). La pérdida de la máquina antes del vaciado significa que esas escrituras se pierden. El desalojo de caché es seguro: turbolite protege automáticamente las páginas pendientes del desalojo.

**Recuperación tras fallo**: Si el proceso falla entre el punto de control y el vaciado, los registros de preparación sobreviven en el disco. En la siguiente llamada `TurboliteVfs::new()`, se recuperan automáticamente y se ponen en cola para la siguiente llamada `flush_to_s3()`. Las lecturas se atienden inmediatamente desde la caché local sin esperar al vaciado.

### Envío de WAL (experimental)

Con la característica `wal` habilitada, turbolite envía marcos WAL a S3 mediante [walrust](https://github.com/russellromney/walrust), cerrando la brecha de durabilidad entre escrituras individuales y puntos de control.```toml
# Cargo.toml
turbolite = { version = "0.5", features = ["cloud", "zstd", "wal"] }

git clone https://github.com/nil0x42/manspider cd manspider pip install pipenv pipenv install pipenv shell

run via pipenv

manspider --help

run via docker

docker build -t manspider . docker run -it --rm --network host manspider --help

run via docker-compose

docker-compose up

Instalación con pipx (Recomendado)

pipx install git+https://github.com/N0tA1dan/manspider

root@kitploit:~
manspider --help

➡️ [sweet] Detección de Interacción Media: Servicios Canary```rust

let config = TurboliteConfig { wal_replication: true, // enable WAL shipping ..Default::default() };

root@kitploit:~
turbolite y walrust se mantienen sincronizados mediante el cursor de reproducción almacenado como `manifest.change_counter`. Las rutas de importación/punto de control siembran ese cursor desde el contador de cambios de archivo de SQLite; la reproducción directa de páginas puede avanzarlo hasta la secuencia de conjunto de cambios confirmados más reciente. En un inicio en frío, turbolite materializa la base de datos a partir de grupos de páginas, luego walrust reproduce segmentos WAL con txid > `change_counter` para recuperar escrituras que ocurrieron después del último punto de control.

**Modelo de durabilidad con envío de WAL**: cada transacción confirmada se envía a S3 como un segmento WAL dentro del intervalo de sincronización (predeterminado 100ms). Si la máquina falla, se pierde como máximo un intervalo de sincronización de escrituras. Después del punto de control, los segmentos WAL con txid <= `change_counter` se recolectan como basura automáticamente.

El envío de WAL es complementario a SyncMode: SyncMode controla cómo los puntos de control llegan a S3, el envío de WAL hace que las escrituras individuales sean duraderas antes del punto de control.

### Modelo de consistencia

Escritor único, lectores de instantáneas. Un proceso escribe; los lectores ven el último manifiesto confirmado cuando abrieron. turbolite no es una base de datos distribuida y no coordina entre múltiples escritores.

## Modo Local (sin S3)

turbolite también funciona como un VFS comprimido/cifrado puramente local:

Compresión: zstd (predeterminado), lz4, snappy, gzip. Con zstd, puedes entrenar e incrustar diccionarios de compresión personalizados y rotarlos automáticamente para una compresión más eficiente. Los tamaños de página más grandes se comprimen mejor. Consulta la CLI para herramientas de entrenamiento.

Cifrado: AES-256-GCM por página.

La operación a nivel de página significa que la mayoría de las características de SQLite aún funcionan: FTS, R-tree, JSON, modo WAL. La mayoría de las otras extensiones de compresión/cifrado de SQLite operan a nivel de archivo o requieren compilaciones personalizadas.

## Instalación

Este repositorio es un espacio de trabajo de Cargo. El crate `turbolite` es la biblioteca pura de Rust en la raíz del espacio de trabajo. Los enlaces de lenguaje y la extensión cargable se encuentran en `turbolite-ffi/`.

**Python**: `pip install turbolite` — ver [turbolite-ffi/packages/python/](https://github.com/russellromney/turbolite/blob/HEAD/turbolite-ffi/packages/python/)```python
import turbolite

# Local compressed (no S3 needed)
conn = turbolite.connect("my.db")

# S3 cloud
conn = turbolite.connect("my.db", mode="s3", bucket="my-bucket", endpoint="https://t3.storage.dev")

# Manual extension loading for full control
import sqlite3
conn = sqlite3.connect(":memory:")
turbolite.load(conn)
conn.close()
conn = sqlite3.connect("file:my.db?vfs=turbolite", uri=True)       # local
# For S3, prefer turbolite.connect(..., mode="s3", bucket=..., prefix=...).
# It registers a per-database VFS so multiple S3 volumes can share one process.

Node.js: npm install turbolite — ver turbolite-ffi/packages/node/

Rust:```toml [dependencies] turbolite = "0.5" # local VFS turbolite = { version = "0.5", features = ["cloud"] } # + S3 storage turbolite = { version = "0.5", features = ["encryption"] } # + encryption

root@kitploit:~
**Go** (cgo, enlaza la biblioteca compartida):```bash
make lib-bundled  # build libturbolite.{so,dylib}
root@kitploit:~
// #cgo LDFLAGS: -L/path/to/target/release -lturbolite
// #include <stdlib.h>
// extern int turbolite_register_local_file_first(const char* name, const char* db_path, int level);
// extern void* turbolite_open(const char* path, const char* vfs_name);
// extern int turbolite_exec(void* db, const char* sql);
// extern char* turbolite_query_json(void* db, const char* sql);
// extern void turbolite_close(void* db);
import "C"

The recommended turbolite_register_local_file_first(name, db_path, level) está indexada por la ruta de base de datos visible para el usuario. La función de nivel inferior turbolite_register_local(name, cache_dir, level) aún se exporta para integradores que quieran gestionar su propio directorio de caché. Consulte examples/go/ para ver un ejemplo completo de servidor HTTP.

Extensión cargable (cualquier lenguaje)

Construya la extensión cargable para cualquier lenguaje con load_extension de SQLite:```bash

after cloning the turbolite repo

make ext # produces target/release/turbolite.{so,dylib}

root@kitploit:~
Please provide the Markdown content you wish to have translated.```c
sqlite3_enable_load_extension(db, 1);
sqlite3_load_extension(db, "path/to/turbolite", NULL, NULL);
// "turbolite" VFS (local) is always registered
// "turbolite-s3" is a single-volume convenience VFS when TURBOLITE_BUCKET is set

Para la historia de usuario de archivo primero, registre un VFS por base de datos que posea el app.db del llamador:```sql SELECT turbolite_register_file_first_vfs('app', '/data/app.db'); -- now open /data/app.db via vfs=app; turbolite stores its sidecar -- metadata at /data/app.db-turbolite/.

root@kitploit:~
Para configurar el VFS `"turbolite"` predeterminado para el modo de archivo primero (file-first) en el momento de carga de la extensión, establece `TURBOLITE_DATABASE_PATH=/data/app.db` en el entorno antes de cargar la extensión. El sidecar es entonces `/data/app.db-turbolite/` y el parámetro de nivel inferior `TURBOLITE_CACHE_DIR` se ignora.

### Node.js```bash
npm install turbolite

Capturas de pantalla

Tools Tools```js const { connect } = require("turbolite");

// File-first: /data/app.db is the local page image. // /data/app.db-turbolite/ holds hidden implementation state. const db = connect("/data/app.db"); db.exec("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT)"); db.prepare("INSERT INTO users VALUES (?, ?)").run(1, 'alice');

const rows = db.prepare("SELECT id, name FROM users").all(); // [{ id: 1, name: 'alice' }] db.close();

root@kitploit:~
`db` es una base de datos estándar de better-sqlite3. `connect()` registra un VFS de archivo primero por base de datos. Para exportar un archivo SQLite estándar (por ejemplo, para inspeccionarlo con la CLI de `sqlite3`), use la API de respaldo de better-sqlite3: `await db.backup('export.sqlite')`. Consulte la [documentación completa en turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/HEAD/turbolite-ffi/packages/node/).

### Rust (local, archivo primero)```rust
use turbolite::tiered::{TurboliteVfs, TurboliteConfig};

// `app.db` is the user-visible local page image.
// `app.db-turbolite/` holds hidden implementation state.
let config = TurboliteConfig::for_database_path("/data/app.db");
let vfs = TurboliteVfs::new_local(config)?;
turbolite::tiered::register("turbolite", vfs)?;

let conn = rusqlite::Connection::open_with_flags_and_vfs(
    "/data/app.db",
    rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
    "turbolite",
)?;

El formulario de nivel inferior le permite seleccionar el directorio de caché directamente:```rust let config = TurboliteConfig { cache_dir: "/path/to/data".into(), // turbolite owns this dir ..Default::default() };

root@kitploit:~
En ese caso, la imagen local es `/path/to/data/data.cache` en lugar de un `app.db` nombrado por el llamador. Los nuevos incrustadores deberían preferir la forma de archivo primero.

### Rust (S3 cloud)```rust
use turbolite::tiered::{TurboliteVfs, TurboliteConfig};
use hadb_storage::StorageBackend;

let config = TurboliteConfig::for_database_path("/data/app.db");
let storage: Arc<dyn StorageBackend> = /* your S3 backend */;
let vfs = TurboliteVfs::with_backend(config, storage, tokio::runtime::Handle::current())?;
turbolite::tiered::register("turbolite", vfs)?;

let conn = rusqlite::Connection::open_with_flags_and_vfs(
    "/data/app.db",
    rusqlite::OpenFlags::SQLITE_OPEN_READ_WRITE | rusqlite::OpenFlags::SQLITE_OPEN_CREATE,
    "turbolite",
)?;

app.db es la imagen de página comprimida de turbolite. No se garantiza que pueda abrirse directamente con sqlite3 estándar. Para un archivo SQLite normal (por ejemplo, para la CLI de sqlite3), use la API de respaldo en línea de SQLite o el asistente de exportación específico del binding (conn.iterdump() en Python, db.backup() en Node).

CLI

turbolite incluye una CLI para inspeccionar, administrar e interactuar con bases de datos turbolite sin escribir Rust.```bash cargo install turbolite --features cloud,zstd

root@kitploit:~
### Comandos```bash
# Inspect a database manifest
turbolite info --db my.db
turbolite info --db my.db --bucket my-bucket --endpoint https://t3.storage.dev

# Interactive SQLite shell (with turbolite VFS)
turbolite shell --db my.db
turbolite shell --db my.db --bucket my-bucket --read-only

# Download entire database from S3 into local cache
turbolite download --db my.db --bucket my-bucket --threads 8

# Export to plain SQLite (for migration or backup)
turbolite export --db my.db --output plain.db

# Import a plain SQLite file into turbolite S3 format
turbolite import --input plain.db --bucket my-bucket --prefix databases/my-db

Todos los comandos S3 aceptan las banderas --bucket, --prefix, --endpoint y --region, o leen de las variables de entorno TURBOLITE_BUCKET, TURBOLITE_PREFIX, AWS_ENDPOINT_URL y AWS_REGION.

Proyectos relacionados y comparación

Hay muchos proyectos en el espacio de SQLite sobre red. turbolite toma ideas de todos ellos.

Solicitudes de rango en archivos .db sin procesar (solo lectura)

El enfoque más común: colocar un archivo .db sin modificar en S3 o una CDN y emitir solicitudes HTTP Range GET cuando SQLite lee una página.

  • sql.js-httpvfs: El original. SQLite WASM con solicitudes HTTP Range. Tiene cabezales de lectura virtuales con precarga exponencial para escaneos. Pionero en la idea de que no es necesario descargar toda la base de datos para consultarla.
  • sqlite_web_vfs: Extensión VFS nativa en C++ con consolidación adaptativa de solicitudes y un archivo de índice .dbi opcional que precolecta nodos interiores del B-tree para precarga - la misma idea que los paquetes de páginas interiores de turbolite. Diseñado para componerse con sqlite_zstd_vfs.
  • sqlite3vfshttp: VFS Go limpio y mínimo. Construido para consultar SQLite en S3 desde Lambda sin descargar el archivo.
  • sqlite-s3-query: Librería Python que usa ctypes para interceptar E/S de archivos y traducir lecturas a GETs de rango de S3. Requiere buckets versionados para consistencia durante el reemplazo de la base de datos.
  • sqlite-wasm-http: Sucesor espiritual de sql.js-httpvfs usando la compilación WASM oficial de SQLite. Caché de páginas compartida mediante SharedArrayBuffer. Mantenido activamente.
  • s3sqlite: Python, usa s3fs (FUSE) + APSW. Deja que FUSE maneje las solicitudes de rango.

Todos estos son de solo lectura y obtienen páginas sin comprimir del archivo sin procesar. Una búsqueda puntual transfiere una página sin procesar de 4KB (o 64KB) por solicitud.

Replicación a nivel de página / sincronización periférica

Estos tratan el almacenamiento de objetos como la fuente de verdad y replican páginas individuales o conjuntos de cambios, permitiendo réplicas parciales y despliegues sin conexión/periféricos.

  • Graft (orbitinghail/graft): Un motor de almacenamiento transaccional para replicación perezosa, parcial y fuertemente consistente sobre S3. La extensión SQLite libgraft implementa un VFS que lee y escribe páginas de 4KB a través de volúmenes Graft. Usa compresión zstd en marcos y conjuntos de cambios basados en splinter. El primo arquitectónico más cercano a turbolite en el espacio de «replicar páginas, no marcos WAL», con un enfoque en la sincronización periférica multi-escritor en lugar de latencia de lectura en frío.
  • mvsqlite: Páginas almacenadas en FoundationDB como pares KV direccionados por contenido. MVCC completo con viaje en el tiempo a cualquier instantánea, codificación delta XOR+zstd entre versiones de página. El motor de almacenamiento más sofisticado en este espacio, pero requiere FoundationDB, no S3.

Replicación y copia de seguridad en S3

Estos replican escrituras locales a S3 para copia de seguridad o restauración.

  • Litestream: Envía continuamente marcos WAL a S3. El estándar de oro para copia de seguridad de SQLite. Un VFS más nuevo de Litestream puede servir lecturas desde S3 usando solicitudes de rango en archivos LTX con una caché LRU y un índice de páginas — arquitectónicamente lo más cercano a la ruta de lectura de turbolite, pero de solo lectura y ligado al formato de replicación de Litestream.
  • LiteFS: Sistema primario/réplica basado en FUSE de Fly.io. Captura conjuntos de cambios de páginas y los transmite a réplicas. Resuelve disponibilidad, no almacenamiento.
  • Verneuil: Divide la base de datos en fragmentos de 64KB con compresión zstd y un archivo de manifiesto, replica de forma asíncrona a S3. El modelo de fragmento+manifiesto se asemeja a los grupos de páginas + manifiesto de turbolite, pero Verneuil es una herramienta de replicación — consultas en disco local, no S3.
  • libSQL/sqld (por Turso): Bifurcación de SQLite con una interfaz Virtual WAL. El modo "Bottomless" envía marcos WAL a S3. Las consultas son locales; S3 es para restauración.

Motores de almacenamiento personalizados

  • sqlite-s3vfs: Cada página de SQLite almacenada como un objeto S3 separado. Permite escrituras pero a un PUT por página, costando $0.02 por 4096 páginas frente a los $0.000005 de turbolite por el mismo lote (con tamaño de página por defecto de 64KB). Vea la tabla de evaluación comparativa anterior para comparación de latencia de consulta en frío; turbolite es 7.5–263× más rápido en el mismo conjunto de datos.
  • wa-sqlite-s3vfs: Puerto TypeScript/navegador de sqlite-s3vfs para wa-sqlite. Mismo modelo de un objeto por página, adaptado para uso WASM/lado del cliente.

Compresión

  • sqlite_zstd_vfs: Almacena páginas comprimidas como filas en una base de datos "envoltorio" externa. zstd con entrenamiento de diccionario. Se compone con sqlite_web_vfs para lecturas comprimidas con solicitudes de rango sobre HTTP. La combinación de sqlite_web_vfs + sqlite_zstd_vfs es probablemente lo más parecido existente a la ruta de lectura de turbolite, pero es de solo lectura y no agrupa páginas en grupos.
  • SQLCipher: Encriptación AES-256 a nivel de página para SQLite local. Sin almacenamiento remoto.

Donde turbolite se diferencia

Evaluación comparativa

Todos los benchmarks viven en benchmark/. Consulte benchmark/README.md para escenarios de despliegue (local, Fly.io, EC2).

El binario tiered-bench genera un conjunto de datos de redes sociales (usuarios, publicaciones, me gusta, amistades) y evalúa consultas en cada nivel de caché contra S3.

Un arnés separado benchmark/bench_s3vfs.py ejecuta las mismas consultas contra sqlite-s3vfs para una comparación directa. Se despliega mediante benchmark/fly-s3vfs.toml y utiliza el mismo generador de conjuntos de datos determinista que tiered-bench.```bash

Basic benchmark: 100K posts, default settings

TIERED_TEST_BUCKET=my-bucket AWS_ENDPOINT_URL=https://t3.storage.dev
cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 100000

1M posts, 8 prefetch threads, only interior-level point queries

cargo run --features zstd,cloud --bin tiered-bench --release --
--sizes 1000000 --prefetch-threads 8 --queries post --modes interior

Quick local VFS comparison (no S3 needed)

cargo run --example quick-bench --features encryption --release

root@kitploit:~
Flags clave: `--sizes` (conteos de filas), `--ppg` (páginas por grupo), `--prefetch-threads`, `--prefetch-search` (programación de BÚSQUEDA), `--prefetch-lookup` (programación de búsqueda), `--grouping` (posicional o btree), `--queries` (post/perfil/quién-dio-like/mutuos), `--modes` (ninguno/interior/índice/datos), `--skip-verify` (omitir COUNT(*) en máquinas pequeñas), `--iterations`, `--plan-aware` (habilitar búsqueda anticipada predictiva), `--matrix` (pares de programación de barrido). Programaciones por consulta: `--post-prefetch`/`--post-lookup`, `--profile-prefetch`/`--profile-lookup`, etc. (búsqueda y búsqueda son independientes por consulta).```bash
# Matrix mode: test 10 schedule pairs x 6 queries at cold level
cargo run --features zstd,cloud --bin tiered-bench --release -- \
    --sizes 1000000 --import auto --plan-aware --matrix --iterations 10

# Tune schedules for your own database and queries
cargo run --features zstd,cloud --bin tiered-tune --release -- \
    --prefix "databases/my-db" \
    --query "SELECT * FROM users WHERE id = ?1" --param 42 \
    --plan-aware --iterations 10

Pruebas```bash

cargo test --features zstd # local VFS tests cargo test --features zstd,cloud # + S3 integration tests cargo test --features zstd,encryption # + encryption tests

root@kitploit:~
## Notas

turbolite anteriormente se llamaba `sqlite-compress-encrypt-vfs`, también conocido como `sqlces`.

### Detalles del modelo de seguridad

Los datos en S3 usan AES-256-GCM con nonces aleatorios únicos por trama (autenticado, detección de manipulación). Los archivos locales usan AES-256-CTR con nonces deterministas (número de página / desplazamiento de bytes), proporcionando confidencialidad contra atacantes de disco en reposo. Los nonces deterministas de CTR significan que los atacantes con múltiples instantáneas podrían recuperar el XOR de los textos planos en desplazamientos reutilizados, coincidiendo con la compensación de la extensión SEE de SQLite. La caché local es efímera y recreable desde S3.

## Licencia

Apache-2.0
Descargar herramienta
ConsultaTipoFrío (S3 Express)Frío (Tigris)
Publicación + usuariobúsqueda puntual + unión86 ms172 ms
Perfilunión multíple (5 JOINs)251 ms479 ms
Quién-dio-likebúsqueda en índice + unión206 ms302 ms
Amigos mutuosunión multíple búsqueda19 ms49 ms
Filtro indexadoescaneo de índice cubierto79 ms88 ms
Escaneo completo + filtroescaneo completo de tabla476 ms532 ms
Nivel de cachéQué está en cachéQué se obtiene de S3Cuándo ocurre
ningunonadatodoInicio fresco, caché vacía
interiorpáginas interiores del árbol Bpáginas de índice + datosPrimera consulta después de abrir conexión
índicepáginas interiores + de índicesolo páginas de datosOperación normal de turbolite
datostodonadaEquivalente a SQLite local
OperaciónSQLiteturboliteSobrecarga
Búsqueda puntual145 K/s73 K/s2.0x
Escaneo de rango8.8 K/s8.3 K/sparidad
Escaneo completo de tabla56/s60/sparidad
INSERT19 K/s23 K/sparidad
UPDATE por PK40 K/s27 K/s1.5x
INSERT por lotes (en txn)685 K/s740 K/sparidad
Restricción de S3Implicación
Los viajes de ida y vuelta son lentosMinimizar el número de solicitudes. Escribir por lotes, precargar lecturas de forma agresiva.
El ancho de banda es un cuello de botellaMaximizar la utilización del ancho de banda.
PUT y GET cobran por operaciónUn GET de 64KB cuesta lo mismo que un GET de 16MB. Optimizar el número de solicitudes, no la eficiencia en bytes.
Los objetos son inmutablesNunca actualizar en el lugar. Escribir nuevas versiones, intercambiar un puntero. Sin corrupción por escritura parcial.
El almacenamiento es baratoNo optimizar para el espacio. Sobredimensionar, mantener versiones antiguas, dejar que la recolección de basura limpie más tarde.
Carga de trabajoConfiguraciónPor qué
OLTP mixtoPredeterminadosPlan-aware maneja escaneos, el schedule de búsqueda calienta índices, el schedule de búsqueda puntual se mantiene conservador.
Carga pesada de puntos (DBs de agentes)prefetch.lookup: vec![0.0, 0.0, 0.0]Las búsquedas puntuales casi nunca necesitan prefetch.
Análisis pesado de escaneosprefetch.search: vec![0.5, 0.5], prefetch.query_plan: trueCalentamiento agresivo de búsqueda más prefetch masivo plan-aware.
Conservador (serverless con ráfagas)prefetch.search: vec![0.1, 0.2, 0.3], prefetch.lookup: vec![0.0, 0.0, 0.1]Ruido mínimo de prefetch.
BackendLatencia GETMejor búsqueda puntualMejor perfilGanancia de ajuste
S3 Express~4ms74ms (off/off: 96ms)188ms (off/off: 212ms)5-23% sobre sin prefetch
Tigris~25ms192ms (off/off: 231ms)524ms (off/off: 616ms)8-34% sobre sin prefetch
turboliteRaw-file range GETsLitestream VFSsqlite_web_vfs + zstd_vfsmvsqliteGraftsqlite-s3vfs
Lecturas desde S3GETs de rango buscables en grupos de páginas comprimidasGETs de rango en páginas sin procesarGETs de rango en archivos LTXGETs de rango en DB externa comprimidabúsquedas KV en FoundationDBobtención diferida de páginas de 4KB / conjuntos de cambiosun GetObject por página
Escrituras a S3punto de control (un PUT por grupo)nononosí (MVCC)sí (replicación asíncrona de conjuntos de cambios)un PUT por página
Compresiónzstd multiframe buscableningunaningunazstd (DB anidada)codificación delta zstdzstd en marcosninguna
EncriptaciónAES-256-GCM por páginaningunaningunaningunaningunano listadaninguna
Precargaanticipación + planificación de saltosninguna o readahead básicacaché LRUconsolidación adaptativabúferes de clienteperezosa / bajo demandaninguna
Optimización de páginas interioresdetectadas, fijadas, empaquetadas por separadoningunaíndice de páginas desde trailers LTXarchivo .dbi opcionalningunano listadaninguna
Bytes por búsqueda puntual (caché: índice)~100KB (un marco comprimido)4-64KB (una página sin procesar)varíavaríavaría4KB (una página)4KB (una página)
Costo de escritura por 4096 páginas~$0.000005 (un PUT)n/an/an/aoperaciones de FoundationDBconjuntos de cambios por lote~$0.02 (4096 PUTs)