Skip to content
KitploitKITPLOIT
FerramentasBlog
Enviar
FerramentasBlog
Enviar

Ferramentas de Hacking, PenTest e Cibersegurança para o seu Arsenal de Segurança!

Kitploit é um diretório de ferramentas de hacking, cibersegurança e pentesting. Descubra as últimas atualizações de projetos para encontrar vulnerabilidades, analisar sistemas, automatizar testes e fortalecer sua segurança.

··Feeds·Contato·Privacidade·© 2026 Kitploit

Diretório de Ferramentas

Categorias

Ver todas as categorias
Loading categories
turbolite — SQLite VFS com consultas JOIN a frio em menos de 100ms a partir do S3 + compressão e criptografia em nível de página | Kitploit
Ferramentas/GitHubGitHub/russellromney/turbolite
Ferramentas de Criptografia/DescriptografiaCriptografiaSegurança na NuvemUtilitários e FrameworksSegurança de Banco de Dados
GitHubrussellromney/turbolite

turbolite

SQLite VFS com consultas JOIN a frio em menos de 100ms a partir do S3 + compressão e criptografia em nível de página

Ver Repositório
480122há 2 mesesRevisado pelo Kitploit

Mais Populares

Ver todos →

Descubra as ferramentas mais usadas pela nossa comunidade.

Explore todas as ferramentas

Navegue pela nossa coleção de ferramentas

Ver todas as ferramentas →
Compartilhar

turbolite

turbolite é um VFS SQLite em Rust que atende buscas pontuais e joins diretamente do S3 com latência a frio inferior a 250 ms.

Este repositório é um workspace Cargo com dois crates:

  • turbolite — Biblioteca puramente Rust. VFS SQLite com compressão em nível de página, criptografia e tiering para S3.
  • turbolite-ffi — FFI C / extensão carregável + bindings para linguagens (Python, Node.js, Go).

Também oferece compressão em nível de página (zstd) e criptografia (AES-256) para eficiência e segurança em repouso, que podem ser usados separadamente do S3.

Experimental. turbolite está em desenvolvimento ativo e contém bugs. Tenha cuidado.

O armazenamento de objetos está ficando rápido. S3 Express One Zone oferece GETs com latência de dígitos únicos em milissegundos e Tigris também é extremamente rápido. A distância entre disco local e armazenamento em nuvem está diminuindo, e turbolite explora isso.

O design e o nome são inspirados na abordagem do turbopuffer de arquitetar impiedosamente em torno das restrições do armazenamento em nuvem. O objetivo inicial do projeto era superar as inicializações a frio de mais de 500 ms do Neon. Objetivo alcançado.

Se você tem um banco de dados por servidor, use um volume. turbolite explora como ter centenas ou milhares de bancos de dados (um por inquilino, um por workspace, um por dispositivo), não quer um volume para cada um e está de acordo com uma única fonte de gravação.

turbolite é distribuído como uma biblioteca Rust, uma extensão carregável SQLite (.so/.dylib) e pacotes de linguagem para Python e Node.js, além de dependências Github para Go. Qualquer armazenamento compatível com S3 funciona (AWS S3, Tigris, R2, MinIO, etc.). É um VFS SQLite padrão operando no nível de página, então a maioria dos recursos do SQLite deve funcionar: FTS, R-tree, JSON, modo WAL, etc.

turbolite faz parte do ecossistema mais amplo do hadb. O turbolite autônomo é um VFS de armazenamento com um único writer seguro; se você quiser eleição de líder HA mais replicação contínua de WAL, use através do haqlite-turbolite, que adiciona HaQLite e walrust por cima. Esse caminho HA ainda é muito experimental.

Se você quiser contribuir com o turbolite ou encontrar bugs, crie um pull request ou abra uma issue.

Desempenho

1M posts / 100K usuários (~1.5GB armazenados) sem nada em cache, cada byte vindo do S3. EC2 c5.2xlarge + S3 Express One Zone (mesma AZ, ~4ms latência GET). Fly performance-8x + Tigris (~25ms latência GET). Ambos: 8 vCPU dedicados, 16GB RAM, 7 threads de worker de pré-busca. Veja Benchmarking e Backend de armazenamento importa.

Os benchmarks são organizados por nível de cache (o que já está no disco local quando a consulta é executada):

interior é o benchmark a frio mais realista: páginas interiores são carregadas ansiosamente na abertura da conexão, então, quando você executa sua primeira consulta, elas já estão em cache. Páginas de índice são pré-buscadas agressivamente no primeiro acesso em segundo plano e podem não estar prontas ainda.

Cache quente (sobrecarga do VFS vs SQLite puro)

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

Buscas pontuais têm a maior sobrecarga por página (~2x). Todo o resto se aproxima ou supera a paridade. A arquitetura de cache livre de bloqueio significa que leituras concorrentes nunca bloqueiam gravações.

Custo de checkpoint

ApósLocalS3 (RustFS mesma região)
1K inserções19ms38ms
10K em lote17ms114ms
1K atualizações9ms36ms

Gravações são sempre na velocidade local. O custo S3 é apenas no checkpoint. Números com RustFS na mesma região Fly (~2ms RTT). S3 Express One Zone seria comparável.

Início Rápido

Python```bash

pip install turbolite

root@kitploit:~
". So the input is just an empty string? Possibly the chunk is just a blank line. According to the rules, I should return only the translated text. If the input is empty, the output should be empty. But that might break the concatenation. However, the user specifically said "Translate ONLY the exact text provided". So I'll output nothing.

But to be safe, I'll check if there is any content after "INPUT:" in the message. The message ends with "INPUT:\n\n". So there are two newlines after "INPUT:", meaning the input is an empty line. I'll treat it as an empty input and return nothing.

I must not add any commentary. So my response should be empty.```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"

Veja Instalação para Node, Go, Rust, modo apenas local e usando a extensão carregável .so diretamente

Design

turbolite é projetado para as restrições do S3 em vez das restrições do sistema de arquivos. Cada decisão decorre deste modelo:

Arquitetura

turbolite adiciona camadas de introspecção e indireção entre o SQLite e o S3 que agrupam, comprimem, rastreiam e buscam páginas de forma eficiente.

O SQLite usa uma árvore B e solicita uma página de cada vez. Ele sabe que a página N está no deslocamento de bytes N * page_size. E essas páginas são distribuídas aleatoriamente pelo mapa de páginas para acesso aleatório eficiente. Mas no S3, buscar uma página por requisição significaria milhares de GETs potencialmente aleatórios por consulta.

Mas as páginas não são criadas igualmente. O SQLite possui diferentes tipos de páginas. turbolite separa grupos de páginas por tipo: páginas de árvore B interna, folha de índice e folha de dados.

Páginas internas são acessadas em toda consulta para rotear buscas para páginas folha. turbolite as detecta, armazena em pacotes comprimidos no S3 e as carrega ansiosamente na abertura do VFS. Depois disso, toda travessia da árvore B é um acerto de cache.

Páginas folha de índice recebem o mesmo tratamento: pacotes separados, pré-carregamento preguiçoso em segundo plano, fixadas contra despejo. Consultas a frio só precisam buscar páginas de dados.

turbolite aproveita a introspecção da árvore B para entender de qual árvore (uma tabela ou índice) uma página faz parte e armazena inteligentemente essas páginas juntas no S3 como grupos de páginas: muitas páginas agrupadas em um único objeto S3. Grandes o suficiente para saturar a largura de banda no pré-carregamento, pequenos o suficiente para consultas pontuais. Padrão: 256 páginas por grupo, ~16MB com páginas de 64KB.

Armazenar a mesma tabela/índice juntos significa que fazemos o menor número possível de GETs para consultas a frio.

turbolite indireciona buscas de páginas com um arquivo de manifesto que é a fonte de verdade de onde cada página reside. Ele substitui o implícito offset = page * size do SQLite por ponteiros explícitos. Versões antigas de grupos de páginas nunca são sobrescritas; o PUT do manifesto é o ponto de commit atômico. Versões antigas tornam-se lixo, limpas por gc().

O SQLite usa páginas de 4KB por padrão para corresponder ao tamanho de página do disco do sistema de arquivos. No S3, o tamanho de página do disco é irrelevante. O que importa é minimizar o número de requisições e maximizar a ramificação da árvore B. A resposta é páginas grandes: turbolite usa páginas de 64KB por padrão. Menos páginas = menos viagens de ida e volta ao S3 para alcançar uma folha.

Para tornar consultas pontuais rápidas, turbolite usa compressão buscável: cada grupo de páginas é codificado como múltiplos quadros zstd (~4 páginas por quadro). O manifesto armazena deslocamentos de bytes por quadro, de modo que uma falha de cache busca apenas o subbloco de ~256KB com a página necessária via GET de intervalo do S3, não o grupo inteiro.

O pré-carregamento possui duas camadas: proativo (antecipação do plano de consulta) e reativo (adaptativo baseado em falhas).

A antecipação do plano de consulta é executada primeiro. Antes de uma consulta executar, turbolite intercepta o plano de consulta do SQLite via EXPLAIN QUERY PLAN, extrai as tabelas e índices exatos que a consulta tocará e submete todos os grupos de páginas deles ao pool de pré-carregamento antes mesmo que a primeira página seja lida. Uma junção de cinco tabelas que, de outra forma, acionaria cinco ciclos sequenciais de falha-e-busca, em vez disso dispara todas as cinco buscas em paralelo no início da consulta. Para consultas SCAN, isso significa que toda a tabela é pré-carregada antecipadamente.

Caveat: O SQLite suporta um callback de rastreio por conexão. Se outra extensão reivindicar o slot primeiro, a antecipação silenciosamente recai para o pré-carregamento reativo.

O pré-carregamento reativo lida com o que a antecipação perde e atua como fallback. Em uma falha de cache, duas coisas acontecem simultaneamente:

  1. GET de intervalo em linha: busca o subbloco específico contendo a página necessária, retorna ao SQLite imediatamente.
  2. Pré-carregamento em segundo plano: submete grupos irmãos para aquela árvore ao pool de pré-carregamento de acordo com um agendamento.

Contadores de falha são rastreados por árvore B, não globalmente. Uma consulta de perfil que atinge users (falha 1) e depois posts (falha 1) corretamente rastreia cada árvore como 1, não 2. Isso impede que uma junção de várias tabelas acidentalmente escale o pré-carregamento em cada árvore só porque toca várias.

Cada falha consecutiva avança por um agendamento de pré-carregamento que controla qual fração de grupos da mesma árvore pré-carregar. turbolite seleciona um agendamento automaticamente com base no plano de consulta:

  • Agendamento de busca [0.3, 0.3, 0.4]: para consultas SEARCH ... USING INDEX que escaneiam porções desconhecidas de índices. Agressivo desde a primeira falha porque não sabemos quanto do índice será escaneado.
  • Agendamento de consulta pontual [0.0, 0.0, 0.0]: para consultas pontuais e buscas de índice que atingem 1-2 páginas por árvore. Três saltos gratuitos antes de qualquer pré-carregamento. Agendamentos com zero pesado superam os de ramificação inicial tanto no S3 Express quanto no Tigris.

Você pode ajustar o agendamento de pré-carregamento no momento da abertura definindo prefetch.search / prefetch.lookup no TurboliteConfig - você conhece a forma de carga de trabalho esperada, então o VFS não precisa adivinhar. Veja Configurando pré-carregamento.

Ambos os agendamentos aproveitam a introspecção da árvore B: todo grupo pré-carregado tem garantia de conter páginas da árvore correta. Um exemplo: se o SQLite solicita uma página da tabela users, depois solicita outra da mesma tabela, turbolite assume que uma varredura está chegando e pré-carrega o resto da tabela users em segundo plano, e nada mais. Sem introspecção da árvore B, acidentalmente buscaria metade da tabela users e metade da tabela posts só porque os dados estão próximos uns dos outros no disco.

A antecipação de folha de índice faz o mesmo para SEARCH indexado. Uma folha de índice já lista os rowids de tabela que o SQLite está prestes a solicitar — então turbolite os resolve através das páginas internas em cache e pré-carrega esses quadros de tabela em um lote em vez de um por um, reduzindo o número de requisições.

Cache de páginas em memória

turbolite possui seu próprio cache de páginas em memória que substitui o cache de páginas embutido do SQLite. O pager do SQLite armazena páginas internamente em cache e nunca relê do VFS para páginas em cache. Isso é adequado para bancos de dados com um único gravador, mas para réplicas de leitura (seguidores HA, leitores de manifesto por polling), o cache do SQLite fica desatualizado quando os dados subjacentes mudam via replicação.

O cache do turbolite é ciente do manifesto: quando set_manifest() é acionado (novos dados da replicação), ele invalida páginas afetadas tanto no cache de disco quanto no cache em memória. Escritas também invalidam suas páginas no cache em memória. Isso garante leituras atualizadas após replicação ou escritas.

Arquitetura:``` 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:~
**Configuração:**
- `cache.mem_budget` no `TurboliteConfig` (bytes). Padrão: 64MB.
- Variável de ambiente `TURBOLITE_MEM_CACHE_BUDGET` (ex.: `128MB`, `1GB`).
- Defina como `0` para desativar completamente o cache em memória.

`turbolite.connect()` (Python/Go/TypeScript) desativa automaticamente o cache de páginas do SQLite e usa o cache do turbolite. Consumidores Rust que usam `Connection::open_with_flags_and_vfs` diretamente devem definir `PRAGMA cache_size=0` para obter o mesmo comportamento.

### Criptografia e Compactação

#### Compactação

Todos os dados são compactados com zstd antes do armazenamento. Grupos de páginas usam codificação multi-quadro buscável que compacta independentemente cada quadro (~4 páginas, ~256KB), então uma consulta por ponto descompacta apenas o quadro relevante, em vez de todo o grupo de páginas. Dicionários zstd personalizados podem melhorar ainda mais as taxas de compactação.

O modo local (não S3) também compacta no nível da página com zstd. Veja a CLI para ferramentas de treinamento de dicionário.

#### Criptografia

Se a criptografia estiver ativada, o turbolite criptografa tudo: objetos S3, cache local, WAL, metadados. Dados S3 usam AES-256-GCM com nonces aleatórios por quadro (autenticado, detector de adulteração). Dados locais usam AES-256-CTR com zero de sobrecarga de tamanho. A criptografia ocorre após a compactação: `plaintext → zstd → criptografar → S3`.

**Rotação de chaves:** `rotate_encryption_key(config, new_key)` re-criptografa, adiciona ou remove criptografia em todos os dados S3 sem descompactar. `Some` para `Some` rotaciona chaves, `Some` para `None` remove criptografia, `None` para `Some` adiciona. Seguro contra falhas: objetos antigos nunca são sobrescritos, o upload do manifesto é o ponto de confirmação atômico, e uma etapa de verificação confirma que os novos dados são legíveis antes de confirmar. Órfãos de execuções parciais são limpos por `gc()`.

## Pontos Fortes e Limitações

### Onde o turbolite é rápido

**Consultas por ponto são o ponto ideal.** No nível de cache `index`, uma consulta por ponto busca 1-2 sub-chunks via S3 range GET (~100KB cada). Páginas interiores e de índice já estão em cache. No nível de cache `none`, adicione ~120ms para re-busca da página interior + primeira página de dados. Isso funciona em qualquer tamanho de máquina.

**Varreduras com núcleos suficientes.** O pool de pré-busca satura a largura de banda do S3 com escalonamento adaptativo por árvore. Consultas de pesquisa aumentam agressivamente a pré-busca a partir da primeira falta; consultas SCAN cientes do plano pré-buscam em massa toda a tabela antecipadamente. Com threads suficientes, é possível sincronizar bancos de dados multi-GB em segundos com 2-3 lotes de pré-busca.

### Onde o turbolite é lento

**Varreduras em máquinas pequenas.** Com 1 thread de pré-busca, uma varredura de 1,46GB leva segundos, não milissegundos. O gargalo são as viagens de ida e volta ao S3: cada salto busca grupos serialmente. Se sua primeira consulta for uma varredura completa em uma máquina de 1 vCPU, espere uma inicialização dolorosa.

**Ajuste ruim de threads.** Poucas threads de pré-busca e as varreduras param de esperar pelo S3. Muitas e o trabalho do SQLite em primeiro plano começa a competir com os downloads. O padrão (`max(num_cpus - 1, 1)`) deixa um núcleo para o trabalho em primeiro plano, mas cargas de trabalho intensivas em varredura em bancos de dados grandes ainda precisam de CPUs suficientes.

**Penalidade da primeira consulta.** A primeira consulta no nível de cache `none` paga ~50-200ms para carregar a página interior mais pelo menos uma busca de dados. Se a consulta precisar de uma página de índice antes que a pré-busca em segundo plano termine, ela recorre a um range GET inline.

### Limitações atuais

- **O turbolite standalone é de escritor único.** Duas máquinas escrevendo diretamente no mesmo prefixo corromperão o manifesto.
- **O modo HA/failover é experimental e vive em `haqlite-turbolite`.** Essa pilha combina leases do HaQLite, hierarquia de páginas do turbolite e replicação contínua de WAL do walrust. É o caminho pretendido para implantações multi-nó, não acesso direto de vários escritores a um único prefixo do turbolite.
- **O envio de WAL é experimental.** Requer a flag de recurso `wal` + walrust. Veja [Durabilidade](#durabilidade).

Funcionalidades do SQLite que **funcionam**: FTS, R-tree, JSON, modo WAL, modo de journal DELETE, VACUUM, autovacuum.

## Ajuste

### Parâmetros gerais

| Parâmetro | O que controla | Padrão |
|-----------|----------------|--------|
| `prefetch.threads` | Threads de trabalho para buscas paralelas do S3 | max(num_cpus - 1, 1) |
| `cache.pages_per_group` | Páginas por objeto S3, maior = menos PUTs, mais bytes por busca | 256 |
| `cache.gc_enabled` | Excluir versões antigas de grupos de páginas após checkpoint | true |
| `sync_mode` | Durabilidade do checkpoint: `Durable` (upload S3 no checkpoint) ou `LocalThenFlush` (adiar upload) | Durable |

### Agendas de pré-busca

O adiantamento do plano de consulta (veja Arquitetura) é o principal mecanismo de pré-busca. As agendas reativas abaixo atuam como fallback quando o adiantamento não está disponível ou quando as consultas acessam páginas que não estavam no plano.

| Estratégia | Quando | Agenda padrão | O que acontece |
|------------|--------|---------------|----------------|
| **SCAN** (adiantamento) | EQP diz `SCAN table` | Todos os grupos antecipadamente | Pré-busca em massa de toda a tabela antes da primeira leitura. Nenhuma agenda de salto necessária. |
| **SEARCH** (reativo) | EQP diz `SEARCH ... USING INDEX` | `[0.3, 0.3, 0.4]` | Pré-busca agressiva a partir da primeira falta; varre partes de índice desconhecidas. |
| **Lookup** (reativo) | Consultas por ponto, sem info EQP | `[0.0, 0.0, 0.0]` | Três saltos livres, zero pré-busca. Consultas por ponto raramente se beneficiam da pré-busca. |

Cada elemento é a fração de grupos irmãos a pré-buscar na N-ésima falta consecutiva de cache por árvore. Quando as faltas excedem o comprimento da matriz, fração=1.0 (todo o restante).

**Por que duas agendas reativas?** Consultas SEARCH varrem partes desconhecidas de índices/tabelas e precisam de aquecimento agressivo. Lookups atingem 1-2 páginas por árvore e dificilmente precisam de pré-busca. Contadores de falta por árvore garantem rastreamento independente: uma consulta de perfil que acessa usuários (falta 1) e depois posts (falta 1) rastreia cada árvore separadamente.

### Configurando pré-busca

Defina `prefetch.search` e `prefetch.lookup` no `TurboliteConfig` na construção do 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 reajuste por consulta sem reabrir a conexão, use a função SQL turbolite_config_set (Phase Cirrus c). Cada push fica restrito ao handle da conexão de chamada e permanece em vigor até que você o altere novamente:```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

When a query uses an index to find table rows (`SEARCH ... USING INDEX`), the index leaf SQLite reads already names the table rowids it's about to fetch. Lookahead parses those rowids, resolves them to their table-leaf frames through the cached interior pages, and prefetches the frames in one batch — so the table rows arrive together instead of one S3 round trip at a time.

It's **on by default** and only engages for indexed `SEARCH` that chases into a table. Scans, point-by-rowid reads, and fully-warm queries take the normal path untouched, so there's rarely a reason to turn it off. It needs query-plan prefetch (`plan_aware`, default true).

The one case for disabling it is a fully-warm, CPU-sensitive workload, where parsing each index leaf costs a little and prefetches nothing because the pages are already cached:```sql
SELECT turbolite_config_set('lookahead', 'false');

Ou defina lookahead no TurboliteConfig / a variável de ambiente TURBOLITE_LOOKAHEAD no momento da abertura.

Chamadores Rust podem invocar o mesmo caminho via turbolite::tiered::settings::set.

Configurações recomendadas

Nota: prefetch é por conexão. Cada nova conexão começa com contadores de miss por árvore frios. O cache é compartilhado, então uma segunda conexão se beneficia das páginas cacheadas pela primeira.

O backend de armazenamento é importante

Cronogramas ótimos de prefetch dependem da compensação latência-largura de banda do seu backend S3. Testamos 10 pares de cronogramas em 6 consultas tanto no S3 Express (~4ms GET) quanto no Tigris (~25ms GET):

No S3 Express, off/off (sem prefetch algum) é surpreendentemente competitivo para consultas pontuais porque cada GET de faixa de sub-chunk é apenas ~4ms. A diferença entre "sem prefetch" e "prefetch ótimo" é pequena (23% para consultas pontuais) porque GETs individuais são baratos. No Tigris, a mesma consulta se beneficia muito mais do prefetch (até 39% no idx-filter) porque cada ida e volta desperdiçada custa 25ms.

O efeito prático: em backends de alta latência, empurre os cronogramas de busca com mais força e mantenha cronogramas de consulta com mais zeros à esquerda. No S3 Express, os padrões funcionam bem e o ajuste proporciona ganhos menores. O desempenho de varredura completa é insensível ao cronograma em ambos os backends porque o query-plan frontrunning faz prefetch em massa de toda a tabela antecipadamente.

Use tiered-tune (veja abaixo) para encontrar cronogramas ótimos para seu backend e consultas específicos.

Ferramenta de ajuste

tiered-tune conecta-se a um banco de dados turbolite existente e varre cronogramas de prefetch contra suas consultas reais. Em vez de adivinhar cronogramas, execute sua carga de trabalho real e deixe a ferramenta encontrar o melhor 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:~
A saída é uma tabela de comparação por consulta (como `tiered-bench --matrix`) mostrando p50, p90, contagem de GET e bytes para cada par de agendamento. A ferramenta recomenda um agendamento e imprime a atribuição `TurboliteConfig` para aplicá-lo.

## Durabilidade

turbolite é uma camada de armazenamento, não um sistema de replicação. A durabilidade depende de quando os dados chegam ao S3.

**Após o checkpoint**: grupos de páginas + manifesto estão no S3. O S3 oferece 11 noves de durabilidade. Esses dados sobrevivem à perda da máquina.

**Entre checkpoints**: as gravações vivem apenas no WAL local no disco local. Se a máquina falhar antes do próximo checkpoint, essas gravações são perdidas.

A frequência do checkpoint controla a troca: checkpoints mais frequentes = janela de dados em risco menor, mas mais PUTs no S3. O padrão é o auto-checkpoint do SQLite (a cada 1000 frames do WAL).

### Modos de checkpoint

turbolite suporta dois modos de checkpoint via `sync_mode` em `TurboliteConfig`:

**`SyncMode::Durable`** (padrão). O checkpoint envia grupos de páginas para o S3 enquanto mantém o bloqueio EXCLUSIVE do SQLite. Simples, totalmente durável em cada checkpoint. Nenhuma gravação ou leitura pode prosseguir até que o envio seja concluído. Bom para a maioria das cargas de trabalho.

**`SyncMode::LocalThenFlush`**. O checkpoint grava apenas no cache do disco local (~1ms de retenção do bloqueio) e, em seguida, libera o bloqueio. O chamador envia para o S3 separadamente via `flush_to_s3()`, durante o qual leituras e gravações continuam normalmente. Isso é útil para cargas de trabalho com muitas gravações onde bloquear leitores durante a duração de um envio para o S3 é inaceitável.

Entre o checkpoint e o flush, os dados existem apenas no cache do disco local. Uma falha de processo está ok (os dados estão no disco local e os logs de staging capturam o conteúdo exato das páginas para envio). A perda da máquina antes do flush significa que essas gravações são perdidas. A remoção de cache é segura: o turbolite protege páginas pendentes da remoção automaticamente.

**Recuperação de falha**: Se o processo falhar entre checkpoint e flush, os logs de staging sobrevivem no disco. Na próxima chamada `TurboliteVfs::new()`, eles são automaticamente recuperados e enfileirados para a próxima chamada `flush_to_s3()`. As leituras são atendidas imediatamente do cache local sem esperar pelo flush.

### Envio de WAL (experimental)

Com a flag de recurso `wal` ativada, o turbolite envia frames do WAL para o S3 via [walrust](https://github.com/russellromney/walrust), fechando a lacuna de durabilidade entre gravações individuais e checkpoints.```toml
# Cargo.toml
turbolite = { version = "0.5", features = ["cloud", "zstd", "wal"] }

⚠️ Requisitos

  • ansible para automações Linux e Mac
  • pwsh para automações Windows e Linux (instale via sudo snap install powershell --classic)
  • python3-venv para ctfcli e seu próprio ambiente virtual

Nota: Temos um canal dedicado em nosso servidor do Discord para ctfcli. Junte-se à nossa comunidade para suporte e dicas!

[!IMPORTANT] Use um ambiente virtual como venv para evitar quebrar os pacotes pip do sistema. Isso evitará conflitos com os pacotes Python de todo o sistema.

Por fim, vale notar que é possível executar o ctfcli no Docker, o que implica alguns requisitos diferentes. Se estiver interessado nisso, consulte a seção sobre Docker específica.

📦 Instalação

Usando pipx

É fortemente recomendado instalar o ctfcli através do pipx. O pipx cria ambientes isolados para aplicações Python, garantindo um espaço de instalação limpo e separado.

Para fazê-lo, basta executar:

root@kitploit:~
pipx install ctfcli

Caso contrário, você também pode instalá-lo via pip, pipsi, ou manualmente a partir do código fonte.

Usando pip

Se você não quiser usar o pipx, pode instalar diretamente com o pip, mas as considerações mencionadas sobre compatibilidade com o restante dos pacotes do sistema ainda se aplicam.

Para pip:

root@kitploit:~
pip3 install ctfcli

Para atualizar:

root@kitploit:~
pip3 install --upgrade ctfcli

🎮 Uso

ctfcli fornece um comando de shell para interagir com implantações do CTFd. Para ver os comandos disponíveis:

root@kitploit:~
ctf

Saída de exemplo:

root@kitploit:~
$ ctf --help
Usage: ctf [OPTIONS] COMMAND [ARGS]...

  CTFd command line tool

Options:
  --version  Show the version and exit.
  --help     Show this message and exit.

Commands:
  challenge  Manage challenges
  config     Manage configuration
  export     Export data from CTFd
  import     Import data into CTFd
  init       Initialize a new CTFd instance
  plugin     Manage plugins
  theme      Manage themes
  user       Manage users

Para definir a URL e os tokens de acesso que a ferramenta de linha de comando usará para se conectar à instância do CTFd, você pode:

  • definir variáveis de ambiente (recomendado) definidas no arquivo .ctf/config
  • usar o arquivo .ctf/config e colocá-lo em seu diretório de trabalho atual ou diretório pessoal

Veja Configuração para mais detalhes.

Cada subcomando aceita uma flag --help para ver seu uso específico:

root@kitploit:~
ctf challenge --help

Para exibir a versão do ctfcli que você tem instalada:

root@kitploit:~
ctf --version

🔐 Autenticação

ctfcli procura por um arquivo de configuração chamado .ctf/config em seu diretório atual ou diretório pessoal. Você também pode definir variáveis de ambiente, que têm precedência sobre o arquivo de configuração.

O arquivo usa o formato YAML e deve conter a URL e o token de acesso de uma instância do CTFd:

root@kitploit:~
url: https://demo.ctfd.io
access_token: "ctfd_xxxxxxx"

Veja Configuração para uma referência completa.

Para obter um token de acesso, você precisa fazer login na sua instância do CTFd e ir para a página de configurações, depois para a seção Tokens de Acesso, e gerar um token com as permissões necessárias. Note que o token deve ter acesso total (admin) se você planeja usar todos os recursos, como desafios e plugins.

🐳 Executando com Docker

Para usuários que preferem ambientes conteinerizados, o ctfcli pode ser executado usando Docker. Fornecemos imagens Docker pré-construídas ou você pode construir a sua própria.

Usando Imagens Pré-Construídas

Imagens Docker oficiais do ctfcli estão disponíveis no GitHub Container Registry (ghcr.io) com as seguintes tags:

Você também pode puxar versões específicas por tag, ex: ghcr.io/ctfd/ctfcli:3.7.3.

Início Rápido com Docker

Execute o ctfcli diretamente usando a imagem Docker:

root@kitploit:~
docker run --rm -v $(pwd):/ctf -v ctfcli-config:/root/.ctf ghcr.io/ctfd/ctfcli:latest

Para conveniência, você pode criar um alias:

root@kitploit:~
alias ctf="docker run --rm -v $(pwd):/ctf -v ctfcli-config:/root/.ctf ghcr.io/ctfd/ctfcli:latest"

Ou criar uma função de shell para montar facilmente o diretório de trabalho:

root@kitploit:~
ctf() {
    docker run --rm -v $(pwd):/ctf -v ctfcli-config:/root/.ctf ghcr.io/ctfd/ctfcli:latest "$@"
}

Construindo a Partir do Código Fonte

Se você preferir construir a imagem Docker você mesmo:

root@kitploit:~
git clone https://github.com/CTFd/ctfcli
cd ctfcli
docker build -t ctfcli .

Estrutura de Diretórios do Docker

Ao executar com Docker, é recomendado montar seu repositório de desafios do CTFd em /ctf dentro do container. É também onde o arquivo .ctf/config deve estar localizado (ou você pode especificar sua localização através da variável de ambiente CTF_CONFIG_PATH).

Usando docker-compose

root@kitploit:~
version: '3'
services:
  ctfcli:
    image: ghcr.io/ctfd/ctfcli:latest
    volumes:
      - .:/ctf              # Monta o diretório atual como diretório de trabalho
      - ctfcli-config:/root/.ctf  # Volume persistente para configuração
    working_dir: /ctf
    # Comando padrão (opcional)
    # command: ctf --help

volumes:
  ctfcli-config:
    # driver: local

Considerações sobre systemd vs Docker```rust

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

root@kitploit:~
turbolite e walrust permanecem sincronizados através do cursor de replay armazenado como `manifest.change_counter`. Caminhos de importação/checkpoint alimentam esse cursor a partir do contador de alterações de arquivo do SQLite; a reprodução direta de páginas pode avançá-lo até a sequência de conjuntos de alterações mais recente confirmada. Na inicialização a frio, o turbolite materializa o banco de dados a partir dos grupos de páginas, então o walrust reproduz segmentos WAL com txid > `change_counter` para recuperar escritas que ocorreram após o último checkpoint.

**Modelo de durabilidade com envio de WAL**: cada transação confirmada é enviada para o S3 como um segmento WAL dentro do intervalo de sincronização (padrão 100ms). Se a máquina falhar, no máximo um intervalo de sincronização de escritas é perdido. Após o checkpoint, segmentos WAL com txid <= `change_counter` são coletados como lixo automaticamente.

O envio de WAL é complementar ao SyncMode: o SyncMode controla como os checkpoints chegam ao S3, o envio de WAL torna as escritas individuais duráveis antes do checkpoint.

### Modelo de consistência

Escritor único, leitores de snapshot. Um processo escreve; os leitores veem o último manifesto confirmado quando abriram. turbolite não é um banco de dados distribuído e não coordena entre múltiplos escritores.

## Modo Local (sem S3)

turbolite também funciona como um VFS puramente local comprimido/criptografado:

- Compressão: zstd (padrão), lz4, snappy, gzip. Com zstd, você pode treinar e embutir dicionários de compressão personalizados e rotacionar automaticamente para uma compressão mais eficiente. Tamanhos de página maiores comprimem melhor. Veja a CLI para ferramentas de treinamento.
- Criptografia: AES-256-GCM por página.

A operação no nível de página significa que a maioria dos recursos do SQLite ainda funciona: FTS, R-tree, JSON, modo WAL. A maioria das outras extensões de compressão/criptografia do SQLite opera no nível de arquivo ou requerem compilações personalizadas.

## Instalação

Este repositório é um workspace Cargo. O crate `turbolite` é a biblioteca Rust pura na raiz do workspace. As ligações de linguagem e a extensão carregável ficam em `turbolite-ffi/`.

**Python**: `pip install turbolite` — veja [turbolite-ffi/packages/python/](https://github.com/russellromney/turbolite/blob/main/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 — veja 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, vincula a biblioteca compartilhada):```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"

O método recomendado turbolite_register_local_file_first(name, db_path, level) é chaveado no caminho do banco de dados voltado para o usuário. O de nível inferior turbolite_register_local(name, cache_dir, level) ainda é exportado para incorporadores que desejam gerenciar o diretório de cache por conta própria. Veja examples/go/ para um exemplo completo de servidor HTTP.

Extensão carregável (qualquer linguagem)

Construa a extensão carregável para qualquer linguagem com o load_extension do SQLite:```bash

after cloning the turbolite repo

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

root@kitploit:~
| Name | Autor | Descrição | Linguagem | Estrelas | Forks | Problemas | Última Atualização | Último Push |
|------|--------|-------------|----------|-------|-------|--------|-------------|--------|
| [oshinguru](https://github.com/ajinabraham/oshinguru) | [ajinabraham](https://github.com/ajinabraham) | Oshinguru é uma ferramenta Python que extrai informações de código JavaScript ofuscado usando análise baseada em padrões e de variáveis. | Python | 44 | 14 | 2 | 2023-05-18 16:42:14 | 2023-05-18 16:43:03 |```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 a história de usuário file-first, registre um VFS por banco de dados que possua o app.db do chamador:```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 o VFS `"turbolite"` padrão para o modo file-first no momento do carregamento da extensão, defina `TURBOLITE_DATABASE_PATH=/data/app.db` no ambiente antes de carregar a extensão. O sidecar é então `/data/app.db-turbolite/` e o parâmetro de nível inferior `TURBOLITE_CACHE_DIR` é ignorado.

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

| mfsidentifier | SERVER_IDENTIFIER | STRING | | | identificador do Metasploit Framework | | mfprotocol | SERVER_PROTOCOL | STRING | | | protocolo do Metasploit Framework (para staging) | | mfstagelesscallback | SERVER_STAGELESS_CALLBACK | STRING | | | callback sem estágio do Metasploit Framework | | mfstagerurl | SERVER_STAGE_URL | STRING | | | URL de estágio do Metasploit Framework (para staging acima de 8k```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` é um banco de dados padrão do better-sqlite3. `connect()` registra um VFS por banco de dados com prioridade de arquivo para você. Para exportar um arquivo SQLite padrão (por exemplo, para inspecionar com o CLI `sqlite3`), use a API de backup do better-sqlite3: `await db.backup('export.sqlite')`. Consulte [turbolite-ffi/packages/node/](https://github.com/russellromney/turbolite/blob/main/turbolite-ffi/packages/node) para documentação completa.

### Rust (local, 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",
)?;

O formato de nível inferior permite que você escolha o diretório de cache diretamente:```rust let config = TurboliteConfig { cache_dir: "/path/to/data".into(), // turbolite owns this dir ..Default::default() };

root@kitploit:~
Nesse caso, a imagem local é `/path/to/data/data.cache` em vez de um `app.db` nomeado pelo chamador. Novos embedders devem preferir a forma arquivo-primeiro.

### 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 é a imagem de página comprimida do turbolite. Não é garantido que seja aberto diretamente pelo sqlite3 padrão. Para um arquivo SQLite normal (por exemplo, para o CLI sqlite3), use a API de backup online do SQLite ou o auxiliar de exportação específico da vinculação (conn.iterdump() em Python, db.backup() em Node).

CLI

O turbolite inclui uma CLI para inspecionar, gerenciar e interagir com bancos de dados turbolite sem escrever 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 os comandos S3 aceitam as flags --bucket, --prefix, --endpoint e --region, ou leem das variáveis de ambiente TURBOLITE_BUCKET, TURBOLITE_PREFIX, AWS_ENDPOINT_URL e AWS_REGION.

Projetos Relacionados e Comparação

Existem muitos projetos no espaço SQLite sobre rede. Turbolite pega emprestado ideias de todos eles.

Requisições de range em arquivos .db brutos (somente leitura)

A abordagem mais comum: colocar um arquivo .db não modificado no S3 ou em uma CDN e emitir HTTP Range GETs quando o SQLite lê uma página.

  • sql.js-httpvfs: O original. SQLite WASM com requisições HTTP Range. Possui cabeças de leitura virtuais com pré-busca exponencial para varreduras. Pioneiro na ideia de que você não precisa baixar o banco de dados inteiro para consultá-lo.
  • sqlite_web_vfs: Extensão VFS nativa em C++ com consolidação adaptativa de requisições e um arquivo de índice .dbi opcional que pré-coleta nós internos da B-tree para pré-busca - a mesma ideia dos pacotes de páginas internas do turbolite. Projetado para compor com sqlite_zstd_vfs.
  • sqlite3vfshttp: VFS Go limpo e minimalista. Construído para consultar SQLite no S3 a partir do Lambda sem baixar o arquivo.
  • sqlite-s3-query: Biblioteca Python usando ctypes para interceptar E/S de arquivo e traduzir leituras para S3 Range GETs. Requer buckets versionados para consistência durante a substituição do banco de dados.
  • sqlite-wasm-http: Sucessor espiritual do sql.js-httpvfs usando a compilação WASM oficial do SQLite. Cache de páginas compartilhado via SharedArrayBuffer. Mantido ativamente.
  • s3sqlite: Python, usa s3fs (FUSE) + APSW. Deixa o FUSE lidar com as requisições de range.

Todos esses são somente leitura e buscam páginas descomprimidas do arquivo bruto. Uma consulta pontual transfere uma página bruta de 4KB (ou 64KB) por requisição.

Replicação em nível de página / sincronização edge

Que tratam o armazenamento de objetos como fonte da verdade e replicam páginas individuais ou conjuntos de alterações, permitindo réplicas parciais e implantações offline-first / edge.

  • Graft (orbitinghail/graft): Um mecanismo de armazenamento transacional para replicação lazy, parcial e fortemente consistente sobre S3. A extensão SQLite libgraft implementa um VFS que lê e escreve páginas de 4KB através de volumes Graft. Usa compressão zstd emoldurada e conjuntos de alterações baseados em splinter. O primo arquitetônico mais próximo do turbolite no espaço de "replicar páginas, não frames WAL", com foco em sincronização edge multi-escritor em vez de latência de leitura a frio.
  • mvsqlite: Páginas armazenadas no FoundationDB como pares KV endereçados por conteúdo. MVCC completo com viagem no tempo para qualquer snapshot, codificação delta XOR+zstd entre versões de página. O mecanismo de armazenamento mais sofisticado neste espaço, mas requer FoundationDB, não S3.

Replicação e backup para S3

Estes replicam escritas locais para S3 para backup ou restauração.

  • Litestream: Envia continuamente frames WAL para S3. O padrão ouro para backup SQLite. Uma versão mais nova do VFS Litestream pode servir leituras do S3 usando requisições Range em arquivos LTX com um cache LRU e índice de páginas — arquitetonicamente a coisa mais próxima do caminho de leitura do turbolite, mas somente leitura e vinculado ao formato de replicação do Litestream.
  • LiteFS: Sistema primário/réplica baseado em FUSE do Fly.io. Captura conjuntos de alterações de páginas e os transmite para réplicas. Resolve disponibilidade, não armazenamento.
  • Verneuil: Divide o banco de dados em pedaços de 64KB com compressão zstd e um arquivo de manifesto, replica de forma assíncrona para o S3. O modelo de pedaço+manifesto se assemelha aos grupos de páginas+manifesto do turbolite, mas Verneuil é uma ferramenta de replicação - você consulta o disco local, não o S3.
  • libSQL/sqld (by Turso): Fork do SQLite com uma interface Virtual WAL. O modo "Bottomless" envia frames WAL para o S3. As consultas são locais; o S3 é para restauração.

Mecanismos de armazenamento personalizados

  • sqlite-s3vfs: Cada página SQLite armazenada como um objeto S3 separado. Permite escritas, mas a um PUT por página, custando $0,02 por 4096 páginas vs $0,000005 do turbolite para o mesmo lote (no padrão de página de 64KB). Veja a tabela de benchmark acima para comparação de latência de consulta a frio; turbolite é 7,5–263× mais rápido no mesmo conjunto de dados.
  • wa-sqlite-s3vfs: Port para TypeScript / navegador do sqlite-s3vfs para wa-sqlite. Mesmo modelo de um-objeto-por-página, adaptado para uso WASM / lado do cliente.

Compressão

  • sqlite_zstd_vfs: Armazena páginas comprimidas como linhas em um banco de dados "wrapper" externo. zstd com treinamento de dicionário. Compõe com sqlite_web_vfs para leituras comprimidas via requisições range sobre HTTP. A combinação de sqlite_web_vfs + sqlite_zstd_vfs é provavelmente a coisa existente mais próxima do caminho de leitura do turbolite, mas é somente leitura e não agrupa páginas em grupos.
  • SQLCipher: Criptografia AES-256 em nível de página para SQLite local. Sem armazenamento remoto.

Onde turbolite difere

Benchmarking

Todos os benchmarks estão em benchmark/. Veja benchmark/README.md para cenários de implantação (local, Fly.io, EC2).

O binário tiered-bench gera um conjunto de dados de mídia social (usuários, postagens, curtidas, amizades) e testa consultas em cada nível de cache contra o S3.

Um harness separado benchmark/bench_s3vfs.py executa as mesmas consultas contra sqlite-s3vfs para comparação direta. Ele é implantado via benchmark/fly-s3vfs.toml e usa o mesmo gerador de conjunto de dados determinístico que o 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 principais: `--sizes` (contagens de linhas), `--ppg` (páginas por grupo), `--prefetch-threads`, `--prefetch-search` (agendamento SEARCH), `--prefetch-lookup` (agendamento de consulta), `--grouping` (posicional ou btree), `--queries` (post/perfil/quem-curtiu/mútuo), `--modes` (nenhum/interior/índice/dados), `--skip-verify` (ignorar COUNT(*) em máquinas pequenas), `--iterations`, `--plan-aware` (ativar pré-busca com antecipação), `--matrix` (pares de agendamento de varredura). Agendamentos por consulta: `--post-prefetch`/`--post-lookup`, `--profile-prefetch`/`--profile-lookup`, etc. (pesquisa e consulta são independentes 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

Testes```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 foi anteriormente nomeado `sqlite-compress-encrypt-vfs`, também conhecido como `sqlces`.

### Detalhes do modelo de segurança

Os dados do S3 usam AES-256-GCM com nonces aleatórios únicos por quadro (autenticado, detecção de adulteração). Arquivos locais usam AES-256-CTR com nonces determinísticos (número da página / deslocamento de byte), fornecendo confidencialidade contra atacantes com acesso ao disco em repouso. Os nonces determinísticos do CTR significam que atacantes com múltiplos snapshots poderiam recuperar o XOR de textos simples em deslocamentos reutilizados, correspondendo à compensação da própria extensão SEE do SQLite. O cache local é efêmero e recriável a partir do S3.

## Licença

Apache-2.0
Baixar ferramenta
ConsultaTipoFrio (S3 Express)Frio (Tigris)
Post + usuáriobusca pontual + join86ms172ms
Perfiljoin multi-tabela (5 JOINs)251ms479ms
Quem curtiubusca em índice + join206ms302ms
Amigos mútuosjoin multi-busca19ms49ms
Filtro indexadovarredura de índice coberto79ms88ms
Escaneamento completo + filtrovarredura completa de tabela476ms532ms
Nível de cacheO que está em cacheO que é buscado do S3Quando isso ocorre
nenhumnadatudoInício limpo, cache vazio
interiorpáginas B-tree interiorespáginas de índice + dadosPrimeira consulta após abrir conexão
índicepáginas interiores + de índiceapenas páginas de dadosOperação normal do turbolite
dadostudonadaEquivalente a SQLite local
OperaçãoSQLiteturboliteSobrecarga
Busca pontual145K/s73K/s2.0x
Varredura de intervalo8.8K/s8.3K/sparidade
Varredura completa de tabela56/s60/sparidade
INSERIR19K/s23K/sparidade
ATUALIZAR por PK40K/s27K/s1.5x
INSERIR em lote (em transação)685K/s740K/sparidade
Restrição do S3Implicação
Viagens de ida e volta são lentasMinimize o número de requisições. Agrupe escritas, pré-carregue leituras agressivamente.
Largura de banda é um gargaloMaximize a utilização da largura de banda.
PUTs e GETs cobram por operaçãoUm GET de 64KB custa o mesmo que um GET de 16MB. Otimize o número de requisições, não a eficiência em bytes.
Objetos são imutáveisNunca atualize no lugar. Escreva novas versões, troque um ponteiro. Sem corrupção por escrita parcial.
Armazenamento é baratoNão otimize por espaço. Superdimensione, mantenha versões antigas, deixe o GC limpar depois.
Carga de trabalhoConfiguraçãoPor que
OLTP mistoPadrõesPlan-aware lida com varreduras, cronograma de busca aquece índices, cronograma de consulta permanece conservador.
Pesado em pontos (bases de agentes)prefetch.lookup: vec![0.0, 0.0, 0.0]Consultas quase nunca precisam de prefetch.
Analítico pesado em varredurasprefetch.search: vec![0.5, 0.5], prefetch.query_plan: trueAquecimento agressivo de busca mais prefetch em massa plan-aware.
Conservador (serverless intermitente)prefetch.search: vec![0.1, 0.2, 0.3], prefetch.lookup: vec![0.0, 0.0, 0.1]Ruído mínimo de prefetch.
BackendLatência GETMelhor consulta pontualMelhor perfilGanho de ajuste
S3 Express~4ms74ms (off/off: 96ms)188ms (off/off: 212ms)5-23% sobre sem prefetch
Tigris~25ms192ms (off/off: 231ms)524ms (off/off: 616ms)8-34% sobre sem prefetch
TagDescriçãoBase
latest, masterÚltima build estável do branch masterpython:3-slim
devBuild de desenvolvimento dos branches de desenvolvimento ativospython:3-slim
alpine, latest-alpineImagem leve baseada no alpinepython:3-alpine
turboliteRange GETs de arquivo brutoLitestream VFSsqlite_web_vfs + zstd_vfsmvsqliteGraftsqlite-s3vfs
Leituras do S3range GETs buscáveis em grupos de páginas comprimidosrange GETs em páginas brutasrange GETs em arquivos LTXrange GETs em banco de dados externo comprimidoconsultas KV no FoundationDBbusca lazy de páginas de 4KB / conjuntos de alteraçõesum GetObject por página
Escritas no S3checkpoint (um PUT por grupo)nãonãonãosim (MVCC)sim (replicação assíncrona de conjunto de alterações)um PUT por página
Compressãozstd multi-quadro buscávelnenhumanenhumazstd (BD aninhado)codificação delta zstdzstd emolduradonenhuma
CriptografiaAES-256-GCM por páginanenhumanenhumanenhumanenhumanenhuma listadanenhuma
Pré-buscalook-ahead + programação de saltonenhuma ou leitura antecipada básicacache LRUconsolidação adaptativabuffers do clientelazy / sob demandanenhuma
Otimização de páginas internasdetectadas, fixadas, agrupadas separadamentenenhumaíndice de páginas de trailers LTXarquivo .dbi opcionalnenhumanenhuma listadanenhuma
Bytes por consulta pontual (cache: índice)~100KB (um quadro comprimido)4-64KB (uma página bruta)variavariavaria4KB (uma página)4KB (uma página)
Custo de escrita por 4096 páginas~$0.000005 (um PUT)n/dn/dn/doperações FoundationDBconjuntos de alterações em lote~$0.02 (4096 PUTs)