
SQLite VFS con query JOIN a freddo inferiori a 100ms da S3 + compressione e crittografia a livello di pagina
turbolite è un VFS per SQLite in Rust che serve point lookup e join direttamente da S3 con una latenza a freddo inferiore a 250 ms.
Questo repository è un workspace Cargo con due crate:
turbolite — Libreria Rust pura. VFS SQLite con compressione a livello di pagina, crittografia e tiering S3.turbolite-ffi — FFI C / estensione caricabile + binding per linguaggi (Python, Node.js, Go).Offre anche compressione a livello di pagina (zstd) e crittografia (AES-256) per efficienza e sicurezza a riposo, che possono essere utilizzati separatamente da S3.
Sperimentale. turbolite è in fase di sviluppo attivo e contiene bug. Fai attenzione.
Lo storage di oggetti sta diventando veloce. S3 Express One Zone fornisce GET a singola cifra millisecondi e Tigris è anche estremamente veloce. Il divario tra disco locale e cloud storage si sta riducendo, e turbolite sfrutta questo.
Il design e il nome sono ispirati all'approccio di turbopuffer di architettare spietatamente intorno ai vincoli del cloud storage. L'obiettivo iniziale del progetto era battere gli avvii a freddo di oltre 500ms di Neon. Obiettivo raggiunto.
Se hai un database per server, usa un volume. turbolite esplora come avere centinaia o migliaia di database (uno per tenant, uno per workspace, uno per dispositivo), non volere un volume per ciascuno, e accettare una singola sorgente di scrittura.
turbolite è distribuito come libreria Rust, una estensione caricabile SQLite (.so/.dylib), e pacchetti per linguaggi per Python e Node.js, oltre a dipendenze Github per Go. Qualsiasi storage compatibile con S3 funziona (AWS S3, Tigris, R2, MinIO, ecc.). È un VFS SQLite standard che opera a livello di pagina, quindi la maggior parte delle funzionalità di SQLite dovrebbe funzionare: FTS, R-tree, JSON, modalità WAL, ecc.
turbolite fa parte dell'ecosistema più ampio di hadb. turbolite standalone è un VFS di storage con un singolo writer sicuro; se vuoi elezione leader HA più replicazione WAL continua, usalo tramite haqlite-turbolite, che aggiunge HaQLite e walrust sopra. Quel percorso HA è ancora molto sperimentale.
Se vuoi contribuire a turbolite o trovare bug, per favore crea una pull request o apri un issue.
| Query | Type | Cold (S3 Express) | Cold (Tigris) |
|---|---|---|---|
| Post + user | point lookup + join | 86ms | 172ms |
| Profile | multi-table join (5 JOINs) | 251ms | 479ms |
| Who-liked | index search + join | 206ms | 302ms |
| Mutual friends | multi-search join | 19ms | 49ms |
| Indexed filter | covered index scan | 79ms | 88ms |
| Full scan + filter | full table scan | 476ms | 532ms |
1M posts / 100K users (~1.5GB stored) with nothing cached, every byte from S3. EC2 c5.2xlarge + S3 Express One Zone (same AZ, ~4ms GET latency). Fly performance-8x + Tigris (~25ms GET latency). Both: 8 dedicated vCPU, 16GB RAM, 7 prefetch worker threads. See Benchmarking and Storage backend matters.
Benchmarks are organized by cache level (what's already on local disk when the query runs):
| Cache level | What's cached | What's fetched from S3 | When this happens |
|---|---|---|---|
| none | nothing | everything | Fresh start, empty cache |
| interior | interior B-tree pages | index + data pages | First query after connection open |
| index | interior + index pages | data pages only | Normal turbolite operation |
| data | everything | nothing | Equivalent to local SQLite |
interior è il benchmark a freddo più realistico: le pagine interior vengono caricate eager all'apertura della connessione, quindi quando esegui la prima query, sono già in cache. Le pagine indice fanno prefetch aggressivo al primo accesso in background e potrebbero non essere pronte.
100K righe, Fly.io performance-2x (vCPU dedicata, NVMe, IAD):
| Operation | SQLite | turbolite | Overhead |
|---|---|---|---|
| Point lookup | 145K/s | 73K/s | 2.0x |
| Range scan | 8.8K/s | 8.3K/s | parità |
| Full table scan | 56/s | 60/s | parità |
| INSERT | 19K/s | 23K/s | parità |
| UPDATE by PK | 40K/s | 27K/s | 1.5x |
| Batch INSERT (in txn) | 685K/s | 740K/s | parità |
I point lookup hanno il sovraccarico per pagina più alto (~2x). Tutto il resto si avvicina o supera la parità. L'architettura della cache lock-free significa che le letture concorrenti non bloccano mai le scritture.
| After | Local | S3 (same-region RustFS) |
|---|---|---|
| 1K inserts | 19ms | 38ms |
| 10K batch | 17ms | 114ms |
| 1K updates | 9ms | 36ms |
Le scritture sono sempre alla velocità locale. Il costo S3 è solo al checkpoint. Numeri con RustFS nella stessa regione Fly (~2ms RTT). S3 Express One Zone sarebbe comparabile.
pip install turbolite
- Puoi navigare tra le scelte usando i tasti freccia e premere Invio per selezionare
- Puoi annullare le selezioni usando ESC o Ctrl+C
### Configurazione
Tutti i file di configurazione sono memorizzati nella directory `~/.config/shellspec/` (o `.shellspec` nella root del progetto). Il file di configurazione principale è `config` (configurazione shellspec) e `shpec.opts` (configurazione shpec).
#### Configurazione shellspec
Il file `config` utilizza un formato simile a TOML:
```toml
[general]
# Imposta il tipo di shell (predefinito: sh)
shell = "bash"
# Imposta le opzioni della shell (predefinito: -eu)
shell_opts = "-euxo pipefail"
Il file config per shpec utilizza un formato simile alle variabili d'ambiente:
SHELL=bash
SHELL_OPTS=-eu
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"
Vedi [Installazione](#installazione) per Node, Go, Rust, modalità solo locale e uso diretto dell'estensione caricabile `.so`
## Progettazione
turbolite è progettato per i vincoli di S3, non per quelli del filesystem. Ogni decisione deriva da questo modello:
| Vincolo S3 | Implicazione |
|------------|--------------|
| **I round trip sono lenti** | Minimizzare il numero di richieste. Scritture in batch, prefetch aggressivo in lettura. |
| **La larghezza di banda è un collo di bottiglia** | Massimizzare l'utilizzo della larghezza di banda. |
| **PUT e GET hanno un costo per operazione** | Un GET da 64KB costa quanto uno da 16MB. Ottimizzare il numero di richieste, non l'efficienza in byte. |
| **Gli oggetti sono immutabili** | Non aggiornare mai in place. Scrivere nuove versioni, scambiare un puntatore. Nessun danneggiamento da scrittura parziale. |
| **Lo storage è economico** | Non ottimizzare per lo spazio. Sovra-provvedere, tenere vecchie versioni, lasciare che la GC pulisca in seguito. |
### Architettura
turbolite aggiunge livelli di introspezione e indirezione tra SQLite e S3 che raggruppano, comprimono, tracciano e recuperano le pagine in modo efficiente.