- Si OpenDocument Presentation (ODP) se guardara en un contenedor SQLite en vez de un archivo ZIP, se podría diseñar de forma más segura y rápida la manera de guardar, iniciar y recuperar documentos
- Actualmente, ODP tiene una estructura que agrupa XML e imágenes en un archivo ZIP; un archivo de presentación de ejemplo con 49 diapositivas está compuesto por 78 elementos en total, incluidos
content.xml, styles.xml, meta.xml, settings.xml e imágenes
- En una estructura basada en ZIP, incluso un cambio pequeño suele requerir reescribir todo el archivo, lo que dificulta las actualizaciones incrementales y deriva en demoras en File/Save y más escrituras en SSD
- Al cambiar a SQLite, los archivos pueden almacenarse como filas de tablas; más aún, sería posible separar el contenido y las versiones por diapositiva, leyendo solo la primera diapositiva o guardando solo las diapositivas modificadas
- No es una crítica ni una propuesta para cambiar OpenDocument en sí, sino un caso que muestra cómo SQLite puede facilitar el guardado atómico, la accesibilidad, el control de versiones y la recuperación en formatos de archivo de aplicaciones
Alcance y objeto del experimento mental
- El objeto es ODP (OpenDocument Presentation), el formato de documentos de presentación dentro de OpenDocument
- El propósito no es cambiar OpenDocument en la práctica, sino evaluar el uso de SQLite como contenedor en el diseño de futuros formatos de archivo
- Los beneficios esperados son documentos más pequeños, File/Save más rápido, inicio más rápido, menor uso de memoria, control de versiones de documentos y una mejor experiencia de usuario
Estructura actual de un archivo ODP
- Un archivo ODP es un archivo ZIP que contiene archivos XML y recursos de imagen
- Como ejemplo, el archivo de presentación de 49 diapositivas sobre SQLite de SouthEast LinuxFest 2014 tiene 78 elementos en total en la salida de
zip -l
- Cuatro archivos XML —
content.xml, styles.xml, meta.xml, settings.xml— definen el diseño de las diapositivas, el contenido de texto y los estilos
- En la presentación se almacenan como archivos separados 62 imágenes, desde fotos de pantalla completa hasta íconos pequeños
- El archivo
mimetype contiene una línea: application/vnd.oasis.opendocument.presentation
- Los archivos de procesador de texto y hojas de cálculo de OpenDocument tienen una estructura similar, pero el objeto del análisis es ODP
Límites de ODP basado en ZIP
- Un archivo ZIP se parece a una base de datos clave/valor optimizada para escribir una vez y leer muchas veces, y es adecuada para una estructura con pocas claves que contienen valores BLOB grandes
- Como es difícil actualizar elementos individuales, cuando el usuario selecciona
File/Save normalmente se vuelve a escribir todo el archivo ZIP
- Es posible actualizar elementos individuales sin corromper todo el documento durante una pérdida de energía o un fallo, pero es lo bastante difícil como para que en la práctica casi no se use
- En una presentación de 50 MB, cambiar una sola letra puede implicar reescribir los 50 MB completos
- El tiempo de inicio puede volverse lento
- ODP guarda todo el contenido de las diapositivas en un único archivo XML grande llamado
content.xml
- LibreOffice lee y parsea todo ese archivo para mostrar la primera diapositiva
- Parece que también lee todas las imágenes en memoria; como resultado, al hacer doble clic en el archivo aparece una barra de progreso en lugar de la primera diapositiva
- El uso de memoria aumenta
- La estructura ZIP incentiva una implementación que lee todo el documento en memoria al iniciar, realiza la edición en memoria y luego escribe todo el documento al disco al guardar
- Una presentación de 50 MB puede usar más de 200 MB de RAM
- Si se abren varias presentaciones a la vez y se usan junto con un navegador y apps de escritorio, puede producirse swapping
- La recuperación ante fallos se vuelve engorrosa
- La familia OpenOffice hace copias de respaldo periódicas del documento en memoria para prepararse ante fallos
- Durante el respaldo, la app puede congelarse por unos segundos, y tras reiniciar hay que pasar por un cuadro de diálogo de recuperación separado
- La accesibilidad del contenido es baja
- Con herramientas ZIP se pueden extraer imágenes, pero extraer o modificar el texto de las diapositivas con herramientas comunes es difícil
- En el archivo de ejemplo,
content.xml tiene una declaración XML en la primera línea y 211,792 caracteres de XML en una sola línea en la segunda
Primera mejora: reemplazar ZIP por SQLite
- El primer paso es una estructura simple que convierte los elementos ZIP en filas de una tabla SQLite
CREATE TABLE OpenDocTree(
filename TEXT PRIMARY KEY,
filesize BIGINT,
content BLOB
);
- En esta etapa no se cambia el resto de la estructura del formato de archivo
- Sigue siendo una estructura de “montón de archivos”, pero cada archivo pasa a ser una fila de una base de datos SQLite en lugar de una entrada ZIP
- La comparación de tamaños de un archivo SQLite reempaquetado con la misma información que
self2014.odp creado por NeoOffice usando la utilidad SQLAR es la siguiente
self2014.odp: 10,514,994 bytes
self2014.sqlar: 10,464,256 bytes
zip.odp, recomprimido con zip de línea de comandos: 10,416,644 bytes
- El archivo SQLite fue aproximadamente 0.5% más pequeño que el ODP generado por NeoOffice
- El archivo ZIP bien comprimido con
zip de línea de comandos volvió a ser aproximadamente 0.5% más pequeño que SQLite
- Una base de datos SQLite puede competir con un archivo ZIP en tamaño
- SQLite ofrece escrituras atómicas, por lo que puede guardar cambios incrementales sin riesgo de corromper el documento durante un fallo o una pérdida de energía
- Sigue existiendo la limitación de tener que reescribir todo
content.xml
- Aun así, los otros 77 archivos pueden quedar intactos, lo que acelera File/Save y reduce la cantidad de escrituras en el SSD
Segunda mejora: dividir el contenido en fragmentos pequeños
- Como SQLite puede almacenar eficientemente tanto bloques grandes como muchos fragmentos pequeños, puede haber una tabla de contenido por diapositiva
CREATE TABLE slide(
pageNumber INTEGER,
slideContent TEXT
);
CREATE INDEX slide_pgnum ON slide(pageNumber);
- Al mostrar la primera pantalla, la aplicación solo necesita leer la primera diapositiva
SELECT slideContent FROM slide WHERE pageNumber=1;
- Con esta estructura, se puede obtener, parsear y mostrar rápidamente solo el contenido de la primera diapositiva, sin necesidad de leer todo
content.xml al iniciar
- También aumentan las opciones de implementación
- Después de mostrar la primera diapositiva, se pueden leer las demás páginas en un hilo en segundo plano
- Se puede mantener en memoria solo la diapositiva actual
- Para transiciones rápidas, también se pueden mantener en memoria la diapositiva actual y la siguiente
- Guardar también se vuelve más rápido porque solo hay que reescribir las páginas modificadas
- En fragmentos cortos de texto, la eficiencia de compresión puede bajar y el tamaño del documento puede aumentar
- Sin embargo, como la mayor parte del espacio del documento la ocupan las imágenes, la menor eficiencia de compresión del texto puede verse como un costo pequeño frente a la mejora en la experiencia de usuario
Tercera mejora: control de versiones
- Si las diapositivas se almacenan como objetos individuales, el historial de versiones puede guardarse dentro del mismo documento
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 diapositiva tiene un
slideId único en lugar de un número de página, y el orden se determina por la lista de slideId guardada en el manifest de la tabla version
- Al iniciar, la aplicación primero elige qué versión mostrar y, normalmente, puede obtener la versión más reciente
SELECT manifest, versionId FROM version ORDER BY versionId DESC LIMIT 1;
- También es posible una consulta que obtenga la versión más reciente según
checkinTime
SELECT manifest, versionId, max(checkinTime) FROM version;
- En SQLite, la consulta anterior con
max(checkinTime) devuelve un resultado definido, pero en muchas otras bases de datos SQL puede devolver un resultado no definido o generar un error
- Cuando el usuario ejecuta
File/Save, solo las diapositivas modificadas pueden agregarse como nuevas filas en la tabla slide, y puede crearse una nueva fila version con el manifest modificado
- La tabla
version registra el momento del check-in, los comentarios del usuario y la versión padre para preservar el historial de cambios
- También es posible guardar varias presentaciones dentro del mismo documento
- Si en lugar de un archivo de respaldo separado se usa una versión especial
pending, se pueden registrar con frecuencia y de forma silenciosa los cambios no guardados
- Como solo se escriben los cambios y no todo el documento, la operación escribe unos KB en lugar de varios MB
- El tiempo de guardado puede ser de milisegundos en lugar de segundos
- Incluso después de un fallo y reinicio, se puede conservar la mayor parte —o casi todo— el trabajo del usuario
- Si el usuario quiere descartar cambios no guardados, basta con volver a una versión anterior
Más funciones posibles en un formato de archivo SQLite
- Un contenedor SQLite puede agregar funciones importantes a un formato de archivo de aplicación con solo tres tablas
- Además, se pueden aprovechar esquemas, índices, triggers, vistas y restricciones para aumentar el rendimiento, la comodidad y la consistencia
- Algunas ideas de extensión son las siguientes
- Guardar una pila automática de undo/redo en tablas de base de datos para poder deshacer incluso hasta sesiones de edición anteriores
- Agregar búsqueda de texto completo a una presentación o a varias presentaciones
- Descomponer
settings.xml en tablas SQL para que otras aplicaciones puedan verlo y editarlo con mayor facilidad
- Separar las notas del presentador de cada diapositiva en una tabla aparte para que apps o scripts de terceros puedan acceder a ellas fácilmente
- Ir más allá de un orden lineal simple de diapositivas y admitir una estructura de presentación con rutas alternativas y desvíos según la reacción del público
Objeciones comunes sobre SQLite y respuestas
- La experiencia con bases de datos SQL empresariales puede generar rechazo a usar SQLite como formato de archivo de aplicaciones
- Muchas bases de datos empresariales recomiendan no poner cadenas grandes ni BLOB en la base de datos y guardarlos como archivos separados, pero SQLite es diferente
- Cualquier columna de SQLite puede almacenar cadenas o BLOB de hasta aproximadamente 1 GB
- Para cadenas y BLOB de 100 KB o menos, el rendimiento de I/O es mejor que con archivos separados
- La idea de que todo esquema SQL debe estar en tercera forma normal (3NF) y guardar solo tipos primitivos pequeños también puede ser una limitación
- La teoría relacional es importante, pero en formatos de archivo reales también puede ser una opción aceptable guardar información compleja como XML o JSON en campos de texto
SQLite como formato de archivo de aplicaciones
- Un archivo de base de datos SQLite tiene casi el mismo tamaño que un archivo ZIP con la misma información y, en algunos casos, puede ser más pequeño
- Gracias a las actualizaciones atómicas, los cambios pequeños pueden registrarse de forma segura en el documento, reduciendo la I/O de disco y mejorando el rendimiento de File/Save
- La aplicación puede leer solo el contenido necesario para la primera pantalla y así reducir el tiempo de inicio
- Puede mantener en memoria solo el contenido relacionado con lo que se muestra actualmente y dejar el resto en disco, reduciendo mucho el uso de memoria
- Un esquema SQL puede expresar la información de forma más directa y concisa que una estructura clave/valor como ZIP
- Mejora la accesibilidad para apps y scripts de terceros
- Facilita implementar funciones avanzadas como control de versiones integrado del documento y recuperación del trabajo tras fallos
- OpenDocument ya es un formato consolidado y bien diseñado, y como SQLite apareció después de OpenDocument, esto no pretende criticar las decisiones existentes
- El documento Application File Format ofrece ideas adicionales para usar SQLite como formato de archivo de aplicaciones
1 comentarios
Opiniones de Hacker News
Estoy creando una app que usa SQLite como formato de archivo.
Como quiero mantener el flujo habitual en el que el archivo solo cambia cuando el usuario edita un documento y lo guarda, al abrir el archivo lo copio a una base de datos
:memory:: https://www.sqlite.org/inmemorydb.htmlEl usuario lo manipula libremente, y la app refleja los cambios directamente en el formato de la base de datos, sin un modelo de documento aparte. Al guardar, lo manejo escribiendo de nuevo al archivo de base de datos con
VACUUM: https://www.sqlite.org/lang_vacuum.htmlFunciona bien con archivos de tamaño razonable, y en mi app siempre están dentro de ese rango.
Mejor sería guardar automáticamente directo en la base de datos y eliminar el botón de guardar. Es resistente a conflictos, al haber una sola base de datos se reduce el código y los bugs, y una escritura de SQLite o tiene éxito o falla, sin estados intermedios. En cambio, como se cita en el documento,
VACUUM INTOpuede dejar la base de datos de salida incompleta o dañada ante un cierre inesperado o pérdida de energía.Si usas SQLite de la forma en que fue pensado originalmente, no tienes que preocuparte por esto durante toda la vida útil de SQLite.
Es mucho mejor guardar después de cada operación en una ubicación temporal, por ejemplo algo como
~/.local/share/application/yourappsegún los directorios XDG, y cuando el usuario presione guardar, copiar el archivo a la ubicación deseada. Si vuelves a abrir la app después de una pérdida de energía, se recupera casi desde el mismo punto y como mucho podrías perder los últimos segundos.Cuando el usuario guarde, haces un checkpoint para fusionar el contenido del WAL en la base de datos principal.
VACUUMcopia el contenido a un archivo temporal de base de datos y luego sobrescribe el original; al sobrescribir usa un rollback journal o WAL como una transacción normal. Por eso requiere espacio libre de hasta aproximadamente el doble del original.VACUUM INTOusa el archivo especificado enINTOen lugar de una base de datos temporal, y omite el paso de copiar de vuelta sobre el original. Importa si lo que realmente se usa esVACUUM, que resiste cortes de energía, oVACUUM INTO, que parece vulnerable a cortes de energía mientras escribe y podría dañar el archivo si se usa un nombre existente.El problema de SQLite es que no es un formato de archivo estandarizado.
Está bien documentado y se entiende ampliamente, pero ningún estándar ISO define en detalle cómo interpretar un archivo SQLite. Lo mismo aplica a las implementaciones alternativas.
Zip y XML tienen una superficie de API mucho menor que SQLite. La API de SQLite va más allá de unas cuantas funciones en C: es el propio lenguaje SQL, y tener que implementar un parser SQL, un optimizador de consultas, un compilador, una máquina virtual de bytecode, un motor de búsqueda de texto completo, etc., sin corrupción de datos, es mucho más trabajo que un parser XML.
Para una app cerrada específica de un dominio donde la interoperabilidad o la estandarización ISO no importen, SQLite es un buen formato de archivo, pero entiendo que en OpenOffice esas preocupaciones sí existían.
La biblioteca C de SQLite también está en dominio público, así que el código fuente está completamente abierto, maneja el formato de archivo y tiene un nivel de documentación superior al de la mayoría de los estándares ISO. También hay bindings para casi todos los lenguajes importantes.
Si el problema es que todavía habría que crear y documentar algún formato OpenDocument que se guarde dentro de un archivo SQLite, eso es otra cosa. Los estándares ISO son buenos, pero si hubiéramos tenido que esperar a que ISO definiera cada formato de archivo, habría muy pocas cosas utilizables.
Es como no necesitar implementar todas las funciones de una hoja de cálculo para leer una hoja de cálculo de LibreOffice. Lo que necesitas es la capacidad de reconstruir las tablas; después puedes recorrerlas con código imperativo escrito en el lenguaje que elijas para obtener la información que quieras.
Reimplementar esa biblioteca sería un gran trabajo, pero es el mismo tipo de trabajo que reimplementar código que usa el formato de archivo OpenDocument. El formato de archivo en sí es bastante simple.
Si te preocupa la compatibilidad, se podría hacer que el documento también sea accesible desde otras bases de datos como MySQL.
Pensé que si Audacity adoptaba SQLite, la función de guardado de archivos mejoraría mucho, pero en la práctica hubo muchas trampas.
En Linux, si guardabas como archivo nuevo en un montaje NTFS propiedad de root pero con escritura para todos, creado con
/etc/fstab, fallaba por razones como errores de permisos; en cambio, al guardar sobre un archivo existente funcionaba bien.En cuanto editas un proyecto, el archivo en disco se modifica, así que si metes un proyecto de Audacity en Git como un bloque binario aparecen diferencias innecesarias en Git. Incluso después de guardar, los datos obsoletos o eliminados permanecen en el archivo SQLite hasta que cierras la ventana del proyecto, así que si no cierras la ventana antes de hacer commit, pueden terminar en el repositorio. Recuerdo que antes había que hacer
VACUUMmanualmente al archivo.aup3, pero ahora basta con cerrar la ventana. Queda esa sensación de Fast Save de Word 2003.Cuando un proyecto que debería pesar unos cientos de MB terminaba ocupando varios GB y había que ahorrar espacio en disco, para trabajos simples de una sola pista la solución era Mix and Render. No cambiaba el audio, pero permitía limpiar residuos al guardar y salir.
Esto no es un problema de SQLite en sí, sino claramente de la capa de aplicación. Creo que Audacity 2 tenía el concepto de un espacio de trabajo temporal, pero Audacity 3 parece usar el propio archivo
.aup3como espacio de trabajo.Revisé el formato de Audacity 3 y me resultó muy extraño que, aunque guarda los datos del proyecto que antes correspondían al archivo
.aupcomo XML en una tabla de una sola fila, no los escribe como texto tal cual, sino que los codifica con un codificador de diccionario simple. Eso dificulta mucho más la interoperabilidad y la inspección, perjudica aunque sea un poquito el rendimiento, y el ahorro de espacio será apenas un error de redondeo de unos pocos KB frente a archivos de audio de cientos de MB.Desde el punto de vista de Git, conviene usar un formato de texto que facilite los diffs y los merges. No sé qué tan fácil sea en ese aspecto un volcado de SQLite.
Si es importante, se puede arreglar manualmente, pero normalmente con descartar el archivo vuelve a funcionar.
Buen artículo. Aun así, me gusta que OpenDocument sea un conjunto de archivos XML dentro de un archivo Zip.
Permite generar con bastante facilidad documentos como hojas de cálculo, sin una biblioteca pesada que conozca el formato del documento.
Hay casos en los que los usuarios de un servicio web quieren usar en varias herramientas los datos exportados como filas de una tabla. UTF-8 CSV es abierto, convencional y usable, pero cualquiera que haya ofrecido CSV a usuarios finales conoce el dolor de que se atasque en las apps de hojas de cálculo.
Guardé una hoja de cálculo de ejemplo como ODS de OpenDocument y como XLSX de OOXML, el monstruo XML de Microsoft, y entendí solo lo básico del formato XML. Reduje el archivo Zip dejando solo los elementos necesarios, marqué los lugares donde debía ir el contenido y, a pedido, genero un nuevo archivo de hoja de cálculo. Ahora puedo sacar los mismos datos como CSV, ODS, XLSX y JSON.
Con SQLite también sería posible, pero sería un poco más complejo y el desarrollo iría más lento. Poder crear un documento de plantilla con una suite de oficina y hurgar en el XML del archivo guardado es una función de nicho, pero buena.
El problema es que Excel en locales como
nl_NLse comporta como si tuviera hardcodeado que el separador de columnas de los archivos CSV es el punto y coma. Esto se debe a que Microsoft decidió, infamemente, que los neerlandeses no usan comas en archivos de comma separated values.localeconv()->decimal_point. Si el valor es,, Excel usa punto y coma tanto en archivos CSV como en el lenguaje de expresiones de las fórmulas.Antes se podía configurar al abrir CSV/TXT en Excel, y en LibreOffice todavía se puede, pero en el proceso general de simplificación de la UI lo movieron a algún lugar del menú
Data/pestaña de la cinta. Hay que abrir un libro nuevo y encontrar la opción correcta; si quieres ahorrar tiempo, conviene usar LibreOffice.Esta parte sí me sorprendió mucho. Es difícil creer que no haga falta una consulta anidada.
SELECT manifest, versionId, max(checkinTime) FROM version;Dicen que en SQLite esta segunda consulta con
max(checkinTime)realmente funciona bien y devuelve una respuesta definida. En otros motores de bases de datos SQL devuelve una respuesta indefinida o un error, pero en SQLite devuelve elmanifesty elversionIddel elemento con elcheckinTimemáximo.En este caso no hace falta una consulta anidada; basta con ordenar por
checkinTimey limitar a uno:select manifest, versionId, checkinTime from version order by checkinTime desc limit 1Al menos debería funcionar en SQLite y PostgreSQL. Recuerdo que en Oracle había que usar
where rownum=1, así que ahí sí hacía falta una consulta anidada.GROUP BY manifest, versionId ORDER BY 3 DESC LIMIT 1o de un CTE que calcula elcheckinTimemáximo y luego hace un join.Pero si hay varias filas con el mismo
checkinTimemáximo, hay aleatoriedad y puede convertirse en un disparo en el pie, así que no suelo usar mucho esta particularidad de SQLite3. Para elegir de forma determinista la mejor fila, hace falta un método explícito parecido al de arriba.En Postgres se puede hacer algo similar con una consulta
DISTINCT ON. Es una de esas tareas que parecen simples en SQL, pero que me resultaron de las más difíciles.manifestyversionIdno tienen dependencia funcional demax(checkinTime).Por ejemplo, podría haber dos filas con el mismo valor de
checkinTime, y ese valor podría ser el máximo.Alguna vez lanzamos un producto que usaba tanto SQLite como archivos XML
Una de las mejoras fue mover algunas tablas con pocos datos a archivos XML. Como los archivos eran pequeños y casi no se usaban, se simplificaron la capa de acceso a datos y el diagnóstico, y los hicimos como XML con indentación de tabulaciones en varias líneas
Pedirle al personal técnico que tenía que diagnosticar el producto que abriera una base de datos SQLite era bastante pesado. Pero en las partes principales del producto, SQLite era abrumadoramente mejor que los archivos XML. La versión anterior usaba archivos XML, pero como XML no tiene una buena forma de hacer actualizaciones incrementales, había problemas de escalabilidad
La ventaja de XML, que es ser un formato legible por humanos, solo funciona bien cuando el archivo es pequeño y el diseño del esquema está adaptado a un XML fácil de leer. Tener que reescribir todo el archivo XML cada vez, junto con la complejidad que aparece a medida que se agregan funciones, erosiona rápidamente la mayor ventaja de XML
Los casos en que un usuario común necesita tocar directamente el interior de un documento de oficina son lo bastante raros como para que aprender a usar un lector de SQLite sea una barrera de entrada aceptable. Las limitaciones de XML+Zip para escrituras arbitrarias en medio de un archivo no se pueden superar ni con la ley de Moore
TEXToBLOBde SQLite se comprimen, o si se asume que quien lo llama comprime el BLOB antes de escribirloODT fue diseñado pensando en la estandarización. El formato anterior también era muy parecido, pero dependía mucho de estándares existentes como XHTML, SVG y CSS
Si no se pudiera hacer referencia a estándares existentes, la propia especificación de ODT se volvería de golpe enorme. El esfuerzo para actualizar el estándar también parece considerable, y en los últimos años no hubo mucho avance
En la práctica, el formato SQLite podría ofrecerse como opción, pero parece que el barco de los formatos de documentos de oficina ya zarpó. Aun así, esto da buenos argumentos para ordenar la especificación de SQLite como un estándar oficial
Salvo algunos defectos —por ejemplo, el problema de que los estilos locales y los rangos de texto se disparan por el atributo
ooo:rsid, las hojas de cálculo no dispersas y el extraño mecanismo de estilo de tablas—, es un marcado muy bien diseñado para este tipo de datos de documentos. Tiene un buen equilibrio entre el marcado semántico y la presentación que el usuario realmente quiere lograrEn cambio, Office OpenXML tiene etiquetas vacías de formato con estado, y en DOCX alternan si el texto que sigue se mostrará en negrita
Vincular un formato de archivo a SQLite huele a que algo está mal
SQLite es bueno, pero es bastante singular en este ámbito. Como hace muchas cosas, es difícil replicarlo tal cual
Pero en este caso, si preguntamos si se necesitan tantas funciones, la respuesta es no. Solo se necesitan semánticas de transacción básicas y seguras, y la capacidad de almacenar una estructura simple de tablas; no hace falta todo el estándar SQL ni siquiera un optimizador de consultas
Podría haber un mejor formato de archivo, pero sería preferible que fuera un formato separado de SQLite
Pesa menos de 1 MB y https://sqlite.org/footprint.html; incluso con todas las funciones activadas son 750 KB: https://www.sqlite.org/about.html
En tiempo de compilación se pueden quitar bastantes funciones, y parece que también hay opciones para ajustar o reducir el planificador de consultas: https://www.sqlite.org/compile.html
Además, también está la frase: “SQLite no compite con bases de datos cliente/servidor. SQLite compite con
fopen()”: https://www.sqlite.org/whentouse.htmlAl final, lo que se necesita no es la base de datos en sí, sino una biblioteca que ofrezca la API y el comportamiento de una base de datos
SQLITE_BUSYera bastante complicadoSé que en el manejo de transacciones se esperan fallos de serialización, pero en SQLite era difícil distinguir entre fallos persistentes, como una especie de interbloqueo propio, y problemas temporales de actualización concurrente. Si es un fallo temporal, basta con volver a ejecutar el cierre que define el trabajo de la transacción; si es persistente, no tiene sentido
Parte del problema es que
sqlite3_stmtcombina tanto el carácter de una sentencia preparada como el de un conjunto de resultados. Uno tiende a conservarlo bastante tiempo para cachear el bytecode compilado, pero si se detiene a mitad de una iteración, en ese momento puede estar reteniendo un bloqueo. Eso puede provocar fallos inesperados al intentar subir de nivel el bloqueoAl final eliminé el problema creando informes de error detallados con
sqlite3_next_stmt,sqlite3_stmt_busyysqlite3_sql. Aunque era para uso personal, el código de reintento de transacciones estaba lleno de logging opcional y comentarios. La lógica de reintento de transacciones para PostgreSQL fue mucho más fácilOtra cosa que me sorprendió fue lo que decía la documentación: en modo WAL con
synchronous=NORMAL, una transacción confirmada puede revertirse después de una pérdida de energía o una caída del sistema: https://sqlite.org/pragma.html#pragma_synchronousNo era relevante para mi aplicación
Si Richard Hipp y su empresa muestran un estándar SQLite ISO/IEC/ANSI/ETSI del que nunca se aparten, una revisión legal que confirme que no hay patentes que afecten, y varias implementaciones SQLite compatibles que conserven todas las ventajas, entonces podríamos hablar de recomendarlo como formato de archivo. De lo contrario, es decir que se acepte una fuerte dependencia de una implementación de fuente única y que también se la trasladen a los usuarios
XML, ASN.1 y JFIF son estándares oficiales, y ZIP también es un estándar oficial adoptado como ISO/IEC 21320-1:2015 durante el proceso de estandarización de OpenDocument
Lo más importante en un documento es que todos los demás puedan leerlo. Reducir el tiempo de actualización en disco es secundario. No hay que olvidar lo aprendido cuando Microsoft distorsionó los organismos de estandarización para mantener la dependencia: https://arstechnica.com/uncategorized/2008/10/norwegian-standards-body-implodes-over-ooxml-controversy/
No puede ser una elección tan equivocada
Otro ejemplo son los mosaicos de mapas rasterizados. En la práctica, son pequeñas imágenes cuadradas que pueden llegar a ser millones
Probamos Zip, tar, sistemas de archivos y SQLite, y SQLite fue el más rápido y el más pequeño, incluso mejor que un archivo genérico sin sobrecarga
SQLite tiene una gran desventaja. Los BLOB obtenidos de la base de datos no se pueden usar con
mmap, así que hay que copiarlos a otro lugar. Un archivo Zip, si no está comprimido o si está comprimido con una codificación particular como PVRTC, se puede usar directamente conmmapOpenDocument es imágenes comprimidas y XML. En definitiva, eso significa que se parsea todo el formato y se carga en memoria.
No termino de ver cómo SQLite mejora esto. XML no es ideal, pero al estar comprimido en Zip, la penalización de tamaño tampoco es grande.
Todas las ventajas enumeradas en el artículo de SQLite se pueden implementar usando SQLite como modelo en tiempo de ejecución del documento. Es posible tanto en disco como en memoria, pero SQLite no tiene por qué ser el formato de transmisión.
De hecho, SQLite podría terminar siendo más grande que el formato actual. Después de cambios, puede quedar espacio sin usar, fragmentarse y volverse disperso. Si hay que optimizar cada vez, también desaparecen ventajas como el guardado rápido.
Los formatos que necesitan actualizaciones delta y búsquedas rápidas en índices, y que no deberían cargar todo el archivo en memoria, en la práctica suelen usar SQLite como formato de archivo. Pero siento que OpenDocument fue un mal ejemplo para elegir como objetivo de SQLite en este escenario hipotético.
Si se usa SQLite como formato en disco y la aplicación está bien implementada, es posible evitar terminar en un estado corrupto.
Con XML/Zip también se puede lograr algo parecido con trucos de renombrado, pero SQLite ofrece eso en un único archivo en disco. Si ya se usa SQLite como modelo en memoria, no hay razón para no usarlo también como formato en disco/de transmisión. En ese punto, es casi gratis.
El problema del tamaño del archivo parece que podría manejarse con
VACUUM.