Por qué elegí SQLite para mi SaaS y cómo lo escalo sin morir en el intento
Decidí usar SQLite para mi nuevo producto SaaS, una elección que muchos considerarían arriesgada. En este post te cuento por qué lo hice y las estrategias que estoy usando para escalarlo en producción de forma eficiente y económica.
Como emprendedor y desarrollador, he montado más servidores de PostgreSQL y MySQL de los que puedo contar. Son la opción por defecto, la elección "segura". Para mi último proyecto SaaS, decidí romper con la norma. Elegí SQLite. Y no, no estoy loco.
La reacción habitual es una mezcla de sorpresa y escepticismo. "¿SQLite? ¿Para un SaaS en producción? ¿Estás seguro?" Sí, lo estoy. Y no solo es viable, sino que para muchos modelos de negocio SaaS, es una opción superior en simplicidad, rendimiento y coste. Déjame contarte por qué y cómo lo estoy haciendo funcionar a escala.
¿Por qué SQLite? La Elección Contraintuitiva
Vivimos en una época de complejidad accidental. Añadimos capas y capas de tecnología porque "así es como se hace". PostgreSQL en un RDS, conectado a través de una VPC a un clúster de Kubernetes... A veces, todo eso es necesario. Pero muchas otras veces, no lo es. Sobre todo al principio.
Mi apuesta por SQLite se basa en tres pilares:
1. Simplicidad Radical: SQLite no es un servidor. Es una librería que se enlaza con tu aplicación y lee/escribe en un fichero local (`.db`). No hay procesos que gestionar, ni usuarios que configurar, ni puertos que abrir, ni latencia de red entre tu app y tu base de datos. La configuración es, literalmente, una línea de código que apunta a un fichero. Esto acelera el desarrollo, simplifica las pruebas (cada test puede tener su propio fichero de base de datos) y hace los despliegues triviales.
2. Rendimiento Brutal: Para la gran mayoría de aplicaciones SaaS, donde la carga de trabajo es principalmente de lectura, SQLite es endiabladamente rápido. ¿Por qué? Porque las lecturas no viajan por la red. Son llamadas a una función dentro de tu propio proceso que accede directamente al disco. Para muchos casos de uso, es órdenes de magnitud más rápido que llamar a una base de datos remota.
3. Coste y Operaciones de Risa: No necesitas pagar por una instancia de base de datos gestionada (que no son baratas). Tu coste de base de datos está incluido en el coste de tu servidor de aplicación. ¿Y los backups? `cp database.db backup.db`. Es así de simple. O mejor aún, como veremos, usar herramientas que lo hagan automáticamente.
El "Problema" de la Escalabilidad: Mitos y Realidades
Aquí es donde la mayoría de la gente se echa para atrás. "Pero, ¿cómo escalas eso?". La respuesta es que no escalas SQLite de la misma forma que escalas Postgres, y eso es una ventaja.
El principal "problema" de SQLite es la concurrencia de escritura. Por defecto, solo un proceso puede escribir en la base de datos a la vez. Sin embargo, gracias al modo WAL (Write-Ahead Logging), múltiples procesos pueden leer la base de datos *mientras* otro está escribiendo. Para un SaaS típico, donde las lecturas superan a las escrituras por 100 a 1, esto es más que suficiente.
Pero el verdadero truco para escalar no es intentar que todos tus servidores hablen con un único y gigantesco fichero SQLite. Eso sería un suicidio. La estrategia es diferente:
En lugar de mover el cómputo a los datos, muevo los datos al cómputo.
En mi SaaS, cada tenant (cliente) tiene su propio fichero de base de datos SQLite. Esto es *sharding* o particionamiento desde el día uno, algo que con Postgres o MySQL es complejo de implementar.
Cuando llega una petición para el `tenant-A`, mi router la envía a un servidor de aplicación disponible. Ese servidor se encarga de tener disponible el fichero `tenant-A.db` y opera sobre él. Las operaciones de un tenant están completamente aisladas de las de otro, lo que mejora la seguridad y el rendimiento.
Mi Stack en Producción y el Arma Secreta: Litestream
Aquí es donde la magia ocurre. ¿Cómo gestiono todos esos ficheros `.db` y me aseguro de que no se pierdan y estén disponibles donde se necesiten? La respuesta tiene un nombre: Litestream.
Litestream es una pequeña herramienta que se ejecuta en segundo plano en tu servidor. Captura cada cambio en tu fichero SQLite y lo replica casi en tiempo real a un almacenamiento de objetos barato como Amazon S3, Google Cloud Storage, etc.
Mi arquitectura se ve así:
* Servidores de Aplicación: Contenedores ligeros (uso Fly.io, pero vale cualquier cosa) que ejecutan mi código. * Base de Datos: Un fichero SQLite por tenant. * Replicación y Backups: Litestream corriendo junto a mi app, replicando cada `.db` a un bucket de S3.
Cuando un servidor de aplicación necesita acceder a los datos de un tenant por primera vez, simplemente restaura el fichero `.db` desde S3 usando Litestream. A partir de ahí, trabaja con el fichero local, y Litestream se encarga de mantener S3 actualizado. Si el servidor muere, puedo levantar otro en segundos, restaurar la base de datos desde S3 y seguir funcionando. Tengo backups continuos y recuperación ante desastres casi gratis.
# Ejemplo conceptual de cómo funciona LitestreamEsta arquitectura me permite escalar horizontalmente. ¿Más tráfico? Añado más servidores de aplicación. Cada uno puede manejar un conjunto de tenants de forma independiente. El cuello de botella de una única base de datos central desaparece.
¿Cuándo NO usar SQLite?
No soy un fanático. SQLite no es la solución para todo. Sería una mala elección si:
* Necesitas alta concurrencia de escritura: Si tu aplicación es una red social donde miles de personas escriben en las mismas tablas a la vez, necesitas un sistema cliente-servidor como Postgres. * Necesitas analíticas complejas sobre todos tus datos: Si constantemente necesitas ejecutar queries que unen datos de *todos* tus tenants, un data warehouse es una herramienta más apropiada. * Tu equipo no quiere salir de su zona de confort: A veces, la mayor barrera es la inercia del equipo. Si tu equipo solo sabe y quiere usar MySQL, forzar un cambio puede crear más problemas de los que resuelve.
Conclusión: Piensa Simple Primero
Mi viaje con SQLite en producción ha sido una revelación. Me ha forzado a adoptar una arquitectura limpia y escalable (sharding por tenant) desde el principio, algo que a menudo se pospone hasta que es dolorosamente tarde.
La lección más importante es esta: cuestiona las opciones por defecto. No elijas una tecnología porque es lo que usa todo el mundo. Entiende tu problema y elige la herramienta más simple y directa que pueda resolverlo. Para mi SaaS, y sospecho que para muchos otros, esa herramienta es la humilde, potente y a menudo subestimada SQLite.