Pare de sincronizar todos os dados
(sqlsync.dev)- Graft é um motor de armazenamento transacional open source que busca combinar a simplicidade da replicação física com a eficiência da replicação lógica, em vez de enviar o log completo de mudanças para todos os clientes
- Ele trata Volumes compostos por Pages de tamanho fixo em unidades de Snapshot, e o servidor entrega não os dados em si, mas um bitmap comprimido dos índices de páginas alteradas, chamado
graft - O cliente consulta o
grafte busca apenas as páginas necessárias, podendo escolher entre prefetch baseado em Leap, prefetch específico de domínio ou busca antecipada de todas as mudanças - Usando armazenamento de objetos e servidores de borda, ele busca replicação parcial mesmo em ambientes restritos como navegador, apps móveis, funções serverless e sistemas embarcados
- O modelo de consistência é Serializable Snapshot Isolation; commits baseados em Snapshots antigos são rejeitados, e o cliente lida com isso usando reset/replay, merge ou fork de Volume
O problema de replicação que o Graft quer resolver
- A replicação parcial parece simples quando se pensa em sincronizar apenas os dados necessários, mas na prática cada abordagem de replicação tem custos bem claros
- A replicação lógica rastreia todas as mudanças com precisão, mas torna mais complexa a obtenção de consistência forte
- A replicação física evita essa complexidade, mas exige sincronizar até mudanças que depois serão descartadas
- Graft é um motor de armazenamento transacional open source criado com foco em sincronização sob demanda, replicação parcial, consistência forte, escalabilidade horizontal e durabilidade em armazenamento de objetos
- O ponto de partida veio da experiência com o SQLSync
- SQLSync é uma stack de banco de dados otimizada para frontend construída sobre SQLite, que usa ideias de Git e sistemas distribuídos no mecanismo de sincronização
- Como o SQLSync replica o log completo de mudanças para todos os clientes, ele funciona no servidor, mas não se encaixa bem em ambientes de borda e navegadores
- O objetivo do Graft é permitir que os clientes sincronizem no próprio ritmo, busquem apenas o necessário e repliquem dados arbitrários com consistência forte, inclusive em edge e dispositivos offline
Um desenho entre replicação completa e diff com conhecimento de esquema
- As soluções existentes se dividem em duas grandes linhas
- Replicação completa: sincroniza o dataset inteiro com cada cliente, o que não é prático em ambientes restritos como funções serverless ou apps web
- Diff com conhecimento de esquema: rastreia mudanças lógicas no nível de linha ou campo, como CDC ou CRDT, mas exige integração profunda com a aplicação e é difícil de generalizar para dados arbitrários
- O Graft, assim como a replicação completa, independe de esquema
- Ele não precisa saber nem se importar com o tipo de dado armazenado; replica páginas contendo bytes
- Ao mesmo tempo, como a replicação lógica, ele entrega ao cliente uma descrição comprimida do que mudou desde a última sincronização
- A abstração central é o Volume
- Um Volume é uma coleção esparsa e ordenada de Pages de tamanho fixo
- O cliente lê e escreve no Volume a partir de um Snapshot específico usando uma API transacional
- Internamente, o Graft armazena e replica apenas o necessário, usando armazenamento de objetos como backend durável e escalável
Sincronização sob demanda: alcançar o estado atual quando o cliente quiser
- O Graft foi projetado assumindo clientes de borda que despertam ocasionalmente, operam em redes instáveis e têm pouco tempo de execução
- Em vez de depender de replicação contínua, o cliente escolhe quando sincronizar
- A sincronização começa com a pergunta: “o que mudou desde o último Snapshot?”
- O servidor não envia os dados reais, mas sim o
graft, um bitmap comprimido dos índices das páginas alteradas- O
graftfunciona como um guia para anexar novas mudanças a um Snapshot existente - O cliente consegue saber quais páginas pode reutilizar e quais deverá buscar sob demanda
- O
- Como o
grafté metadado de mudança, não dado em si, o controle sobre o que buscar e quando buscar permanece com o cliente
Replicação parcial e prefetch
- Em abas de navegador, apps móveis e funções serverless, é difícil baixar o dataset inteiro só para atender algumas consultas
- Depois de receber o
graft, o cliente determina quais páginas ainda são válidas e quais precisam ser buscadas - Como apenas as páginas necessárias são buscadas seletivamente, dá para replicar somente os dados que realmente serão usados
- O Graft oferece vários modos de prefetch para reduzir a latência de acesso às páginas
- Prefetch de uso geral: um prefetcher embutido baseado no algoritmo Leap identifica padrões de acesso e prevê acessos futuros a páginas
- Prefetch específico de domínio: a aplicação pode usar conhecimento sobre dados consultados com frequência, como perfis de usuários, para antecipar páginas relacionadas
- Busca antecipada: se necessário, é possível buscar todas as mudanças e voltar, na prática, à replicação completa, algo especialmente útil para cargas de trabalho de Graft no lado do servidor
- Como as páginas ficam hospedadas diretamente no armazenamento de objetos, isso serve de base de replicação com durabilidade e escalabilidade
Implantação em edge e clientes embarcados
- A replicação em edge do Graft não busca apenas decidir quais dados sincronizar, mas também colocar os dados onde eles são necessários
- As páginas são servidas a partir do armazenamento de objetos por meio de uma frota global de servidores de borda
- Hot pages acessadas com frequência podem ser armazenadas em cache perto do cliente
- O objetivo é manter baixa latência e alta responsividade independentemente da localização dos usuários no mundo
- O cliente Graft foi projetado para ser leve e embarcável
- Tem poucas dependências e runtime pequeno
- Pode ser integrado a ambientes como navegador, dispositivos, apps móveis e funções serverless
- Como o cache de edge cria problemas de consistência e tratamento de conflitos, o Graft também oferece um modelo de consistência forte
Modelo de consistência e tratamento de conflitos
- O Graft usa Serializable Snapshot Isolation como modelo de consistência
- O cliente obtém uma visão isolada e consistente dos dados em um Snapshot específico, e leituras podem ocorrer em paralelo sem interferência mútua
- Escritas são serializadas rigidamente, criando uma ordem globalmente consistente para todas as transações
- Por causa da abordagem offline-first e da replicação adiada, um cliente pode tentar fazer commit com base em um Snapshot antigo
- Se esse commit fosse sempre aceito, a strict serializability seria quebrada
- O Graft rejeita esse commit com segurança e deixa o cliente escolher como lidar com a situação
- As três opções mais comuns do cliente são:
- Reset and replay: buscar o Snapshot mais recente, reaplicar as transações locais e tentar de novo
- O estado global dos dados permanece strict serializable
- Localmente, há uma experiência de Optimistic Snapshot Isolation: as leituras veem um Snapshot internamente consistente, mas ele pode ser descartado se o commit for rejeitado
- Merge: mesclar o estado local com o Snapshot mais recente do servidor
- Nesse caso, o modelo global de consistência pode cair para snapshot isolation
- Volume fork: separar permanentemente em um novo Volume
- A serializabilidade global é preservada
- Reset and replay: buscar o Snapshot mais recente, reaplicar as transações locais e tentar de novo
Aplicações que podem ser construídas
- Apps offline-first: em apps que funcionam parcialmente offline, como notas, gerenciamento de tarefas e apps CRUD, o Graft pode cuidar da sincronização
- Em conjunto com um tratador de conflitos, também pode habilitar recursos multiplayer sobre dados arbitrários
- Dados multiplataforma: permite compartilhar dados entre plataformas móveis, dispositivos e web, reduzindo dependência de fornecedor
- Réplicas de leitura sem estado: é possível subir uma réplica do banco sem estado local, buscar os metadados do Snapshot mais recente e executar consultas imediatamente
- Não é necessário baixar todos os dados nem reproduzir logs
- Replicação de dados arbitrários: como o Graft foca na replicação de páginas, ele não interfere no formato interno dos dados
- Pode ser usado com bancos SQLite, modelos de IA, arquivos Parquet, Lance e Geospatial tilesets
Extensão SQLite libgraft
- Hoje, a forma mais simples de usar o Graft é com a extensão nativa de SQLite
libgraft - O
libgraftpode ser usado onde quer que o SQLite rode e replica apenas a parte do banco que o cliente realmente utiliza - Ele implementa a VFS do SQLite para interceptar leituras e escritas no banco
- Fornece a mesma semântica de transação e concorrência que o SQLite oferece em WAL mode
- Os recursos oferecidos incluem:
- Replicação assíncrona com armazenamento de objetos
- Replicação parcial adiada em edge e dispositivos
- Serializable Snapshot Isolation
- Restauração em um ponto no tempo
- A documentação pode ser consultada na documentação de SQLite no GitHub
Participação e planos de serviço gerenciado
- O Graft é desenvolvido abertamente no GitHub
- Issues, discussões e Pull Requests são bem-vindos, e há um guia de contribuição
- Também há canais de contato via Discord e e-mail
- Existe ainda um plano para lançar o Graft Managed Service, com link para lista de espera
Roadmap
- O Graft passou por um ano de pesquisa, várias iterações e uma grande mudança de direção, mas ainda há muito trabalho pela frente
- Entre os itens planejados estão:
- Suporte a WebAssembly: permitir o uso do Graft no navegador, com meta de suportar o build oficial Wasm do SQLite, wa-sqlite e sql.js
- Integração entre Graft e SQLSync: após o suporte a Wasm, a ideia é separar as camadas de mutation, rebase e query subscription do SQLSync e colocá-las sobre um banco replicado com Graft
- Expansão das bibliotecas cliente: interesse em wrappers nativos de cliente Graft para Python, JavaScript, Go e Java
- Escritas de baixa latência: hoje a operação de push fica bloqueada até o commit completo no armazenamento de objetos
- Experimentos com S3 express zone
- Uso de um grupo de consenso durável e de baixa latência na frente do armazenamento de objetos
- Coleta de lixo, checkpointing e compactação: necessários para maximizar o desempenho de consulta, minimizar espaço desperdiçado e permitir exclusão permanente
- Autenticação e autorização: um trabalho amplo, do gerenciamento de contas do serviço até permissões granulares de leitura e escrita por Volume
- Volume forking: o serviço consegue fazer fork zero-copy copiando referências de Segment para um novo Volume, mas o fork local ainda precisa copiar todas as páginas
- Tratamento de conflitos: há planos para estratégias embutidas de resolução e pontos de extensão; a estratégia inicial será mesclar automaticamente transações que não se sobrepõem
Comparação com soluções de replicação para SQLite
- As comparações foram reunidas a partir de documentação e posts de blog, com a ressalva de que podem não estar 100% corretas
-
mvSQLite
- mvSQLite implementa uma camada VFS customizada que armazena páginas do SQLite diretamente no FoundationDB
- Graft e mvSQLite se parecem por permitir fetch sob demanda e visões parciais do banco via versionamento em nível de página
- A diferença está em onde os dados ficam e como as mudanças de página são rastreadas
- O mvSQLite depende de FoundationDB, e todos os nós precisam acessar o cluster diretamente
- O changeset do Graft baseado em Splinter é autocontido, facilita a implantação e dispensa consultas diretas ao FoundationDB para descobrir versões alteradas de páginas
-
Litestream
- Litestream é uma solução de backup em streaming que replica continuamente frames do WAL do SQLite para armazenamento de objetos
- O Graft se integra diretamente ao processo de commit do SQLite por meio de uma VFS customizada, permitindo replicação parcial adiada e escritas distribuídas
- Ambos replicam páginas para armazenamento de objetos e suportam restauração em um ponto no tempo
-
cr-sqlite
- cr-sqlite é uma extensão de SQLite que transforma tabelas em CRDTs para permitir replicação lógica em nível de linha
- Ele oferece resolução automática de conflitos, mas exige conhecimento de esquema e integração no nível da aplicação
- O Graft é independente de esquema e compatível com extensões arbitrárias de SQLite e estruturas de dados customizadas, mas exige que a aplicação trate explicitamente a resolução de conflitos para preservar a serializabilidade global
-
Cloudflare Durable Objects with SQLite Storage
- A combinação de Durable Objects com SQLite permite manter um banco de dados com consistência forte e alta durabilidade na rede de edge da Cloudflare, encapsulado por lógica de negócio
- Internamente, isso se parece com o Litestream ao replicar o WAL do SQLite para armazenamento de objetos e fazer checkpoints periódicos
- O Graft expõe a replicação como recurso de primeira classe e busca eficiência na replicação com edge
-
Cloudflare D1
- Cloudflare D1 é um banco SQLite gerenciado acessado por API HTTP
- O Graft segue um modelo distribuído em que os dados ficam embutidos na aplicação cliente e são replicados diretamente até a edge
-
Turso & libSQL
- Turso oferece banco SQLite gerenciado e réplicas embarcadas por meio do libSQL
- O Graft se diferencia por oferecer replicação parcial e suporte a estruturas de dados arbitrárias sem dependência de esquema
- O serviço backend do Graft opera no nível de página e deixa todo o ciclo de vida da transação nas mãos do cliente
-
rqlite & dqlite
-
Verneuil
- Verneuil replica assincronamente Snapshots do SQLite para réplicas de leitura por meio de armazenamento de objetos, priorizando confiabilidade
- Ele evita explicitamente mecanismos para minimizar atraso de replicação ou maximizar frescor dos dados
- O Graft opera mais como um banco distribuído com múltiplos escritores e enfatiza replicação parcial seletiva em tempo quase real
Ainda não há comentários.