24 puntos por GN⁺ 2025-12-19 | 4 comentarios | Compartir por WhatsApp
  • SQLite mantiene una alta confiabilidad y robustez gracias a un sistema de pruebas automatizadas exhaustivo, con 590 veces más código de pruebas que código fuente
  • Cuatro harnesses de prueba independientes (TCL, TH3, SQL Logic Test, dbsqlfuzz) validan la biblioteca principal y ejecutan cientos de millones de pruebas
  • Mediante pruebas de situaciones anómalas (OOM, errores de I/O, simulación de fallos) y fuzz testing, se verifica que funcione de forma estable incluso ante entradas anormales y fallas del sistema
  • Mantiene un proceso de validación en múltiples capas con 100% de cobertura de ramas y MC/DC, detección de fugas de recursos, Valgrind, análisis estático y listas de verificación
  • Gracias a este esquema sistemático de pruebas, SQLite es considerado una base de datos de código abierto con confiabilidad y calidad al nivel de una BD comercial

1. Panorama general

  • La confiabilidad y robustez de SQLite provienen de un proceso de pruebas minucioso
    • A partir de la versión 3.42.0, SQLite está compuesto por aproximadamente 155.8 KSLOC de código C y 92053.1 KSLOC de código de pruebas
  • El sistema de pruebas incluye 4 harnesses independientes, 100% de cobertura de ramas y millones de casos de prueba
    • Incluye OOM, errores de I/O, fallos, fuzzing, valores límite, regresión, archivos de BD anómalos, pruebas con optimizaciones desactivadas y muchos otros elementos

2. Harnesses de prueba

  • TCL Tests
    • Conjunto público de pruebas usado principalmente durante el desarrollo de SQLite
    • Compuesto por 27.2 KSLOC de código C y 1390 archivos de script (23.2MB)
    • Aproximadamente más de 50 mil casos de prueba; con parametrización, se ejecutan cientos de millones en la corrida completa
  • TH3
    • Conjunto comercial de pruebas basado en C que alcanza 100% de cobertura de ramas y MC/DC
    • También funciona en entornos embebidos e incluye 1055.4 KSLOC y alrededor de 50 mil casos
    • En una prueba completa de cobertura se ejecutan alrededor de 2.4 millones de pruebas, y antes de cada lanzamiento se realizan 248 millones de pruebas soak
  • SQL Logic Test (SLT)
    • Compara los resultados de SQLite con PostgreSQL, MySQL, SQL Server y Oracle 10g
    • Está compuesto por 7.2 millones de consultas y 1.12GB de datos
  • dbsqlfuzz
    • Fuzzer basado en libFuzzer que muta al mismo tiempo SQL y archivos de base de datos
    • Ejecuta alrededor de mil millones de pruebas de mutación por día, validando la robustez frente a entradas maliciosas
  • Herramientas adicionales
    • speedtest1.c, mptester.c, threadtest3.c, fuzzershell.c, jfuzz, etc.
    • Todas las pruebas deben pasar en múltiples plataformas y configuraciones de compilación para que un lanzamiento sea posible

3. Pruebas de situaciones anómalas

  • Pruebas OOM
    • Simulan fallos de malloc() para verificar si hay recuperación correcta ante falta de memoria
    • Se repiten incrementando el contador del punto de fallo
  • Pruebas de errores de I/O
    • Usan un sistema de archivos virtual (VFS) para simular errores de disco
    • Después del error, se verifica si hubo corrupción de datos con PRAGMA integrity_check
  • Pruebas de fallos
    • Simulan cortes de energía y fallos del sistema operativo
    • El harness de TCL se basa en procesos hijo, y TH3 usa un VFS en memoria
    • Verifican que una transacción se revierta por completo o se complete por completo
  • Pruebas de fallos compuestos
    • También validan situaciones donde, después de un fallo, ocurren de forma consecutiva OOM o errores de I/O

4. Fuzz testing

  • SQL Fuzz
    • Genera SQL sintácticamente válido pero anómalo para verificar la reacción de SQLite
  • American Fuzzy Lop (AFL)
    • Fuzzer guiado por perfil, introducido en 2014, que explora nuevas rutas de control
    • Encontró múltiples fallos de assert, crashes y resultados incorrectos en SQLite
  • Google OSS Fuzz
    • Desde 2016 ejecuta fuzzing automatizado sobre la infraestructura de Google
    • Detecta problemas intermitentes en commits nuevos
  • dbsqlfuzz / jfuzz
    • Adoptados como fuzzers internos desde 2018, mutan al mismo tiempo SQL y archivos de BD
    • Ejecutan más de 500 millones de pruebas por día, y casi eliminaron los reportes de bugs provenientes de fuzzers externos
    • Desde 2024, jfuzz añadió validación de entradas JSONB
  • Fuzzers de terceros y fuzzcheck
    • Investigadores externos (por ejemplo, Manuel Rigger) descubrieron numerosos casos de cálculo de resultados incorrectos
    • La utilidad fuzzcheck vuelve a validar miles de casos “interesantes” de fuzzing del pasado
  • La tensión entre MC/DC y fuzz testing
    • MC/DC minimiza el código defensivo, mientras que el fuzzing necesita código defensivo
    • SQLite combina ambos enfoques para mantener código robusto tanto para entradas normales como maliciosas

5. Pruebas de regresión

  • Todo bug reportado, una vez corregido, se agrega obligatoriamente como nuevo caso de prueba
    • El objetivo es evitar la reaparición de errores pasados

6. Detección automática de fugas de recursos

  • Los harnesses TCL y TH3 supervisan automáticamente fugas de memoria, archivos, hilos y mutexes
    • No debe haber fugas de memoria incluso después de OOM o errores de I/O

7. Cobertura de pruebas

  • El núcleo de SQLite alcanza 100% de cobertura de ramas según TH3
    • Se excluyen extensiones como FTS3 y RTree
  • Cobertura de sentencias vs. cobertura de ramas
    • La cobertura de ramas es más estricta que la de sentencias y valida cada bifurcación condicional en ambos sentidos
  • Cobertura de código defensivo
    • Las macros ALWAYS() y NEVER() especifican condiciones defensivas
    • Las pruebas se repiten con tres formas de definición para verificar la consistencia
  • Pruebas de valores límite y vectores booleanos
    • La macro testcase() valida tanto resultados positivos como negativos de las condiciones
    • Se usan 1184 llamadas a testcase()
  • Logro de MC/DC
    • Mediante la macro testcase() se verifica el efecto independiente de cada condición
  • Medición basada en gcov
    • La cobertura se mide con las opciones -fprofile-arcs -ftest-coverage
    • Al comparar resultados, se detectan bugs del compilador o comportamiento indefinido
  • Mutation Testing
    • Se modifican instrucciones de bifurcación para verificar si las pruebas lo detectan
    • Las ramas de optimización (/*OPTIMIZATION-IF-TRUE*/) se tratan como excepción
  • La experiencia de cobertura completa
    • Gracias a probar todas las ramas, se minimizan los efectos secundarios al cambiar el código
    • El costo de mantenimiento es alto, pero se justifica al ser una biblioteca de infraestructura distribuida masivamente

8. Análisis dinámico

  • Assert()
    • 6754 sentencias assert verifican precondiciones, postcondiciones e invariantes de bucle
    • Solo están activas en compilaciones SQLITE_DEBUG
  • Valgrind
    • Detecta errores de memoria, desbordamientos de pila y accesos a memoria no inicializada
    • Antes de cada lanzamiento se ejecutan las pruebas veryquick y TH3 con Valgrind
  • Memsys2
    • En compilaciones SQLITE_MEMDEBUG, inserta wrappers para supervisar errores de memoria
    • Permite repetir verificaciones más rápido que con Valgrind
  • Mutex Asserts
    • Verifican la sincronización multihilo con sqlite3_mutex_held() y similares
  • Journal Tests
    • Verifican que el rollback journal se escriba antes que la BD, garantizando la atomicidad de las transacciones
  • Verificaciones de comportamiento indefinido
    • Se detecta comportamiento indefinido con -ftrapv, -fsanitize=undefined, /RTC1, etc.
    • Se repiten en 32/64 bits, distintos endianness y diversas arquitecturas de CPU

9. Pruebas con optimizaciones desactivadas

  • sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS) permite desactivar optimizaciones
    • Debe producirse el mismo resultado independientemente de que haya optimizaciones o no
    • Algunas pruebas orientadas a medir rendimiento son la excepción

10. Lista de verificación

  • Antes de cada lanzamiento se valida una checklist manual de alrededor de 200 elementos
    • Algunos toman segundos y otros varias horas
    • Si se detecta un problema, se agrega de inmediato un nuevo elemento, mejorando el proceso de forma continua

11. Análisis estático

  • Compila sin advertencias en GCC, Clang y MSVC
    • Tampoco hay advertencias válidas en Clang Static Analyzer
    • El análisis estático tiene un efecto limitado para detectar bugs reales

12. Resumen

  • SQLite mantiene calidad de nivel comercial y una baja tasa de defectos a pesar de ser de código abierto
    • Las pruebas exhaustivas y el diseño del código son factores clave
    • Cada lanzamiento pasa por este proceso y se ofrece como un motor de base de datos confiable incluso en entornos mission-critical

4 comentarios

 
regentag 2025-12-19

Artículo relacionado para leer junto con este: La historia desconocida de SQLite

Este es un texto que resume una entrevista con Richard Hipp, desarrollador de SQLite.

Se dice que los desarrolladores de SQLite conocieron Do-178 cuando trabajaban con Rockwell Collins y que empezaron a seguir este procedimiento. Uno de esos puntos es alcanzar el 100% de MC/DC.

Do-178 es realmente una guía muy útil, así que recomiendo que cualquier desarrollador la lea.

 
regentag 2025-12-19

Creo que el enlace que compartiste es material de capacitación de DO-178.
Puedes ver el documento original en este enlace.
https://studylib.net/doc/27132454/rtca-do-178b

 
GN⁺ 2025-12-19
Comentarios en Hacker News
  • Hace más de 10 años, el mantenedor de SQLite dio una charla en OSCON sobre las prácticas de testing
    Lo que más me impresionó fue el poder de una checklist. Exactamente esa herramienta que usan los pilotos antes de cada vuelo
    También mencionó el caso de Médicos Sin Fronteras (Doctors Without Borders), donde los resultados de las cirugías eran bajos porque el personal médico ni siquiera conocía sus nombres y además hablaban distintos idiomas
    La solución fue simple: crear una checklist previa a la cirugía para que cada quien dijera su nombre y su rol. Este pequeño ritual mejoró la tasa de supervivencia no por la técnica, sino por una mejor comunicación
    Material relacionado: ejemplo de checklist de SQLite

    • Por otro lado, creo que este tipo de historias generan burocracia innecesaria. Las checklists de aviación, MSF y SQLite son excelentes, pero la mayoría de las organizaciones pierden tiempo con checklists inútiles
      Hace falta más discusión sobre la diferencia entre una checklist buena y una mala. Parece simple, como una fórmula hermosa en matemáticas, pero da la impresión de que es difícil de descubrir
    • Siempre he sentido que hay mucho que aprender de las operaciones aéreas y la ingeniería. Me imagino una organización de TI que combine estos principios con un liderazgo de estilo militar
      En particular, he leído varias veces el documento FM22-100 del Ejército de EE. UU., y es sorprendentemente moderno e inspirador
      Ver documento FM22-100
    • Si quieres saber cómo hacer una buena checklist, recomiendo muchísimo The Checklist Manifesto
      Link del libro
    • No entiendo por qué la mayoría de los desarrolladores evita las tareas simples que no son programación
      Además de las pruebas y CI, yo sigo una checklist de despliegue en Markdown. Ni siquiera guardo los resultados, pero la sigo paso a paso
      No sé por qué los demás no hacen algo tan simple
    • Me parece interesante que un ritual mejore el desempeño. Hoy en día, incluso en reuniones, cuando hacemos una ronda de presentación de participantes, la participación claramente sube
      Si existe una página oficial sobre el caso de MSF, me gustaría verla. No la pude encontrar buscando en Google
  • Aquí reunieron discusiones pasadas sobre el testing de SQLite
    Lista de hilos de HN de 2009 a 2024
    Parece que los reposts se repiten con intervalos de un año

  • Me da envidia y asombro el proceso de pulir a la perfección un software como SQLite
    Se siente como una obra con verdadera artesanía

    • En realidad, cualquiera puede hacerlo así. Nunca me han despedido por construir las cosas despacio y bien
      Con el tiempo, el estándar de calidad sube y obtienes mayores recompensas con el mismo esfuerzo
      A nadie le desagrada alguien que deja un poco más limpio lo que tocó
  • SQLite es un software realmente excelente. También me gusta que su sitio oficial sea más informativo que de marketing
    Aunque me parece curioso que últimamente en HN estén subiendo una por una las páginas del sitio oficial

    • Probablemente ayer volvió a llamar la atención por el post de simonw sobre portar un LLM
    • En HN esto se repite periódicamente. Antes era Haskell, y ahora parece que Zig está pasando por ese ciclo
      Estaría divertido recopilar este tipo de links
  • Me parece interesante que SQLite use pruebas privadas aunque sea software público
    Apenas ahora caigo en que un proyecto open source puede tener testing cerrado
    Siento que este modelo podría convertirse en un nuevo modelo de negocio parecido a open-core

    • De hecho, muchas veces la suite de pruebas vale más que el código. Por ejemplo, documentar la enorme cantidad de edge cases de un software como Excel es más difícil que la implementación misma
    • Yo hago algo similar en proyectos de mi empresa. Junto con doble licenciamiento GPL, los generadores de pruebas y de datos solo se revelan a usuarios con licencia comercial
      Ejemplo: licencia de railgunlabs/unicorn
  • El 100% de cobertura de ramas de SQLite es tan impresionante como el proyecto mismo
    Que además se mantenga en el tiempo es especialmente notable

  • Me parece interesante que las pruebas sean privadas. Ahora que la programación con LLM está avanzando, estamos entrando en una época donde las pruebas importan más que la implementación
    Al ver el caso reciente en el que simonw convirtió casi automáticamente el motor justHTML de Python a JS, me acordé de la estrategia de pruebas de SQLite

    • Desde la perspectiva del modelo de negocio de un producto open source, una suite de pruebas enorme se vuelve un activo clave para hacer cambios eficientes y generar valor
    • El documento How SQLite Is Tested se siente literalmente como una biblia del testing
  • Hace poco estuve hablando con un LLM sobre compatibilidad entre SQLite y DuckDB, y llegué a la conclusión de que SQLite es mejor en el tema de manejo de concurrencia

  • Me sorprendió que en la documentación de testing de SQLite hubiera tan poca mención a las pruebas de regresión de rendimiento (performance regression)
    La corrección importa, pero una caída de rendimiento en ciertas rutas de consultas puede ser fatal

    • Trabajé en HFT, pero casi nunca he visto proyectos open source que prometan garantías de rendimiento
      Me pregunto si existe algún proyecto que tenga esa meta como misión central
  • Al ver la estabilidad de SQLite, quería saber más sobre cómo hicieron las pruebas de anomalías
    Pero en el texto casi no se menciona. Aun así, SQLite sigue siendo uno de los softwares más robustos que se usan en todo tipo de dispositivos

    • Como el acceso a la suite de pruebas ronda los 150 mil dólares al año, parece poco probable que revelen su contenido interno