2 pontos por GN⁺ 2024-10-16 | 1 comentários | Compartilhar no WhatsApp
  • Um hub que reúne em um só lugar a documentação de WebAssembly e da API JavaScript para desenvolvedores que querem usar o sqlite3 diretamente em navegadores modernos com suporte a WASM
  • Para uma execução rápida, comece pelo HOWTO em 3 etapas; também é possível conferir os caminhos de instalação e distribuição na documentação de downloads, módulo npm, build e detalhes do Emscripten
  • A documentação da API abrange formas de carregamento, receitas de código cliente, armazenamento persistente, estruturas C na API JS, até a criação de tabelas virtuais e funções com valor de tabela
  • Organiza casos de uso reais como SQLime, Evolu, SQLiteNext, sqlite-wasm-esm e sqlite-wasm-http, junto com projetos predecessores como sql.js, wa-sqlite e absurd-js
  • Conecta materiais sobre SQLite no navegador, OPFS, IndexedDB e WASI, permitindo comparar rapidamente opções de implementação para usar SQLite no ambiente web

Escopo da documentação do sqlite3 WASM

Casos de uso e ecossistema relacionado

  • Projetos de terceiros que usam este projeto

    • SQLime: oferece uma interface de navegador de banco de dados
    • Evolu: plataforma local-first focada em privacidade, facilidade de uso e ausência de dependência de fornecedor
    • SQLiteNext: demo que integra este projeto ao next.js
    • sqlite-wasm-esm: demonstra como usar este projeto no toolchain Vite
    • sqlite-wasm-http: SQLite VFS que fornece acesso somente leitura a bancos de dados servidos diretamente via HTTP
  • Trabalhos anteriores e projetos adjacentes

    • sql.js, de Alon Zakai: é conhecido como o primeiro caso de uso direto do sqlite3 no navegador, exceto pelo WebSQL, e remonta a um commit de 2012
    • wa-sqlite, de Roy Hashimoto: conhecido como a primeira implementação de armazenamento OPFS para bancos de dados sqlite3, também incluindo trabalhos relacionados a SQLite VFS no lado JS
    • absurd-js, de James Long: demonstra uma forma de armazenar bancos de dados sqlite3 dentro de um banco de dados IndexedDB
    • postgres-wasm: executa um servidor de banco de dados Postgres no navegador
    • Jaccwabyt: pequena biblioteca JS para manipular, a partir de código JS, estruturas C hospedadas em WASM; foi criada para apoiar a implementação de OPFS sqlite3_vfs deste projeto
    • CoWasm: “Collaborative WebAssembly for Servers and Browsers”, e inclui um build WASM da aplicação sqlite3 shell no demo app
    • absurder-js: projeto de Nicholas G. Piesco que reimagina o absurd-js, também com um anúncio no SQLite forum
    • sqlite-wasm-rs: fornece bindings Rust para SQLite voltados ao build wasm32-unknown-unknown
  • Documentação externa e artigos

1 comentários

 
GN⁺ 2024-10-16
Opiniões do Hacker News
  • Seria interessante rodar um SQLite em memória no navegador e fazer streaming de cópias do log WAL para o servidor, como no modo usado pelo Litestream e pelos Cloudflare Durable Objects
    Poderia ser com WebSocket, ou aparentemente também com POSTs fetch() intermitentes. Em visitas posteriores, os dados do lado do servidor seriam usados para repopular o SQLite do cliente
    Porém, olhando esta publicação no fórum do SQLite, parece que a build padrão do SQLite WASM não inclui o modo WAL, então talvez seja necessária uma build customizada

    • Há várias camadas de “não funciona assim” aqui
      SQLite em memória não usa WAL, e o Wasm no navegador não oferece coisas como a API de memória compartilhada que o modo WAL do SQLite espera
      O Litestream exige uma configuração de WAL muito precisa; na configuração padrão do SQLite nativo isso acaba coincidindo bem, mas é difícil reproduzir em Wasm
      Os Cloudflare Durable Objects podem até ter sido inspirados pelo Litestream, mas o funcionamento real é bastante diferente, e o mesmo vale para LiteFS ou Turso
      A ideia geral de fazer streaming das alterações do SQLite em si é possível, mas dá bastante trabalho, e o modelo de concorrência do Wasm no navegador torna a implementação complicada
      Escrevi aquela publicação no fórum algum tempo atrás e consegui fazer WAL funcionar em builds de SQLite Wasm do lado do servidor, mas não era uma opção utilizável ou viável no navegador
    • Em agosto de 2022, criei uma prova de conceito parecida com isso e mandei uma DM no Twitter, mas não recebi resposta. O handle é justjs14
      Eu teria que procurar o código da época, mas compilei para Wasm um SQLite com VFS no navegador, abria o DB, fazia alterações e permitia enviar/receber o WAL do servidor. Também era possível sincronizar entre navegadores, manualmente ou via WebRTC, e funcionava até no GitHub Pages se você desse um token do GitHub ao cliente no navegador
      Se houver interesse, posso tentar refazer do zero em uma forma executável. Na época experimentei bastante com essa abordagem e acho que ela pode ser útil pelo menos para algumas aplicações
      Há também uma demo apenas com funcionalidade de pull aqui: https://just.billywhizz.io/sqlite/demo/#https://just.billywhizz.io/sqlite/demo/db/chinook.db
    • Ao contrário de outras ideias aqui, acho que o streaming de cópias do WAL é tecnicamente possível. Mas precisa ser implementado dentro do VFS
      A “memória compartilhada” pode ser SharedArrayBuffer, e se você abrir apenas uma conexão de DB por vez, um buffer comum também serve. Por exemplo, isso pode ser feito dentro de um SharedWorker, que já é algo comum
      A implementação não seria simples, mas é claramente possível. Na prática, o problema maior é que você captura alterações em nível de bloco, não em nível de linha
      Por isso serve para replicação do DB inteiro, mas é difícil fazer sincronização parcial, como compartilhar algumas linhas com outros usuários
      Se você precisa de alterações em nível de linha para sincronização parcial, deve usar triggers ou algo como a extensão de sessão do SQLite: https://sqlite.org/sessionintro.html
      No PowerSync usamos triggers do lado do cliente e, fora o trabalho de manutenção dos triggers, não encontrei grandes desvantagens
    • Você está pensando em algo como https://electric-sql.com/?
    • Não entendi completamente essa ideia de “hidratação”, mas recentemente fiquei interessado em usar Wasm para executar no navegador parte da lógica de backend
      Por exemplo, se a renderização de templates comuns ou alguns endpoints Htmx rodassem em Wasm, mesmo frameworks web comuns do lado do servidor poderiam oferecer funcionalidade offline com um armazenamento SQLite Wasm parecido com o que o OP mencionou
      Exemplo completo em Django: https://github.com/m-butterfield/django_webassembly
      Exemplo mínimo em Rust com macros separando cliente/servidor: https://github.com/richardanaya/wasm-service
      Clojure Electric, que faz algo parecido sem Wasm e com mais macros: https://github.com/hyperfiddle/electric
      Há também uma ferramenta web recente da Microsoft que começa com B
      Nos últimos anos, uma quantidade surpreendente do ecossistema Python ganhou suporte a Wasm e passou a poder ser usada nessa abordagem. Não só bibliotecas obviamente relacionadas a machine learning, mas também bibliotecas de jogos como Pygame e Pyxel; como o SDL2 adicionou suporte, o suporte ao Kivy, uma biblioteca de UI multitoque, também parece estar próximo
      Parece haver muitas novas possibilidades de executar código em qualquer computador e, quando necessário, dentro do sandbox do navegador, evitando burocracias e taxas de plataformas
  • A documentação diz que esses componentes apareceram pela primeira vez na versão 3.40 como beta público e que a API seria estabilizada na 3.41 com base no feedback da comunidade.
    Mas a versão mais recente do SQLite é a 3.46.1, de 13/08/2024, então provavelmente a API já foi estabilizada, mas a documentação não foi atualizada.
    Acho que seria um bom meio de distribuição se a equipe do SQLite lançasse um pacote npm oficial com a versão WASM. Depois vi que, segundo uma resposta, isso já existe.
    Pessoalmente, meu SQLite-in-WASM favorito ainda é a variante do Pyodide. Ela existe há muito mais tempo que a implementação oficial do SQLite, e, ao usar Pyodide, o SQLite em WASM vem junto como parte da biblioteca padrão do Python.
    É assim que ele é usado em https://lite.datasette.io/, e também dá para testar em https://pyodide.org/en/stable/console.html.
    Ao executar select sqlite_version() com sqlite3.connect(':memory:'), aparece a versão 3.39.0, de 25/06/2022, então parece que o Pyodide precisa de uma atualização de versão. Essa versão parece ser herdada do emscripten: https://github.com/emscripten-core/emscripten/blob/main/tools/ports/sqlite3.py

    • A estabilização da API está correta. Quando eu me sentar na frente do computador da próxima vez, vou tentar atualizar essa frase.
      No entanto, a equipe do SQLite não vai lançar um pacote npm oficial. Nós distribuímos apenas JS puro e nos recusamos firmemente a entrar na toca do coelho do suporte a ferramentas fora da linguagem. Nenhum membro do projeto usa essas ferramentas.
      Damos suporte ao build npm “oficialmente aprovado” mantido por Thomas Steiner, mas não miramos ativamente nenhum framework JS para desenvolvimento.
      Dar suporte direto a um framework específico, incluindo npm, pode passar a impressão de endossar esse framework, e nós evitamos ativamente endossar projetos de terceiros.
    • Parece que a equipe do SQLite já faz isso há algum tempo. Em um módulo JS, basta importar @sqlite.org/sqlite-wasm via jsdelivr e carregar sqlite3-bundler-friendly.mjs e sqlite3.wasm.
      Com isso, é possível usar a API C do SQLite e a API orientada a objetos oo1, e funciona tanto com tags de script comuns type=module quanto no Deno.
      Há um exemplo de HTML aqui: https://github.com/Ciantic/experimenting-sqlite-wasm/blob/main/standalone.html
    • https://github.com/sqlite/sqlite-wasm
      O sqlite-wasm carrega muito mais rápido que o Pyodide, então, se você não precisa de Python, o primeiro é uma opção melhor.
    • DuckDB WASM pode ser usado independentemente do Pyodide e também permite extensões do SQLite.
      Porém, parece ser bastante limitado. Como não há sqlite_version(), nem consegui verificar a versão, e a versão no repositório é a 3.38.1, bem antiga.
      Atualmente, o shell web do DuckDB não consegue carregar a extensão do SQLite porque a extensão do SQLite para a 1.1.2, lançada ontem, ainda não foi publicada. A versão anterior funciona com a edição WASM atualizada recentemente.
      Também é possível acoplar extensão espacial, GDAL, busca vetorial etc.: https://github.com/duckdb/duckdb-wasm/releases/tag/v1.29.0
      Criar seu próprio “shell SQL web” também não foi muito difícil, mas a documentação não era completa o suficiente.
      https://github.com/duckdb/sqlite_scanner/blob/main/src/sqlite/sqlite3.h
    • Ler o FAQ dessa página pode ajudar.
  • Ao usar em Go, gosto muito do pacote Wasm SQLite do ncruces: https://github.com/ncruces/go-sqlite3
    O pacote Go do cznic também é excelente, mas esta versão Wasm funciona bem até em ambientes como OpenBSD.

  • WebSQL simplesmente deveria ter sido SQLite, e, se tivesse sido, o ecossistema de apps offline-first e de armazenamento de apps em geral seria muito melhor.
    Será que ainda há possibilidade agora? Em vez de abstrair o SQLite e especificar demais, não daria para especificar a versão da API do SQLite que os navegadores devem oferecer e atualizar essa versão periodicamente?

    • Em navegadores, a abordagem de “atualizar a versão periodicamente” provavelmente causaria bastante problema.
      Um dos pontos centrais da web é que, sempre que possível, o que já existe deve continuar funcionando, e esse tipo de mudança incompatível é difícil.
      Mesmo que a especificação liste algumas versões e permita que a página escolha, os navegadores assumiriam o fardo complicado de manter versões antigas do SQLite que talvez nem sejam mais suportadas. Cada versão também ocupa cerca de 1 MB.
      Nesse caso, acho melhor simplesmente deixar cada site arcar com as consequências da sua própria escolha, como acontece hoje.
      Se a preocupação é baixar repetidamente recursos comuns, esse é um problema que outras CDNs já aceitaram. Uma solução de cache compartilhado para dependências comuns teria muito mais valor do que algo limitado ao SQLite.
      A abordagem atual parece melhor do que uma versão fornecida pelo navegador.
    • Gosto do SQLite, mas é preciso reconhecer que ele tem vários comportamentos peculiares para ser incluído em todos os navegadores. Por isso, algo como uma API parecida com SQLite parece melhor.
    • Acho até bom que o WebSQL não tenha se estabelecido.
      Agora, quando necessário, dá para trazer o SQLite mais recente como um blob WASM comprimido de 410 KB. Se estivéssemos presos à versão do SQLite imposta pelo navegador, talvez hoje estivéssemos usando uma versão de 10 anos atrás.
  • Passando rapidamente pelas páginas, não fica claro para mim quais são os casos de uso
    Mais especificamente: isso pode ser um substituto do IndexedDB? Os dados são persistidos, ou é preciso armazenar diretamente o arquivo SQLite na File System API ou no IndexedDB/localStorage?

    • Segundo a página de introdução, um dos objetivos deste projeto é dar suporte a armazenamento persistente no lado do cliente por meio de APIs JS utilizáveis, dentro do possível
      Atualmente isso inclui armazenamento via Origin-Private FileSystem (OPFS) e, de forma bem limitada, backends window.localStorage e window.sessionStorage
  • Ultimamente tenho me interessado bastante pela área de local-first, mas, comparado a usar as APIs de armazenamento embutidas do navegador, especialmente IndexedDB, embutir SQLite parece bem pesado
    As principais bibliotecas open source também parecem, em geral, usar as APIs nativas
    Em vez de tentar incluir mais um executável via WebAssembly, eu gostaria de ver uma solução open source que oferecesse uma API parecida com SQLite e sincronização em cima do armazenamento nativo do navegador

    • SQL pode ser considerado mais fácil de usar do que a API do IndexedDB e, por exemplo, usar uma cláusula WHERE em vez de um filtro if() também pode consumir menos memória e CPU
    • Talvez você se interesse pelo TinyBase. É uma biblioteca que eu criei
  • Mesmo baixando pela página oficial de downloads e removendo todos os arquivos mjs e os arquivos “bundler-friendly”, a dependência mínima do SQLite Wasm fica em cerca de 1,3 MB
    Para um app de navegador isso parece um pouco grande, mas hoje em dia Wasm também roda em outros lugares, então nesses ambientes pode fazer mais sentido

    • Ainda assim, ele comprime bem. sqlite3.js e o Wasm têm 1,3 MB no original, mas, minificando o JS e comprimindo os dois arquivos com Brotli, dá para chegar a 410 KB
    • Acho que 1,3 MB é um tamanho bastante razoável para uma aplicação web moderna. Especialmente porque deve ficar em cache depois da primeira visita ao site
      Se for só para salvar preferências do usuário, claro que não faz sentido baixar SQLite; mas, se a tarefa se beneficia de um banco de dados completo, não é preciso se preocupar demais com 1 MB e acabar reinventando a roda à toa
      Se o outro comentário estiver certo, na rede nem seriam 1,3 MB para começo de conversa
    • É um fator a considerar junto com o fato de que o navegador já traz IndexedDB embutido
      Ainda assim, em apps de navegador como Figma ou algo parecido com Photoshop, ou apps de machine learning, nos quais o código da aplicação e os dados já são grandes por natureza, 1,3 MB talvez não seja uma sobrecarga adicional significativa
      Também vale considerar que o parsing de Wasm é consideravelmente mais rápido que o de JS. Não consegui encontrar agora a fonte dessa afirmação, mas havia pelo menos um bom texto relacionado
      https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API
    • É preciso pensar no modelo de sandbox do WebAssembly. Em teoria, o programa — aqui, o SQLite — pode rodar em qualquer lugar apropriado para sua execução
      Pode ser local, pode ser em um servidor central, ou pode ser na borda próxima
    • No lado do servidor, é bem provável que seja necessária uma build Wasm do SQLite separada, que trate concorrência e bloqueio de arquivos de forma diferente
      Além disso, WASI ainda está longe de ser uma resposta. A amalgamation do SQLite compila bem para WASI, mas a concorrência continua sendo um problema não resolvido
      Foi preciso criar uma VFS do zero para deixar um driver SQLite baseado em Wasm em condição utilizável
      https://github.com/ncruces/go-sqlite3/blob/main/vfs/README.md
  • Por um tempo tentei fazer isso funcionar no ecossistema Rust, mas os wrappers SQL do blessed.rs, rusqlite e sqlx, ainda não pareciam conseguir aproveitar isso
    Também era difícil encapsular diretamente, porque não encontrei uma forma de fazer o código Wasm do emscripten funcionar bem com wasm32-unknown-unknown
    No fim, é necessário algum tipo de wrapper JS, e então é preciso implementar a interface esperada por esses crates e expô-la em JS. Se isso for resolvido em Rust, vai ficar muito bom

    • Estou trabalhando em algo relacionado. Se tiver interesse ou quiser contribuir, pode participar aqui: https://github.com/rhashimoto/wa-sqlite/discussions/154
      Essencialmente, é preciso trazer uma build emscripten do SQLite para o wasm-bindgen via cabeçalhos extern C, compilar em modo multithread para permitir acesso a shared array buffers e reimplementar a VFS em Rust
      Depois disso, será possível acessar linhas do SQLite como bytes Wasm brutos e criar um wrapper ou integração no estilo Rust-SQLite
      Ainda não haveria conveniências como connection pooling, mas em Wasm é provável que seja melhor usar o DB em modo exclusivo
    • Há aqui algo que fez essa build do SQLite funcionar com o Diesel SQLite em Rust: https://github.com/xmtp/diesel-wasm-sqlite
      Estamos preparando a versão 0.2 para esta semana e vamos aparar algumas arestas. Fora isso, dá para usar o Diesel ORM como se fosse nativo
    • Sobre fazer o código Wasm de emscripten se encaixar bem com wasm32-unknown-unknown, há boas notícias
      Existe um plano para fazer Emscripten e Rust funcionarem bem juntos em Wasm: https://github.com/rustwasm/wasm-bindgen/pull/4014#issuecomment-2356908451
    • Eu também venho esperando por isso há bastante tempo. Há alguns PRs parados há cerca de um ano e vi provas de conceito, mas estavam longe de algo realmente utilizável
  • Criei uma biblioteca SQLite que roda em JVM pura usando o build Wasm do SQLite e o runtime Chicory: https://github.com/dylibso/sqlite-zero
    É mais um experimento do que algo voltado a estar pronto para produção, mas acho que pode ser útil para trazer uma ferramenta SQLite sem dependências para o ecossistema JVM

    • Como fica o acesso ao sistema de arquivos? É WASI?
  • Uma pergunta geral: em que situações é mais vantajoso enviar o DB inteiro e deixar o navegador processar as queries?
    Em outras palavras, quando você usaria isso para melhorar a experiência do usuário em comparação com o modelo tradicional de DB hospedado no servidor?

    • Pessoalmente, estou usando em um site com hospedagem estática, então um banco de dados hospedado no servidor nem era uma opção
      Com o driver adequado, dá para transmitir em streaming os chunks necessários do banco de dados em vez de enviar o banco inteiro: https://github.com/mmomtchev/sqlite-wasm-http
      Dá para fazer busca de texto completo do SQLite sem baixar o banco FTS inteiro. Se o termo de busca for curto o suficiente, você acaba recebendo a maior parte, mas não tudo
    • Quando vi o título deste artigo, o que me veio intuitivamente à cabeça foi algo como a forma como PouchDB e CouchDB interagem em apps offline-first
      https://medium.com/offline-camp/couchdb-pouchdb-and-hoodie-as-a-stack-for-progressive-web-apps-a6078a985f18
    • Para uso offline, pode ser bom ao lidar com grandes volumes de dados
      Pode ir de uma biblioteca de áudio a software de modelagem 3D. Você cria e persiste as alterações localmente e depois sincroniza com o lado do servidor periodicamente ou quando voltar a ficar online
    • Estou criando dashboards que se comunicam diretamente com vários serviços. No meu servidor há apenas uma quantidade mínima de dados para controle de acesso e gerenciamento de tokens, e os dados reais não passam pelo meu servidor
      Esse tipo de app é um bom candidato para um DB embarcado no lado do cliente