Skip to content
KitploitKITPLOIT
StrumentiBlog
Invia
StrumentiBlog
Invia

Strumenti di Hacking, PenTest e Cybersecurity per il tuo Arsenale di Sicurezza!

Kitploit è una directory di strumenti di hacking, cybersecurity e pentesting. Scopri gli ultimi aggiornamenti dei progetti per trovare vulnerabilità, analizzare sistemi, automatizzare i test e rafforzare la tua sicurezza.

··Feed·Contatto·Privacy·© 2026 Kitploit

Directory degli strumenti

Categorie

Vedi tutte le categorie
Loading categories
turbolite — SQLite VFS con query JOIN a freddo inferiori a 100ms da S3 + compressione e crittografia a livello di pagina | Kitploit
Strumenti/GitHubGitHub/russellromney/turbolite
Strumenti di Crittografia/DecrittografiaCrittografiaSicurezza CloudUtilità e FrameworkSicurezza dei Database
GitHubrussellromney/turbolite

turbolite

SQLite VFS con query JOIN a freddo inferiori a 100ms da S3 + compressione e crittografia a livello di pagina

Vedi Repository
4801242 mesi faRevisionato da Kitploit

Più Popolari

Vedi tutti →

Scopri gli strumenti più utilizzati dalla nostra community.

Esplora tutti gli strumenti

Sfoglia la nostra collezione di strumenti

Vedi tutti gli strumenti →
Condividi

turbolite

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.

Performance

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):

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.

Warm cache (VFS overhead vs plain SQLite)

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

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.

Checkpoint cost

AfterLocalS3 (same-region RustFS)
1K inserts19ms38ms
10K batch17ms114ms
1K updates9ms36ms

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.

Avvio Rapido

Python```bash

pip install turbolite

root@kitploit:~
- 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"

Configurazione shpec

Il file config per shpec utilizza un formato simile alle variabili d'ambiente:

root@kitploit:~
SHELL=bash
SHELL_OPTS=-eu

Esecuzione dei task```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"

root@kitploit:~
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.

SQLite utilizza un albero B-tree e richiede una pagina alla volta. Sa che la pagina N si trova all'offset byte `N * page_size`. E queste pagine sono distribuite casualmente nella mappa delle pagine per un accesso casuale efficiente. Ma su S3, recuperare una pagina per richiesta significherebbe migliaia di GET potenzialmente casuali per ogni query.

Ma le pagine non sono create allo stesso modo. SQLite ha diversi tipi di pagine. turbolite **separa i gruppi di pagine per tipo**: pagine interne B-tree, foglia di indice e foglia di dati.

Le pagine interne vengono toccate in ogni query per instradare le ricerche alle pagine foglia. turbolite le rileva, le archivia in bundle compressi su S3 e le carica con impazienza all'apertura del VFS. Dopo di che, ogni attraversamento del B-tree è un hit nella cache.

Le pagine foglia di indice ricevono lo stesso trattamento: bundle separati, prefetch pigro in background, fissati contro l'evizione. Le query a freddo devono solo recuperare le pagine dati.

turbolite sfrutta **l'introspezione del B-tree** per capire *a quale albero (una tabella o un indice) appartiene una pagina*, e archivia intelligentemente quelle pagine insieme in S3 come **gruppi di pagine**: molte pagine raggruppate in un singolo oggetto S3. Abbastanza grande da saturare la larghezza di banda in prefetch, abbastanza piccolo per query puntuali. Predefinito: 256 pagine per gruppo, ~16MB con pagine da 64KB.

Archiviare la stessa tabella/indice insieme significa fare il minor numero possibile di GET per le query a freddo.

turbolite **indirizza le ricerche delle pagine con un file manifest** che è la fonte di verità su dove si trova ogni pagina. Sostituisce l'implicito `offset = pagina * dimensione` di SQLite con puntatori espliciti. Le vecchie versioni del gruppo di pagine non vengono mai sovrascritte; il PUT del manifest è il punto di commit atomico. Le vecchie versioni diventano spazzatura, ripulite da `gc()`.

SQLite utilizza di default pagine da 4KB per corrispondere alla dimensione della pagina del disco del filesystem. Su S3, la dimensione della pagina del disco è irrilevante. Ciò che conta è minimizzare il numero di richieste e massimizzare il fan-out del B-tree. La risposta sono **pagine grandi**: turbolite usa di default pagine da 64KB. Meno pagine = meno round trip S3 per raggiungere una foglia.

Per rendere veloci le query puntuali, turbolite utilizza **compressione ricercabile**: ogni gruppo di pagine è codificato come più **frame zstd** (~4 pagine per frame). Il manifest memorizza gli offset byte per frame, quindi un cache miss recupera solo il sottogruppo di ~256KB con la pagina necessaria tramite range GET di S3, non l'intero gruppo.

Il prefetch ha due livelli: **proattivo** (anticipazione del piano di query) e **reattivo** (adattivo basato sui miss).

**L'anticipazione del piano di query** viene eseguita per prima. Prima che una query venga eseguita, turbolite intercetta il piano di query di SQLite tramite `EXPLAIN QUERY PLAN`, estrae le tabelle e gli indici esatti che la query toccherà e sottopone tutti i loro gruppi di pagine al pool di prefetch prima ancora che la prima pagina venga letta. Un join di cinque tabelle che altrimenti innescherebbe cinque cicli sequenziali di miss-e-recupero avvia invece tutti e cinque i recuperi in parallelo all'inizio della query. Per le query `SCAN`, ciò significa che l'intera tabella viene precaricata all'inizio.

> Avvertenza: SQLite supporta un solo callback di trace per connessione. Se un'altra estensione rivendica per prima lo slot, l'anticipazione torna silenziosamente al prefetch reattivo.

**Il prefetch reattivo** gestisce ciò che l'anticipazione non coglie e funge da fallback. In caso di cache miss, accadono due cose contemporaneamente:
1. **Range GET inline**: recupera il sottogruppo specifico contenente la pagina necessaria, restituiscilo immediatamente a SQLite.
2. **Prefetch in background**: sottoponi i gruppi fratelli *di quell'albero* al pool di prefetch secondo un programma.

I contatori dei miss sono tracciati **per B-tree, non globalmente**. Una query di profilo che colpisce `users` (miss 1) poi `posts` (miss 1) traccia correttamente ogni albero a 1, non 2. Ciò impedisce che un join multi-tabella possa accidentalmente aumentare il prefetch su ogni albero solo perché ne tocca diversi.

Ogni miss consecutivo avanza attraverso un **programma di prefetch** che controlla quale frazione dei gruppi dello stesso albero precaricare. turbolite seleziona automaticamente un programma in base al piano di query:
- **Programma di ricerca** `[0.3, 0.3, 0.4]`: per query `SEARCH ... USING INDEX` che scansionano porzioni sconosciute degli indici. Aggressivo dal primo miss perché non sappiamo quanta parte dell'indice verrà scansionata.
- **Programma di lookup** `[0.0, 0.0, 0.0]`: per query puntuali e lookup di indici che colpiscono 1-2 pagine per albero. Tre hop gratuiti prima di qualsiasi prefetch. I programmi con pesi iniziali a zero superano quelli con rampa anticipata sia su S3 Express che su Tigris.

Puoi regolare il programma di prefetch al momento dell'apertura impostando `prefetch.search` / `prefetch.lookup` su `TurboliteConfig` – conosci la forma del carico di lavoro previsto, quindi il VFS non deve indovinare. Vedi [Configurazione del prefetch](#configurazione-del-prefetch).

Entrambi i programmi sfruttano l'introspezione del B-tree: ogni gruppo precaricato è garantito per contenere pagine dell'albero giusto. Un esempio: se SQLite richiede una pagina dalla tabella `users`, poi ne richiede un'altra dalla stessa tabella, turbolite presume che stia arrivando una scansione e precarica il resto della tabella `users` in background, e nient'altro. Senza introspezione del B-tree, recupererebbe accidentalmente metà della tabella `users` e metà della tabella `posts` solo perché i dati si trovano uno accanto all'altro sul disco.

**Il lookahead sulle foglie di indice** fa lo stesso per `SEARCH` indicizzato. Una foglia di indice elenca già i rowid delle tabelle che SQLite sta per richiedere — quindi turbolite li risolve attraverso le pagine interne in cache e precarica quei frame della tabella in un unico batch invece che uno per uno, riducendo il numero di richieste.

### Cache di pagine in memoria

turbolite ha una propria cache di pagine in memoria che sostituisce la cache di pagine integrata di SQLite. Il pager di SQLite memorizza nella cache le pagine internamente e non rilegge mai dal VFS per le pagine in cache. Questo va bene per database a singolo scrittore, ma per le repliche di lettura (followers HA, lettori che eseguono polling del manifest), la cache di SQLite diventa obsoleta quando i dati sottostanti cambiano tramite replica.

La cache di turbolite è **consapevole del manifest**: quando `set_manifest()` viene attivato (nuovi dati dalla replica), invalida le pagine interessate sia nella cache su disco che nella cache in memoria. Le scritture invalidano anche le loro pagine nella cache in memoria. Ciò garantisce letture aggiornate dopo una replica o una scrittura.

**Architettura:**```
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)

Configurazione:

  • cache.mem_budget su TurboliteConfig (byte). Predefinito: 64 MB.
  • Variabile d'ambiente TURBOLITE_MEM_CACHE_BUDGET (ad es., 128MB, 1GB).
  • Imposta a 0 per disabilitare completamente la cache in memoria.

turbolite.connect() (Python/Go/TypeScript) disabilita automaticamente la cache di pagina di SQLite e utilizza invece quella di turbolite. I consumer Rust che usano direttamente Connection::open_with_flags_and_vfs dovrebbero impostare PRAGMA cache_size=0 per ottenere lo stesso comportamento.

Crittografia e Compressione

Compressione

Tutti i dati vengono compressi con zstd prima dell'archiviazione. I gruppi di pagine utilizzano la codifica seekable multi-frame che comprime indipendentemente ogni frame (~4 pagine, ~256 KB), quindi una ricerca puntuale decomprime solo il frame rilevante anziché l'intero gruppo di pagine. Dizionari zstd personalizzati possono migliorare ulteriormente i rapporti di compressione.

La modalità locale (non S3) comprime anche a livello di pagina con zstd. Vedi il CLI per gli strumenti di addestramento dei dizionari.

Crittografia

Se la crittografia è abilitata, turbolite crittografa tutto: oggetti S3, cache locale, WAL, metadati. I dati S3 utilizzano AES-256-GCM con nonce casuali per frame (autenticati, rilevamento di manomissioni). I dati locali utilizzano AES-256-CTR con overhead di dimensione zero. La crittografia avviene dopo la compressione: plaintext → zstd → encrypt → S3.

Rotazione delle chiavi: rotate_encryption_key(config, new_key) ricripta, aggiunge o rimuove la crittografia su tutti i dati S3 senza decomprimere. Some a Some ruota le chiavi, Some a None rimuove la crittografia, None a Some la aggiunge. Resistente ai crash: i vecchi oggetti non vengono mai sovrascritti, il caricamento del manifest è il punto di commit atomico, e un passaggio di verifica conferma che i nuovi dati siano leggibili prima del commit. Gli orfani da esecuzioni parziali vengono puliti da gc().

Punti di forza e limitazioni

Dove turbolite è veloce

Le ricerche puntuali sono il punto di forza. Con livello cache index, una ricerca puntuale recupera 1-2 sotto-chunk tramite S3 range GET (~100 KB ciascuno). Le pagine interiori e di indice sono già in cache. Con livello cache none, aggiungi ~120 ms per il recupero dell'interior + prima pagina dati. Funziona su qualsiasi dimensione di macchina.

Scan con abbastanza core. Il pool di prefetch satura la larghezza di banda S3 con scheduling adattivo per albero. Le query di search aumentano il prefetch in modo aggressivo dal primo miss; le query SCAN plan-aware prefetchano l'intera tabella in blocco in anticipo. Con thread sufficienti, si possono sincronizzare database multi-GB in secondi con 2-3 batch di prefetch.

Dove turbolite è lento

Scan su macchine piccole. Con 1 thread di prefetch, una scansione su 1.46 GB richiede secondi, non millisecondi. Il collo di bottiglia sono i round trip S3: ogni salto recupera gruppi in serie. Se la tua prima query è una scansione completa su una macchina a 1 vCPU, aspettati un avvio difficile.

Scarsa regolazione dei thread. Troppi pochi thread di prefetch e gli scan si bloccano in attesa di S3. Troppi e il lavoro in foreground di SQLite inizia a competere con i download. Il default (max(num_cpus - 1, 1)) lascia un core per il lavoro in foreground, ma carichi di lavoro pesanti sugli scan su database grandi richiedono comunque abbastanza CPU.

Penalità della prima query. La prima query a livello cache none paga ~50-200 ms per il caricamento delle pagine interiori più almeno un recupero dati. Se la query necessita di una pagina di indice prima che il prefetch in background finisca, ricade su un inline range GET.

Limitazioni attuali

  • Turbolite standalone è a singolo scrittore. Due macchine che scrivono direttamente allo stesso prefisso corromperanno il manifest.
  • La modalità HA/failover è sperimentale e risiede in haqlite-turbolite. Questo stack combina i lease di HaQLite, il page tiering di turbolite e la replica WAL continua di walrust. È il percorso previsto per distribuzioni multi-nodo, non l'accesso diretto multi-scrittore a un singolo prefisso turbolite.
  • L'invio del WAL è sperimentale. Richiede il flag di feature wal + walrust. Vedi Durabilità.

Le funzionalità di SQLite che funzionano: FTS, R-tree, JSON, modalità WAL, modalità journal DELETE, VACUUM, autovacuum.

Ottimizzazione

Parametri generali

Pianificazioni del prefetch

Il frontrunning del piano di query (vedi Architettura) è il meccanismo di prefetch principale. Le pianificazioni reattive sottostanti agiscono come fallback quando il frontrunning non è disponibile o quando le query accedono a pagine che non erano nel piano.

Ogni elemento è la frazione di gruppi fratelli da prefetchare all'N-esimo miss di cache consecutivo per albero. Quando i miss superano la lunghezza dell'array, frazione=1.0 (tutti i rimanenti).

Perché due pianificazioni reattive? Le query SEARCH scansionano porzioni sconosciute di indici/tabelle e necessitano di un riscaldamento aggressivo. I lookup colpiscono 1-2 pagine per albero e non necessitano quasi di prefetch. I contatori di miss per albero garantiscono un monitoraggio indipendente: una query di profilo che colpisce users (miss 1) poi posts (miss 1) tiene traccia di ciascun albero separatamente.

Configurare il prefetch

Imposta prefetch.search e prefetch.lookup su TurboliteConfig alla costruzione 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() };

root@kitploit:~
Per la regolazione per query senza riaprire la connessione, utilizzare la funzione SQL `turbolite_config_set` (Phase Cirrus c). Ogni push è limitato al handle della connessione chiamante e rimane in vigore fino a quando non lo si modifica di nuovo:```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

Anticipazione delle foglie dell'indice

Quando una query utilizza un indice per trovare righe di tabella (SEARCH ... USING INDEX), la foglia dell'indice che SQLite legge già nomina i rowid delle tabelle che sta per recuperare. L'anticipazione analizza quei rowid, li risolve nei loro frame di foglia di tabella attraverso le pagine interne memorizzate nella cache, e recupera in anticipo i frame in un unico lotto — in modo che le righe della tabella arrivino insieme invece che un round trip S3 alla volta.

È attiva per impostazione predefinita e si attiva solo per SEARCH indicizzate che proseguono in una tabella. Scansioni, letture punto per rowid e query completamente a caldo seguono il percorso normale senza modifiche, quindi raramente c'è motivo di disattivarla. Necessita del prefetch basato sul piano di query (plan_aware, true di default).

L'unico caso per disabilitarla è un carico di lavoro completamente a caldo e sensibile alla CPU, dove analizzare ogni foglia dell'indice costa un po' e non recupera nulla perché le pagine sono già nella cache:```sql SELECT turbolite_config_set('lookahead', 'false');

root@kitploit:~
Oppure imposta `lookahead` su `TurboliteConfig` / la variabile d'ambiente `TURBOLITE_LOOKAHEAD` all'apertura.

I chiamanti Rust possono invocare lo stesso percorso tramite `turbolite::tiered::settings::set`.

### Configurazioni consigliate

| Carico di lavoro | Configurazione | Perché |
|-----------------|----------------|--------|
| OLTP misto | Valori predefiniti | Plan-aware gestisce le scansioni, il programma di ricerca riscalda gli indici, il programma di lookup rimane conservativo. |
| Intenso su punti (DB agenti) | `prefetch.lookup: vec![0.0, 0.0, 0.0]` | I lookup non necessitano quasi mai di prefetch. |
| Analytics intensivo su scansioni | `prefetch.search: vec![0.5, 0.5]`, `prefetch.query_plan: true` | Riscaldamento aggressivo della ricerca più prefetch bulk plan-aware. |
| Conservativo (serverless a raffica) | `prefetch.search: vec![0.1, 0.2, 0.3]`, `prefetch.lookup: vec![0.0, 0.0, 0.1]` | Minimo rumore di prefetch. |

**Nota**: il prefetch è per connessione. Ogni nuova connessione parte con contatori di miss per albero a freddo. La cache è condivisa, quindi una seconda connessione beneficia delle pagine memorizzate dalla prima.

### Il backend di storage è importante

I programmi di prefetch ottimali dipendono dal compromesso latenza/bandwidth del tuo backend S3. Abbiamo testato 10 coppie di programmi su 6 query sia su S3 Express (~4ms GET) che su Tigris (~25ms GET):

| Backend | Latenza GET | Miglior lookup puntuale | Miglior profilo | Guadagno di ottimizzazione |
|---------|-------------|--------------------------|-----------------|---------------------------|
| **S3 Express** | ~4ms | 74ms (off/off: 96ms) | 188ms (off/off: 212ms) | 5-23% rispetto a nessun prefetch |
| **Tigris** | ~25ms | 192ms (off/off: 231ms) | 524ms (off/off: 616ms) | 8-34% rispetto a nessun prefetch |

Su S3 Express, `off/off` (nessun prefetch) è sorprendentemente competitivo per le query puntuali perché ogni GET di sub-chunk range è solo di ~4ms. Il divario tra "nessun prefetch" e "prefetch ottimale" è piccolo (23% per i lookup puntuali) perché i singoli GET sono economici. Su Tigris, la stessa query beneficia molto di più del prefetch (fino al 39% su idx-filter) perché ogni round trip sprecato costa 25ms.

L'effetto pratico: su backend ad alta latenza, spingere più aggressivamente i programmi di ricerca e mantenere programmi di lookup con più zeri iniziali. Su S3 Express, i valori predefiniti funzionano bene e l'ottimizzazione offre guadagni minori. Le prestazioni delle scansioni complete sono insensibili al programma su entrambi i backend perché il query-plan frontrunning esegue il prefetch bulk dell'intera tabella in anticipo.

Usa `tiered-tune` (vedi sotto) per trovare programmi ottimali per il tuo backend e le tue query specifici.

### Strumento di ottimizzazione

`tiered-tune` si connette a un database turbolite esistente e analizza i programmi di prefetch rispetto alle tue query reali. Invece di indovinare i programmi, esegui il tuo carico di lavoro reale e lascia che lo strumento trovi la coppia migliore:```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

L'output è una tabella di confronto per query (come tiered-bench --matrix) che mostra p50, p90, conteggio GET e byte per ogni coppia di schedule. Lo strumento consiglia uno schedule e stampa l'assegnazione TurboliteConfig per applicarlo.

Durability

turbolite è un layer di storage, non un sistema di replica. La durabilità dipende da quando i dati raggiungono S3.

Dopo il checkpoint: i gruppi di pagine + il manifest sono in S3. S3 offre 11 nove di durabilità. Questi dati sopravvivono alla perdita della macchina.

Tra i checkpoint: le scritture vivono solo nel WAL locale sul disco locale. Se la macchina muore prima del prossimo checkpoint, quelle scritture sono perse.

La frequenza del checkpoint controlla il compromesso: checkpoint più frequenti = finestra di dati a rischio più piccola ma più PUT su S3. Il default è l'auto-checkpoint di SQLite (ogni 1000 frame WAL).

Checkpoint modes

turbolite supporta due modalità di checkpoint tramite sync_mode in TurboliteConfig:

SyncMode::Durable (default). Il checkpoint carica i gruppi di pagine su S3 mentre mantiene il lock EXCLUSIVE di SQLite. Semplice, completamente durevole ad ogni checkpoint. Nessuna scrittura o lettura può procedere fino al completamento del caricamento. Buono per la maggior parte dei carichi di lavoro.

SyncMode::LocalThenFlush. Il checkpoint scrive solo nella cache del disco locale (~1ms di lock hold), poi rilascia il lock. Il chiamante carica su S3 separatamente tramite flush_to_s3(), durante il quale letture e scritture continuano normalmente. Questo è utile per carichi di lavoro con molte scritture dove bloccare i lettori per la durata di un upload su S3 è inaccettabile.

Tra checkpoint e flush, i dati esistono solo nella cache del disco locale. Un crash del processo va bene (i dati sono sul disco locale e i log di staging catturano i contenuti esatti delle pagine per l'upload). La perdita della macchina prima del flush significa che quelle scritture sono perse. La rimozione dalla cache è sicura: turbolite protegge automaticamente le pagine in sospeso dalla rimozione.

Recupero da crash: Se il processo crasha tra checkpoint e flush, i log di staging sopravvivono sul disco. Al successivo TurboliteVfs::new(), vengono automaticamente recuperati e accodati per la successiva chiamata flush_to_s3(). Le letture vengono servite immediatamente dalla cache locale senza attendere il flush.

WAL shipping (experimental)

Con il flag di funzionalità wal abilitato, turbolite invia i frame WAL a S3 tramite walrust, chiudendo il divario di durabilità tra scritture individuali e checkpoint.```toml

Cargo.toml

turbolite = { version = "0.5", features = ["cloud", "zstd", "wal"] }

root@kitploit:~
[No content provided in the INPUT section. Please paste the Markdown chunk to translate.]```rust
let config = TurboliteConfig {
    wal_replication: true,  // enable WAL shipping
    ..Default::default()
};

turbolite e walrust rimangono sincronizzati tramite il cursore di replay memorizzato come manifest.change_counter. I percorsi di import/checkpoint inizializzano quel cursore dal contatore di modifiche del file di SQLite; il replay diretto delle pagine può avanzarlo fino all'ultima sequenza di changeset committata. All'avvio a freddo, turbolite materializza il database dai gruppi di pagine, quindi walrust riproduce i segmenti WAL con txid > change_counter per recuperare le scritture avvenute dopo l'ultimo checkpoint.

Modello di durabilità con WAL shipping: ogni transazione committata viene inviata a S3 come segmento WAL entro l'intervallo di sincronizzazione (default 100ms). Se la macchina si blocca, al massimo un intervallo di sincronizzazione di scritture viene perso. Dopo il checkpoint, i segmenti WAL con txid <= change_counter vengono automaticamente raccolti come spazzatura.

Il WAL shipping è complementare a SyncMode: SyncMode controlla come i checkpoint raggiungono S3, il WAL shipping rende durevoli le singole scritture prima del checkpoint.

Modello di consistenza

Scrittore singolo, lettori di snapshot. Un processo scrive; i lettori vedono l'ultimo manifest committato al momento dell'apertura. turbolite non è un database distribuito e non coordina tra più scrittori.

Modalità locale (nessun S3)

turbolite funziona anche come VFS puramente locale compresso/crittografato:

Compressione: zstd (default), lz4, snappy, gzip. Con zstd, puoi addestrare e incorporare dizionari di compressione personalizzati e ruotarli automaticamente per una compressione più efficiente. Dimensioni di pagina più grandi comprimono meglio. Vedi CLI per strumenti di addestramento.

Crittografia: AES-256-GCM per pagina.

L'operazione a livello di pagina significa che la maggior parte delle funzionalità di SQLite funziona ancora: FTS, R-tree, JSON, modalità WAL. La maggior parte delle altre estensioni di compressione/crittografia di SQLite operano a livello di file o richiedono build personalizzate.

Installazione

Questo repository è un workspace Cargo. Il crate turbolite è la libreria pura Rust alla radice del workspace. I binding per linguaggi e l'estensione caricabile si trovano in turbolite-ffi/.

Python: pip install turbolite — vedere 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.

root@kitploit:~
**Node.js**: `npm install turbolite` — vedi [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/main/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

Go (cgo, collega la libreria condivisa):```bash make lib-bundled # build libturbolite.{so,dylib}

root@kitploit:~
```go
// #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"

La funzione raccomandata turbolite_register_local_file_first(name, db_path, level) è basata sul percorso del database visibile all'utente. La funzione di livello inferiore turbolite_register_local(name, cache_dir, level) è ancora esportata per gli sviluppatori che desiderano gestire manualmente la directory della cache. Vedi l'esempio examples/go/ per un esempio completo di server HTTP.

Estensione caricabile (qualsiasi linguaggio)

Costruisci l'estensione caricabile per qualsiasi linguaggio utilizzando load_extension di SQLite:```bash

after cloning the turbolite repo

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

root@kitploit:~
Il tool richiede un server MongoDB per memorizzare i risultati delle scansioni. L'installazione e la configurazione di MongoDB non sono coperte in questa guida.```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

Per la user story file-first, registra un VFS per database che possiede il app.db del chiamante:```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:~
Per configurare il VFS predefinito `"turbolite"` per la modalità file-first al momento del caricamento dell'estensione, imposta `TURBOLITE_DATABASE_PATH=/data/app.db` nell'ambiente prima di caricare l'estensione. Il sidecar è quindi `/data/app.db-turbolite/` e il parametro di livello inferiore `TURBOLITE_CACHE_DIR` viene ignorato.

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

👥 Destinatari

Copilot-For-Security è uno strumento versatile progettato per un'ampia gamma di utenti:

  • 🛡️ Professionisti della sicurezza: Migliora le tue capacità di threat hunting, risposta agli incidenti e valutazione delle vulnerabilità.
  • ☁️ Architetti e amministratori cloud: Assicurati che i tuoi ambienti Azure e M365 siano sicuri e conformi.
  • 👨‍💻 Sviluppatori e DevOps: Integra gli insight sulla sicurezza direttamente nel tuo ciclo di vita dello sviluppo.
  • 📊 Leader aziendali e CISO: Ottieni una panoramica chiara della tua postura di sicurezza e supporta il processo decisionale strategico con report basati sui dati.
  • 🤖 Appassionati di automazione: Sfrutta la potenza dello scripting e dell'automazione per creare flussi di lavoro di sicurezza personalizzati.```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` è un database standard better-sqlite3. `connect()` registra un VFS per-database file-first per te. Per esportare un file SQLite standard (ad esempio per ispezionarlo con la CLI `sqlite3`), usa l'API di backup di better-sqlite3: `await db.backup('export.sqlite')`. Vedi [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/main/turbolite-ffi/packages/node) per la documentazione completa.

### Rust (locale, file-first)```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",
)?;

Il modulo di livello inferiore ti permette di scegliere direttamente la directory della cache:```rust let config = TurboliteConfig { cache_dir: "/path/to/data".into(), // turbolite owns this dir ..Default::default() };

root@kitploit:~
In quel caso l'immagine locale è `/path/to/data/data.cache` piuttosto che un `app.db` denominato dal chiamante. I nuovi embedders dovrebbero preferire la forma file-primario.

### 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 è l'immagine di pagina compressa di turbolite. Non è garantito che possa essere aperto direttamente dal sqlite3 standard. Per un file SQLite normale (ad esempio per la CLI sqlite3), utilizza l'API di backup online di SQLite o l'helper di esportazione specifico per il binding (conn.iterdump() in Python, db.backup() in Node).

CLI

turbolite include una CLI per ispezionare, gestire e interagire con i database turbolite senza scrivere codice Rust.```bash cargo install turbolite --features cloud,zstd

root@kitploit:~
### Comandi```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

Tutti i comandi S3 accettano i flag --bucket, --prefix, --endpoint e --region, oppure leggono dalle variabili d'ambiente TURBOLITE_BUCKET, TURBOLITE_PREFIX, AWS_ENDPOINT_URL e AWS_REGION.

Progetti correlati e confronto

Esistono molti progetti nell'ambito di SQLite su rete. turbolite prende idee da tutti loro.

Richieste Range su file .db grezzi (sola lettura)

L'approccio più comune: mettere un file .db non modificato su S3 o una CDN ed emettere HTTP Range GET quando SQLite legge una pagina.

  • sql.js-httpvfs: L'originale. SQLite WASM con richieste HTTP Range. Ha testine di lettura virtuali con prefetch esponenziale per scansioni. Ha aperto la strada all'idea che non è necessario scaricare l'intero database per interrogarlo.
  • sqlite_web_vfs: Estensione VFS nativa in C++ con consolidamento adattivo delle richieste e un file indice .dbi opzionale che pre-raccoglie i nodi interni dell'albero B per il prefetch - la stessa idea dei bundle di pagine interne di turbolite. Progettato per comporsi con sqlite_zstd_vfs.
  • sqlite3vfshttp: VFS Go pulito e minimale. Costruito per interrogare SQLite su S3 da Lambda senza scaricare il file.
  • sqlite-s3-query: Libreria Python che usa ctypes per intercettare I/O file e tradurre le letture in S3 Range GET. Richiede bucket con versioning per coerenza durante la sostituzione del database.
  • sqlite-wasm-http: Successore spirituale di sql.js-httpvfs che utilizza la build WASM ufficiale di SQLite. Cache di pagine condivisa tramite SharedArrayBuffer. Mantenuto attivamente.
  • s3sqlite: Python, usa s3fs (FUSE) + APSW. Lascia che FUSE gestisca le richieste Range.

Tutti sono in sola lettura e recuperano pagine non compresse dal file grezzo. Una ricerca puntuale trasferisce una pagina grezza da 4KB (o 64KB) per richiesta.

Replicazione / sincronizzazione a livello di pagina

Questi trattano lo storage a oggetti come sorgente di verità e replicano singole pagine o set di modifiche, consentendo repliche parziali e distribuzioni offline-first / edge.

  • Graft (orbitinghail/graft): Un motore di archiviazione transazionale per replicazione lazy, parziale e fortemente coerente su S3. L'estensione SQLite libgraft implementa un VFS che legge e scrive pagine da 4KB attraverso volumi Graft. Usa compressione zstd con frame e set di modifiche basati su splinter. È il cugino architetturale più vicino a turbolite nello spazio "replicare pagine, non frame WAL", con un focus sulla sincronizzazione multi-scrittore edge piuttosto che sulla latenza di lettura a freddo.
  • mvsqlite: Pagine memorizzate in FoundationDB come coppie chiave-valore content-addressable. MVCC completo con viaggio nel tempo a qualsiasi snapshot, codifica delta XOR+zstd tra versioni di pagina. Il motore di archiviazione più sofisticato in questo spazio, ma richiede FoundationDB, non S3.

Replicazione e backup su S3

Questi replicano le scritture locali su S3 per backup o ripristino.

  • Litestream: Invia continuamente frame WAL su S3. Lo standard di riferimento per il backup di SQLite. Una versione più recente del VFS di Litestream può servire letture da S3 usando richieste Range su file LTX con cache LRU e indice pagine — architetturalmente la cosa più simile al percorso di lettura di turbolite, ma in sola lettura e legata al formato di replica di Litestream.
  • LiteFS: Sistema primario/replica basato su FUSE di Fly.io. Cattura i set di modifiche delle pagine e li trasmette alle repliche. Risolve la disponibilità, non lo storage.
  • Verneuil: Divide il database in blocchi da 64KB con compressione zstd e un file manifest, replica asincrona su S3. Il modello blocco+manifest assomiglia ai gruppi di pagine + manifest di turbolite, ma Verneuil è uno strumento di replica - si interroga il disco locale, non S3.
  • libSQL/sqld (di Turso): Fork di SQLite con interfaccia Virtual WAL. La modalità "Bottomless" invia frame WAL su S3. Le interrogazioni sono locali; S3 è per il ripristino.

Motori di archiviazione personalizzati

  • sqlite-s3vfs: Ogni pagina SQLite memorizzata come oggetto S3 separato. Abilita le scritture ma a un PUT per pagina, costando $0.02 per 4096 pagine contro i $0.000005 di turbolite per lo stesso lotto (con default di pagina da 64KB). Vedi la tabella di benchmark sopra per il confronto della latenza di interrogazione a freddo; turbolite è 7.5–263× più veloce sullo stesso dataset.
  • wa-sqlite-s3vfs: Porting TypeScript / browser di sqlite-s3vfs per wa-sqlite. Stesso modello un-oggetto-per-pagina, adattato per uso WASM / lato client.

Compressione

  • sqlite_zstd_vfs: Memorizza pagine compresse come righe in un database "wrapper" esterno. zstd con addestramento del dizionario. Si compone con sqlite_web_vfs per letture compresse con richieste Range su HTTP. La combinazione di sqlite_web_vfs + sqlite_zstd_vfs è probabilmente la cosa più simile al percorso di lettura di turbolite, ma è in sola lettura e non raggruppa le pagine in gruppi.
  • SQLCipher: Cifratura AES-256 a livello di pagina per SQLite locale. Nessuno storage remoto.

Dove turbolite si differenzia

Benchmark

Tutti i benchmark risiedono in benchmark/. Vedi benchmark/README.md per scenari di deployment (locale, Fly.io, EC2).

Il binario tiered-bench genera un dataset di social media (utenti, post, like, amicizie) e misura le query a ogni livello di cache verso S3.

Un harness separato benchmark/bench_s3vfs.py esegue le stesse query contro sqlite-s3vfs per un confronto diretto. Viene deployato tramite benchmark/fly-s3vfs.toml e usa lo stesso generatore di dataset deterministico di 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:~
Key flags: `--sizes` (conteggi di righe), `--ppg` (pagine per gruppo), `--prefetch-threads`, `--prefetch-search` (pianificazione SEARCH), `--prefetch-lookup` (pianificazione lookup), `--grouping` (posizionale o btree), `--queries` (post/profile/who-liked/mutual), `--modes` (none/interior/index/data), `--skip-verify` (salta COUNT(*) su macchine piccole), `--iterations`, `--plan-aware` (abilita prefetch in anticipo), `--matrix` (spazza coppie di pianificazioni). Pianificazioni per query: `--post-prefetch`/`--post-lookup`, `--profile-prefetch`/`--profile-lookup`, ecc. (search e lookup sono indipendenti per query).```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

Test```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:~
## Note

turbolite era precedentemente chiamato `sqlite-compress-encrypt-vfs`, noto anche come `sqlces`.

### Dettagli del modello di sicurezza

I dati in S3 utilizzano AES-256-GCM con nonce casuali unici per frame (autenticati, rilevamento manomissioni). I file locali utilizzano AES-256-CTR con nonce deterministici (numero di pagina / offset byte), fornendo riservatezza contro attaccanti con accesso al disco a riposo. I nonce deterministici di CTR significano che attaccanti con più snapshot potrebbero recuperare XOR dei testi in chiaro in offset riutilizzati, corrispondente al compromesso dell'estensione SEE di SQLite stesso. La cache locale è effimera e ricreabile da S3.

## Licenza

Apache-2.0
Scarica lo strumento
QueryTypeCold (S3 Express)Cold (Tigris)
Post + userpoint lookup + join86ms172ms
Profilemulti-table join (5 JOINs)251ms479ms
Who-likedindex search + join206ms302ms
Mutual friendsmulti-search join19ms49ms
Indexed filtercovered index scan79ms88ms
Full scan + filterfull table scan476ms532ms
Cache levelWhat's cachedWhat's fetched from S3When this happens
nonenothingeverythingFresh start, empty cache
interiorinterior B-tree pagesindex + data pagesFirst query after connection open
indexinterior + index pagesdata pages onlyNormal turbolite operation
dataeverythingnothingEquivalent to local SQLite
OperationSQLiteturboliteOverhead
Point lookup145K/s73K/s2.0x
Range scan8.8K/s8.3K/sparità
Full table scan56/s60/sparità
INSERT19K/s23K/sparità
UPDATE by PK40K/s27K/s1.5x
Batch INSERT (in txn)685K/s740K/sparità
ParametroCosa controllaPredefinito
prefetch.threadsThread worker per fetch paralleli S3max(num_cpus - 1, 1)
cache.pages_per_groupPagine per oggetto S3, più grande = meno PUT, più byte per fetch256
cache.gc_enabledElimina vecchie versioni dei gruppi di pagine dopo il checkpointtrue
sync_modeDurabilità del checkpoint: Durable (caricamento S3 nel checkpoint) o LocalThenFlush (caricamento differito)Durable
StrategiaQuandoPianificazione predefinitaCosa succede
SCAN (frontrun)EQP dice SCAN tableTutti i gruppi in anticipoPrefetch in blocco dell'intera tabella prima della prima lettura. Nessuna pianificazione di hop necessaria.
SEARCH (reactive)EQP dice SEARCH ... USING INDEX[0.3, 0.3, 0.4]Prefetch aggressivo dal primo miss; scansiona porzioni di indice sconosciute.
Lookup (reactive)Query puntuali, nessuna informazione EQP[0.0, 0.0, 0.0]Tre hop gratuiti, zero prefetch. Le query puntuali raramente beneficiano del prefetch.
turboliteRange GET su file grezzoLitestream VFSsqlite_web_vfs + zstd_vfsmvsqliteGraftsqlite-s3vfs
Legge da S3Range GET seekable su gruppi di pagine compressiRange GET su pagine grezzeRange GET su file LTXRange GET su DB esterno compressoKV lookup su FoundationDBfetch lazy di pagine / set di modifiche da 4KBun GetObject per pagina
Scrive su S3checkpoint (un PUT per gruppo)nononosì (MVCC)sì (replica asincrona di set di modifiche)un PUT per pagina
Compressionezstd multi-frame seekablenessunanessunazstd (DB annidato)codifica delta zstdzstd con framenessuna
CifraturaAES-256-GCM per paginanessunanessunanessunanessunanessuna elencatanessuna
Prefetchlook-ahead + piano di saltonessuno o readahead basecache LRUconsolidamento adattivobuffer clientlazy / on-demandnessuno
Ottimizzazione pagine internerilevate, fissate, raggruppate separatamentenessunaindice pagine da trailer LTXfile .dbi opzionalenessunanessuna elencatanessuna
Byte per ricerca puntuale (cache: indice)~100KB (un frame compresso)4-64KB (una pagina grezza)variabilevariabilevariabile4KB (una pagina)4KB (una pagina)
Costo di scrittura per 4096 pagine~$0.000005 (un PUT)n/dn/dn/doperazioni FoundationDBset di modifiche raggruppati~$0.02 (4096 PUT)