- Se uma apresentação OpenDocument (ODP) fosse armazenada em um contêiner SQLite em vez de um arquivo ZIP, seria possível projetar formas mais seguras e rápidas de salvar, abrir e recuperar documentos
- Hoje, o ODP organiza XML e arquivos de imagem dentro de um arquivo ZIP; um arquivo de apresentação de exemplo com 49 slides é composto por 78 itens no total, incluindo
content.xml, styles.xml, meta.xml, settings.xml e imagens
- Nessa estrutura baseada em ZIP, mesmo pequenas alterações tendem a exigir a regravação do arquivo inteiro, o que dificulta atualizações incrementais e aumenta a latência em File/Save e o volume de escrita em SSDs
- Com SQLite, os arquivos podem ser armazenados como linhas de tabela e, indo além, o conteúdo e as versões podem ser separados por slide, permitindo ler apenas o primeiro slide ou salvar apenas os slides modificados
- Não se trata de uma crítica ao OpenDocument nem de uma proposta de mudança no formato em si, mas de um exemplo de como o SQLite pode facilitar salvamento atômico, acessibilidade, controle de versões e recuperação em formatos de arquivo de aplicações
Escopo e objeto do experimento mental
- O foco é o OpenDocument, mais especificamente o formato de apresentação ODP (OpenDocument Presentation)
- O objetivo não é mudar de fato o OpenDocument, mas avaliar o uso do SQLite como contêiner em projetos futuros de formatos de arquivo
- Os benefícios esperados são documentos menores, File/Save mais rápido, inicialização mais rápida, menor uso de memória, versionamento de documentos e melhor experiência do usuário
Estrutura atual de um arquivo ODP
- Um arquivo ODP é um arquivo ZIP contendo arquivos XML e recursos de imagem
- Como exemplo, um arquivo de apresentação sobre SQLite com 49 slides da SouthEast LinuxFest de 2014 tem 78 itens no total na saída de
zip -l
- Os quatro arquivos XML
content.xml, styles.xml, meta.xml e settings.xml definem layout dos slides, conteúdo de texto e estilos
- O arquivo de apresentação armazena 62 imagens como arquivos separados, desde fotos em tela cheia até pequenos ícones
- O arquivo
mimetype contém uma única linha: application/vnd.oasis.opendocument.presentation
- Arquivos OpenDocument de processador de texto e planilha têm estrutura semelhante, mas a análise aqui se concentra no ODP
Limitações do ODP baseado em ZIP
- Um arquivo ZIP se parece mais com um banco de dados chave/valor otimizado para escrever uma vez e ler muitas vezes, adequado a um pequeno número de chaves com grandes valores BLOB
- Como é difícil atualizar itens individuais, quando o usuário escolhe
File/Save, normalmente o arquivo ZIP inteiro é recriado
- É possível atualizar itens individuais sem corromper o documento inteiro em caso de queda de energia ou travamento, mas isso é difícil o bastante para quase nunca ser usado na prática
- Em uma apresentação de 50 MB, mudar um único caractere pode significar reescrever os 50 MB completos
- O tempo de inicialização pode ficar mais lento
- ODP armazena o conteúdo de todos os slides em um único arquivo XML grande,
content.xml
- O LibreOffice lê e faz o parse desse arquivo inteiro para exibir o primeiro slide
- As imagens também parecem ser todas carregadas na memória e, por isso, ao dar duplo clique no arquivo aparece uma barra de progresso em vez do primeiro slide
- O uso de memória aumenta
- A estrutura ZIP incentiva implementações que leem o documento inteiro para a memória ao abrir, fazem a edição na memória e gravam tudo de volta no disco ao salvar
- Um arquivo de apresentação de 50 MB pode consumir mais de 200 MB de RAM
- Se várias apresentações estiverem abertas ao mesmo tempo junto com navegador e apps de desktop, pode ocorrer swapping
- A recuperação após travamentos fica mais trabalhosa
- Aplicativos da família OpenOffice fazem backup periódico do documento em memória para se proteger contra falhas
- Durante o backup, o app pode congelar por alguns segundos, e após reiniciar é preciso passar por uma caixa de diálogo separada de recuperação
- A acessibilidade ao conteúdo é baixa
- Dá para extrair imagens com ferramentas ZIP, mas extrair ou editar o texto dos slides com ferramentas comuns é difícil
- No arquivo de exemplo,
content.xml tem a declaração XML na primeira linha e 211.792 caracteres de XML em uma única segunda linha
Primeira melhoria: substituir ZIP por SQLite
- O primeiro passo é uma estrutura simples que troca entradas ZIP por linhas em uma tabela SQLite
CREATE TABLE OpenDocTree(
filename TEXT PRIMARY KEY,
filesize BIGINT,
content BLOB
);
- Nesta etapa, o restante da estrutura do formato de arquivo não muda
- Continua sendo uma estrutura de “monte de arquivos”, mas cada arquivo vira uma linha do banco SQLite em vez de uma entrada ZIP
- A comparação de tamanho entre um arquivo SQLite reempacotado com o mesmo conteúdo de
self2014.odp gerado pelo NeoOffice usando o utilitário SQLAR é a seguinte
self2014.odp: 10.514.994 bytes
self2014.sqlar: 10.464.256 bytes
zip.odp, recompresso com o zip de linha de comando: 10.416.644 bytes
- O arquivo SQLite ficou cerca de 0,5% menor que o ODP gerado pelo NeoOffice
- Um arquivo ZIP bem comprimido com o
zip de linha de comando ficou novamente cerca de 0,5% menor que o SQLite
- Em termos de tamanho, o banco SQLite é competitivo com um arquivo ZIP
- O SQLite fornece escrita atômica, então é possível salvar mudanças incrementais sem risco de corromper o documento em caso de travamento ou queda de energia
- A limitação de ainda precisar regravar todo o
content.xml permanece
- Mesmo assim, os outros 77 arquivos podem ser mantidos intactos, acelerando o File/Save e reduzindo a escrita no SSD
Segunda melhoria: dividir o conteúdo em partes menores
- Como o SQLite consegue armazenar com eficiência tanto blocos grandes quanto muitos blocos pequenos, é possível usar uma tabela de conteúdo por slide
CREATE TABLE slide(
pageNumber INTEGER,
slideContent TEXT
);
CREATE INDEX slide_pgnum ON slide(pageNumber);
- Para mostrar a primeira tela, a aplicação precisaria ler apenas o primeiro slide
SELECT slideContent FROM slide WHERE pageNumber=1;
- Com essa estrutura, dá para buscar, fazer o parse e exibir rapidamente apenas o conteúdo do primeiro slide, sem precisar ler todo o
content.xml na inicialização
- As opções de implementação também aumentam
- Depois de exibir o primeiro slide, o restante das páginas pode ser lido em uma thread em background
- Só o slide atual pode permanecer na memória
- Ou apenas o slide atual e o próximo, para permitir transições rápidas
- O salvamento também fica mais rápido, já que basta regravar apenas as páginas alteradas
- Em trechos curtos de texto, a eficiência de compressão pode cair e o documento pode ficar maior
- Ainda assim, como a maior parte do espaço do documento costuma ser ocupada por imagens, essa perda de compressão no texto pode ser um custo pequeno diante da melhora na experiência do usuário
Terceira melhoria: controle de versões
- Se os slides forem armazenados como objetos individuais, o próprio documento pode conter um histórico de versões
CREATE TABLE slide(
slideId INTEGER PRIMARY KEY,
derivedFrom INTEGER REFERENCES slide,
content TEXT
);
CREATE TABLE version(
versionId INTEGER PRIMARY KEY,
priorVersion INTEGER REFERENCES version,
checkinTime DATETIME,
comment TEXT,
manifest TEXT
);
- Cada slide passa a ter um
slideId exclusivo em vez de um número de página, e a ordem é determinada por uma lista de slideId armazenada em manifest, na tabela version
- Ao iniciar, a aplicação primeiro escolhe qual versão exibir e normalmente pode pegar a versão mais recente
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
- Também é possível consultar a versão mais recente com base em
checkinTime
SELECT manifest, versionId, max(checkinTime) FROM version;
- No SQLite, essa consulta com
max(checkinTime) retorna um resultado definido, mas em muitos outros bancos SQL ela retornaria um resultado indefinido ou geraria erro
- Quando o usuário executa
File/Save, apenas os slides modificados precisam ser inseridos como novas linhas na tabela slide, junto com uma nova linha em version contendo o manifest atualizado
- A tabela
version registra horário do check-in, comentário do usuário e versão-pai, preservando o histórico de mudanças
- Também seria possível guardar várias apresentações diferentes dentro do mesmo documento
- Em vez de um arquivo de backup separado, uma versão especial
pending permitiria registrar em silêncio e com frequência as alterações ainda não salvas
- Como só as diferenças são gravadas, o trabalho deixa de ser escrever vários MB e passa a ser escrever apenas alguns KB
- O tempo de salvamento pode cair de segundos para milissegundos
- Mesmo após reiniciar depois de um travamento, a maior parte ou quase todo o trabalho do usuário pode ser preservada
- Se o usuário quiser descartar alterações não salvas, basta voltar para uma versão anterior
Outros recursos possíveis em um formato de arquivo SQLite
- Um contêiner SQLite pode adicionar recursos importantes a um formato de arquivo de aplicação com apenas três tabelas
- Além disso, schema, índices, triggers, views e constraints podem ser usados para melhorar desempenho, conveniência e consistência
- Algumas ideias de extensão são
- Armazenar uma pilha automática de undo/redo nas tabelas do banco para permitir desfazer até sessões de edição anteriores
- Adicionar busca full-text a um deck de slides ou a vários decks
- Decompor
settings.xml em tabelas SQL para facilitar visualização e edição por outras aplicações
- Separar as notas do apresentador de cada slide em uma tabela própria, facilitando o acesso por apps ou scripts de terceiros
- Ir além da sequência linear simples de slides e suportar estruturas de apresentação com caminhos alternativos e desvios conforme a reação do público
Resistência comum ao SQLite e contrapontos
- Por causa da experiência com bancos SQL corporativos, pode haver resistência a usar SQLite como formato de arquivo de aplicação
- Muitos bancos corporativos recomendam não colocar strings grandes ou BLOBs no banco e armazená-los em arquivos separados, mas com o SQLite é diferente
- Qualquer coluna do SQLite pode armazenar strings ou BLOBs de até cerca de 1 GB
- Para strings e BLOBs de até 100 KB, o desempenho de I/O é melhor do que com arquivos separados
- Outra limitação mental comum é a ideia de que todo schema SQL precisa estar em terceira forma normal (3NF) e armazenar apenas tipos primitivos pequenos
- A teoria relacional é importante, mas em um formato de arquivo real também pode ser perfeitamente aceitável armazenar informações complexas como XML ou JSON em campos de texto
SQLite como formato de arquivo de aplicação
- Um arquivo de banco SQLite tem praticamente o mesmo tamanho que um arquivo ZIP com a mesma informação e, em alguns casos, pode ser até menor
- Graças às atualizações atômicas, pequenas mudanças podem ser gravadas com segurança no documento, reduzindo I/O de disco e melhorando o desempenho de File/Save
- A aplicação pode ler apenas o conteúdo necessário para a primeira tela, reduzindo o tempo de inicialização
- É possível manter na memória só o conteúdo relacionado ao que está sendo exibido e deixar o restante no disco, reduzindo bastante o uso de memória
- Um schema SQL pode representar as informações de forma mais direta e concisa do que uma estrutura chave/valor como ZIP
- Isso melhora a acessibilidade para apps e scripts de terceiros
- Também facilita implementar recursos avançados como versionamento embutido do documento e recuperação do trabalho após travamentos
- O OpenDocument já é um formato estabelecido e bem projetado, e como o SQLite surgiu depois dele, isto não é uma crítica às escolhas originais
- O documento Application File Format traz outras ideias sobre o uso do SQLite como formato de arquivo de aplicação
1 comentários
Opiniões no Hacker News
Estou criando um app que usa SQLite como formato de arquivo
Como quero manter o fluxo comum em que o arquivo só muda quando o usuário edita o documento e salva, ao abrir o arquivo eu o copio para um banco de dados
:memory:: https://www.sqlite.org/inmemorydb.htmlO usuário manipula à vontade, e o app reflete diretamente no formato do banco de dados, sem um modelo de documento separado. Ao salvar, escrevo de volta no arquivo de banco de dados com
VACUUM: https://www.sqlite.org/lang_vacuum.htmlFunciona bem para arquivos de tamanho razoável e, no meu app, eles estão sempre dentro desse intervalo
Seria melhor salvar automaticamente direto no banco de dados e remover o botão de salvar. É resistente a falhas, há apenas um banco de dados, o que reduz código e bugs, e uma escrita no SQLite ou tem sucesso ou falha, sem estado intermediário. Por outro lado, como citado no documento,
VACUUM INTOpode deixar o banco de dados de saída incompleto ou corrompido em caso de encerramento inesperado ou perda de energiaSe você usar SQLite da forma como ele foi originalmente pensado, não precisará se preocupar com isso durante toda a vida útil do SQLite
É muito melhor salvar após cada operação em um local temporário, por exemplo algo como
~/.local/share/application/yourappsegundo os diretórios XDG, e, quando o usuário clicar em salvar, copiar o arquivo para o local desejado. Mesmo ao reabrir o app depois de uma perda de energia, ele se recupera quase no mesmo ponto, e você pode perder só os últimos poucos segundosQuando o usuário salvar, você executa um checkpoint para mesclar o conteúdo do WAL ao banco de dados principal
VACUUMcopia o conteúdo para um arquivo temporário de banco de dados e depois sobrescreve o original; ao sobrescrever, usa um rollback journal ou WAL como em uma transação normal. Por isso, precisa de espaço livre de até cerca de duas vezes o tamanho do originalVACUUM INTOusa o arquivo especificado emINTOem vez de um banco de dados temporário, e omite a etapa de copiar de volta por cima do original. É importante saber se o que está sendo usado de fato éVACUUM, que resiste até a quedas de energia, ouVACUUM INTO, que parece vulnerável a queda de energia durante a escrita e pode corromper um arquivo existente se o nome for o mesmoO problema do SQLite é que ele não é um formato de arquivo padronizado
Ele é bem documentado e amplamente compreendido, mas nenhuma norma ISO define em detalhes como interpretar arquivos SQLite. O mesmo vale para implementações alternativas
Zip e XML têm uma superfície de API muito menor do que SQLite. A API do SQLite vai além de algumas funções em C: é a própria linguagem SQL, e implementar sem corrupção de dados um parser SQL, um otimizador de consultas, um compilador, uma máquina virtual de bytecode, um mecanismo de busca de texto completo etc. é algo muito maior do que um parser XML
Para um app fechado, específico de domínio, em que interoperabilidade ou padronização ISO não importam, SQLite é um bom formato de arquivo, mas entendo que essas preocupações realmente existiam para o OpenOffice
A biblioteca C do SQLite também é de domínio público, com o código totalmente aberto, lida com o formato de arquivo e tem um nível de documentação melhor que o de boa parte das normas ISO. Também há bindings para praticamente todas as linguagens principais
Se o problema for que algum formato OpenDocument a ser armazenado dentro de um arquivo SQLite ainda precisa ser criado e documentado, aí é outra questão. Normas ISO são boas, mas, se tivéssemos de esperar a ISO definir formatos de arquivo, haveria pouquíssimas coisas que poderíamos usar
É como não ser necessário implementar todos os recursos de planilhas para ler uma planilha do LibreOffice. O que você precisa é da capacidade de reconstruir as tabelas; depois disso, pode percorrer os dados com código imperativo escrito na linguagem que preferir para obter as informações desejadas
Reimplementar essa biblioteca seria um trabalhão, mas é o mesmo tipo de trabalho que reimplementar código que usa o formato de arquivo OpenDocument. O formato de arquivo em si é bem simples
Se a compatibilidade preocupa, dá para fazer o documento também ser acessível por outros bancos de dados, como MySQL
Achei que a função de salvar arquivos melhoraria bastante se o Audacity adotasse SQLite, mas na prática havia muitas armadilhas
No Linux, ao salvar como um novo arquivo em uma montagem NTFS criada via
/etc/fstab, pertencente ao root mas gravável por todos, ele falhava por motivos como erro de permissão; ao salvar em um arquivo existente, funcionava normalmenteNo momento em que você edita um projeto, o arquivo em disco é modificado; então, se colocar um projeto do Audacity no Git como um bloco binário, surgem diffs desnecessários no Git. Mesmo salvando, dados antigos ou excluídos permanecem no arquivo SQLite até a janela do projeto ser fechada, então podem entrar no repositório se você não fechar a janela antes de fazer commit. Pelo que lembro, antes era preciso executar
VACUUMdiretamente no arquivo.aup3, mas hoje basta fechar a janela. Fica com um ar de Fast Save do Word 2003Quando um projeto que deveria ter algumas centenas de MB virava vários GB e eu precisava economizar espaço em disco, Mix and Render era a solução para trabalhos simples de uma única faixa. Ele não alterava o áudio, mas permitia limpar os resíduos ao salvar e sair
Isso claramente não é um problema do SQLite em si, e sim da camada da aplicação. Acho que o Audacity 2 tinha o conceito de área de trabalho temporária, enquanto o Audacity 3 parece usar o próprio arquivo
.aup3como área de trabalhoDei uma olhada no formato do Audacity 3 e achei muito estranho: os dados do projeto que antes correspondiam ao arquivo
.aupsão armazenados em XML em uma tabela de uma única linha, mas em vez de gravar o texto diretamente, eles são codificados com um codificador de dicionário simples. Isso torna a interoperabilidade e a inspeção muito mais difíceis, prejudica um pouquinho o desempenho e a economia de espaço provavelmente é só um erro de arredondamento de alguns KB diante de arquivos de áudio de centenas de MBDo ponto de vista do Git, é melhor usar um formato de texto que facilite diffs e merges. Não sei o quanto um dump do SQLite é fácil nesse aspecto
Se for importante, dá para consertar manualmente, mas em geral é só descartar o arquivo que volta a funcionar
Bom texto. Ainda assim, gosto do fato de o OpenDocument ser um conjunto de arquivos XML dentro de um arquivo Zip
Mesmo sem uma biblioteca pesada que entenda o formato de documentos, é relativamente fácil gerar documentos como planilhas
Às vezes, usuários de serviços web querem usar em várias ferramentas os dados que exportam como linhas de tabela. CSV em UTF-8 é aberto, convencional e utilizável, mas qualquer um que já tenha oferecido CSV a usuários finais conhece a dor de vê-los esbarrar em aplicativos de planilha
Salvei uma planilha de exemplo como ODS do OpenDocument e como XLSX, o monstro XML da Microsoft, o OOXML, e então entendi apenas o básico do formato XML. Reduzi o arquivo Zip aos elementos obrigatórios, marquei os lugares onde o conteúdo entraria e, sob demanda, gero um novo arquivo de planilha. Agora posso emitir os mesmos dados em CSV, ODS, XLSX e JSON
Também seria possível com SQLite, mas seria um pouco mais complexo e tornaria o desenvolvimento mais lento. Poder criar um documento-modelo em uma suíte de escritório e fuçar no XML do arquivo salvo é um recurso de nicho, mas bom
O Excel em locales como
nl_NL, em especial, é problemático porque se comporta como se o separador de colunas em arquivos CSV estivesse hardcoded como ponto e vírgula. Isso acontece porque a Microsoft decidiu, de forma infame, que holandeses não usam vírgulas em arquivos de comma separated valueslocaleconv()->decimal_point. Se o valor for,, o Excel usa ponto e vírgula tanto em arquivos CSV quanto na linguagem de expressão de fórmulasAntigamente era possível configurar isso ao abrir CSV/TXT no Excel, e no LibreOffice ainda é possível, mas no processo geral de simplificação da UI foi movido para algum lugar no menu/faixa
Data. É preciso abrir uma nova pasta de trabalho e encontrar as opções corretas; para economizar tempo, é melhor usar o LibreOfficeEsta parte foi realmente surpreendente. É difícil acreditar que não seja necessária uma consulta aninhada
SELECT manifest, versionId, max(checkinTime) FROM version;Dizem que, no SQLite, essa segunda consulta usando
max(checkinTime)de fato funciona bem e retorna uma resposta definida. Outros motores de banco de dados SQL retornariam uma resposta indefinida ou dariam erro, mas no SQLite ela retorna omanifeste oversionIddo item com o maiorcheckinTimeNesse caso, não é necessária uma consulta aninhada; basta ordenar por
checkinTimee limitar a um:select manifest, versionId, checkinTime from version order by checkinTime desc limit 1Pelo menos no SQLite e no PostgreSQL isso deve funcionar. No Oracle, pelo que lembro, era preciso usar
where rownum=1, então uma consulta aninhada era necessáriaGROUP BY manifest, versionId ORDER BY 3 DESC LIMIT 1, ou de uma CTE que obtém o maiorcheckinTimee depois faz um joinPorém, se houver várias linhas com o mesmo maior
checkinTime, há aleatoriedade e isso pode ser uma arma apontada para o próprio pé; por isso não costumo usar muito esse recurso específico do SQLite3. Para escolher deterministicamente a melhor linha, é preciso um método explícito parecido com o acimaNo Postgres, dá para fazer algo semelhante com uma consulta
DISTINCT ON. É uma daquelas tarefas que parecem simples em SQL, mas que achei das mais difíceismanifesteversionIdnão são funcionalmente dependentes demax(checkinTime)Por exemplo, pode haver duas linhas com o mesmo valor de
checkinTime, e esse valor pode ser o máximoJá lancei um produto que usava tanto SQLite quanto arquivos XML
Uma das melhorias foi mover algumas tabelas com poucos dados para arquivos XML. Como os arquivos eram pequenos e quase não eram usados, isso simplificou a camada de acesso a dados e o diagnóstico, e os transformamos em XML com indentação por tabulação em várias linhas
Pedir ao responsável técnico que precisava diagnosticar o produto para abrir um banco de dados SQLite era um grande obstáculo. Mas, nas partes principais do produto, o SQLite era esmagadoramente melhor que arquivos XML. A versão anterior usava arquivos XML, mas eles não tinham um bom método de atualização incremental, o que causava problemas de escalabilidade
A vantagem do XML de ser um formato legível por humanos só funciona bem quando o arquivo é pequeno e o design do esquema foi adaptado para um XML legível. A necessidade de reescrever o arquivo XML inteiro a cada vez e a complexidade que surge com o aumento de funcionalidades rapidamente corroem a maior vantagem do XML
Casos em que usuários comuns precisam mexer diretamente no interior de documentos de escritório são raros o bastante para que aprender a usar um leitor de SQLite seja uma barreira de entrada aceitável. As limitações de XML+Zip para escritas arbitrárias no meio do arquivo não podem ser superadas nem pela Lei de Moore
TEXTouBLOBdo SQLite são comprimidos, ou se a suposição é que o chamador comprima o BLOB antes de gravá-loO ODT foi projetado tendo a padronização em mente. O formato anterior também era muito parecido, mas dependia fortemente de padrões existentes como XHTML, SVG e CSS
Se não for possível referenciar padrões existentes, a própria especificação do ODT de repente ficaria enorme. O esforço para atualizar o padrão também parece considerável, e houve pouco progresso nos últimos anos
Na prática, o formato SQLite até poderia ser oferecido como opção, mas o barco dos formatos de documentos de escritório parece já ter partido. Ainda assim, isso dá bons argumentos para a ideia de organizar a especificação do SQLite como um padrão oficial
Fora algumas falhas — por exemplo, o problema de explosão de estilos locais e intervalos de texto por causa do atributo
ooo:rsid, planilhas não esparsas e o mecanismo estranho de estilização de tabelas —, é uma marcação muito bem projetada para esse tipo de dado documental. Há um bom equilíbrio entre marcação semântica e a apresentação que os usuários realmente querem criarJá o Office OpenXML tem tags vazias de formatação com estado e, no DOCX, elas alternam se o texto seguinte será exibido em negrito
Integrar um formato de arquivo ao SQLite me parece meio errado
O SQLite é bom, mas é bastante singular nessa área, porque faz muita coisa e por isso é difícil replicá-lo tal como é
Mas, neste caso, não é como se tantos recursos fossem necessários. Bastariam semântica básica e segura de transações e a capacidade de armazenar uma estrutura simples de tabelas; não é preciso todo o padrão SQL nem um otimizador de consultas
Pode haver um formato de arquivo melhor, mas eu preferiria que fosse um formato separado do SQLite
O tamanho é menor que 1 MB e https://sqlite.org/footprint.html, mesmo com todos os recursos ativados, são 750 KB: https://www.sqlite.org/about.html
Dá para remover bastante funcionalidade na compilação, e também parece haver opções para ajustar ou reduzir o planejador de consultas: https://www.sqlite.org/compile.html
Além disso, há a frase: “SQLite não compete com bancos de dados cliente/servidor. SQLite compete com
fopen()”: https://www.sqlite.org/whentouse.htmlNo fim, o que é necessário não é o banco de dados em si, mas uma biblioteca que forneça a API e o comportamento de um banco de dados
SQLITE_BUSYera bem complicadoEu sei que falhas de serialização são esperadas no processamento de transações, mas no SQLite era difícil distinguir falhas persistentes, como uma espécie de autodeadlock, de problemas temporários de atualização simultânea. Se for uma falha temporária, basta executar de novo a closure que define o trabalho da transação; se for persistente, não faz sentido
Parte do problema é que
sqlite3_stmtcombina tanto a natureza de uma instrução preparada quanto a de um conjunto de resultados. Você acaba mantendo isso por bastante tempo para cachear o bytecode compilado e, se parar no meio da iteração, pode estar segurando um bloqueio naquele momento. Isso pode causar falhas inesperadas de upgrade de bloqueioNo fim, eliminei o problema criando relatórios de erro detalhados com
sqlite3_next_stmt,sqlite3_stmt_busyesqlite3_sql. Mesmo sendo para uso pessoal, o código de retry de transações estava cheio de logging opcional e comentários. A lógica de retry de transações para PostgreSQL foi muito mais fácilOutra coisa que me surpreendeu foi a documentação dizendo que, no modo WAL com
synchronous=NORMAL, uma transação commitada pode ser revertida após perda de energia ou queda do sistema: https://sqlite.org/pragma.html#pragma_synchronousIsso não era relevante para a minha aplicação
Se Richard Hipp e a empresa mostrarem um padrão SQLite ISO/IEC/ANSI/ETSI do qual nunca se afastarão, uma análise jurídica de que não há patentes que possam afetá-lo e várias implementações SQLite compatíveis que preservem todos os benefícios, aí poderemos discutir recomendá-lo como formato de arquivo. Caso contrário, é pedir para criar uma forte dependência de uma implementação de fonte única e repassar isso aos usuários
XML, ASN.1 e JFIF são padrões formais, e o ZIP também é um padrão formal adotado como ISO/IEC 21320-1:2015 durante o processo de padronização do OpenDocument
O mais importante em documentos é que todos os outros consigam lê-los. Reduzir o tempo de atualização em disco é secundário. Não podemos deixar de aprender com o que a Microsoft fez ao distorcer órgãos de padronização para manter dependência: https://arstechnica.com/uncategorized/2008/10/norwegian-standards-body-implodes-over-ooxml-controversy/
Não pode ser uma escolha tão errada assim
Outro exemplo são tiles de mapa raster. Na prática, são pequenas imagens quadradas que podem chegar a milhões
Testei Zip, tar, sistema de arquivos e SQLite, e o SQLite foi o mais rápido e o menor, melhor até do que arquivos comuns sem overhead
O SQLite tem uma grande desvantagem. Um BLOB obtido do banco de dados não pode ser acessado com
mmap, então precisa ser copiado para outro lugar. Um arquivo Zip, se não estiver comprimido ou estiver comprimido com alguma codificação peculiar como PVRTC, pode ser acessado diretamente commmapO OpenDocument é composto por XML e imagens compactadas. No fim, isso significa analisar o formato inteiro e carregá-lo na memória
Não entendo muito bem como o SQLite melhoraria isso. XML não é ideal, mas como está compactado em Zip, a penalidade de tamanho também não é grande
Todas as vantagens listadas no artigo sobre SQLite poderiam ser implementadas usando SQLite como o modelo de runtime do documento. Isso seria possível tanto em disco quanto em memória, mas o SQLite não precisa ser o formato de transmissão
Na verdade, o SQLite pode ficar maior do que o formato atual. Após alterações, podem surgir espaços não utilizados, e ele pode ficar fragmentado e esparso. Se for preciso otimizar toda vez, vantagens como salvamento rápido também desaparecem
Formatos que precisam de atualizações delta e consultas rápidas por índice, sem carregar o arquivo inteiro na memória, de fato costumam usar bastante SQLite como formato de arquivo. Mas sinto que o OpenDocument foi um exemplo ruim para escolher como alvo do SQLite nesse cenário hipotético
Se usar SQLite como formato em disco e implementar a aplicação corretamente, talvez não se termine em um estado corrompido
XML/Zip também conseguem algo parecido com um truque de renomeação, mas o SQLite oferece isso em um único arquivo em disco. Se você já usa SQLite como modelo em memória, não há motivo para não usá-lo também como formato em disco/de transmissão. Nesse ponto, é praticamente de graça
A questão do tamanho do arquivo provavelmente pode ser tratada com
VACUUM