- Wafris es una empresa de firewall de aplicaciones web de código abierto que ofrece un cliente middleware para Rails
- El cliente v1 requería un almacén de datos local en Redis, pero en v2 usa SQLite
- Explica el contexto de la decisión de migrar de Redis a SQLite, así como las consideraciones de rendimiento y los cambios de arquitectura
TL;DR
- SQLite tiene cosas para las que es bueno y cosas para las que no
- Redis tiene cosas para las que es bueno y cosas para las que no
- Los RDBMS tradicionales (Postgres/MySQL) tienen cosas para las que son buenos y cosas para las que no
- Estos almacenes de datos no se pueden sustituir directamente entre sí, y tratar de hacerlo te meterá en problemas
- Este artículo describe las pruebas y el proceso de decisión al re-arquitectar el cliente v1 basado en Redis hacia un cliente v2 basado en SQLite
Factores que forzaron el cambio
- El objetivo de Wafris es hacer que a los desarrolladores les resulte fácil proteger sus sitios
- Por problemas de despliegue de Redis, v1 no logró plenamente ese objetivo
- Eligieron Redis porque trabajaban en entornos como Heroku, donde es fácil usar Redis, pero muchos usuarios tuvieron problemas al desplegar Redis
- Obligar a los usuarios a usar una base de datos aparte como Redis no estaba pensado para beneficiar al usuario
¿Qué significa "velocidad"?
- Redis es “más rápido” que un RDBMS tradicional, pero aun así hay que gestionar conexiones, memoria, procesos, etc.
- En entornos cloud, la latencia de red puede ser un problema grande
- Como las reglas de Wafris deben evaluarse en cada solicitud HTTP entrante, la latencia de red puede ralentizar la aplicación
Suposición de monolito (Monolith-ish)
- Existen aplicaciones totalmente distribuidas, pero la mayoría de las apps Rails son “Majestic Monoliths”
- Las apps desplegadas en varias regiones, divididas en servidores con funciones superpuestas o que solo son parcialmente Rails presentan más problemas al usar Redis
Repensando la arquitectura
- Wafris es un firewall de aplicaciones web que se instala como middleware de Rails
- Simplificándolo en dos pasos: 1) comparar solicitudes HTTP contra reglas, y 2) reportar el resultado del procesamiento
- La “lectura” de reglas (paso 1) es mucho más importante que la “escritura” (paso 2)
- Las lecturas deben procesarse en secuencia, no pueden fallar y afectan el rendimiento percibido por el usuario
- Las escrituras pueden ser lentas, procesarse por lotes o hacerse de forma asíncrona
Entra SQLite
- Otras personas ya han explicado bien para qué casos de uso SQLite es adecuado
- SQLite no compite con las bases de datos cliente/servidor, sino con
fopen()
- Esperaban que al eliminar la ida y vuelta por red fuera mucho más rápido que Redis
- Decidieron evaluar SQLite y Redis con benchmarks
Benchmarking de SQLite y Redis
- Hacer benchmarks es un arte oscuro para engañarte con números precisos
- Hacer benchmarks de almacenes de datos es todavía más complicado
- No buscaban una velocidad absoluta, sino crear benchmarks específicos para sus datos y su caso de uso
- Ignoraron tweaks de optimización. Querían que Wafris funcionara al instante al integrarlo en una app
- Probaron la ruta crítica principal de su app y su peor consulta, no benchmarks teóricos
- La peor consulta era una solicitud a una estructura de datos compleja de “lexical decimal” que mapea rangos de IP (IPv4, IPv6) a categorías
- Precalcularon las búsquedas por rango y las escribieron en tablas de SQLite y conjuntos ordenados de Redis
- En cada solicitud HTTP entrante, deben comparar la IP de la solicitud con rangos personalizados de allow/block, rangos GeoIP y rangos de reputación IP
Protocolo de prueba
- Probaron con Redis instalado por Homebrew en una MacBook Air M2 y una base de datos SQLite local
- Hicieron pruebas con su conjunto de datos de rangos existente (1.2 millones de entradas)
- Ejecutaron varios conjuntos de IP sobre SQLite y Redis en el mismo orden
- Para cada multiplicador, ejecutaron la prueba 5 veces y sacaron el promedio
Resultados de la prueba
- SQLite aplastó a Redis (en su caso de uso específico)
- SQLite fue aproximadamente 3 veces más rápido que una instancia local de Redis
- Esto fue antes de considerar la latencia de red
- Incluso si SQLite solo empatara con Redis, seguiría siendo ventajoso por eliminar el tiempo de red
Lo que no aparece en la gráfica
- Aunque el rendimiento de SQLite fuera 2 veces peor en el benchmark, en la práctica podría ser más rápido por la latencia de red
- Por más potente que sea el servidor Redis, existen límites de ancho de banda de red, conexiones y latencia entre regiones
- SQLite ofrece escalado horizontal casi infinito “gratis”
- La experiencia de onboarding mejora muchísimo con SQLite. Es probable que el usuario ni note que se está usando
- Se podía exprimir más rendimiento de Redis, pero era difícil convencer a los usuarios de cambiar su configuración de Redis
Los resultados son solo el comienzo
- Demostraron que SQLite era más rápido que Redis, pero hay compensaciones reales
- La prueba anterior no consideraba las escrituras
- Para gestionar la competencia entre lecturas y escrituras se necesitan conexiones a la base de datos, pools de conexiones, transacciones, etc.
- Igual que a un superauto eléctrico le cuesta cargar bloques de concreto, no se debe usar SQLite para un rol para el que no es adecuado
Construyendo una arquitectura de sincronización
- En v1 (Redis), cuando el usuario actualizaba reglas en Wafris Hub, se actualizaban las reglas del almacén de datos Redis
- Eso no funciona con SQLite, porque no se puede “empujar” hacia el servidor web
- En v2 (SQLite): 1) el usuario actualiza reglas en Wafris Hub, 2) el cliente revisa a intervalos si hay reglas actualizadas, 3) si las reglas se actualizaron, descarga una base de datos SQLite completamente nueva
- Esto reduce drásticamente la responsabilidad del usuario en instalación y configuración
- La tasa de éxito de instalación del cliente v2 aumentó 3 veces
Arquitectura distribuida con SQLite
- Pensemos en una app Rails desplegada en un proveedor cloud con autoescalado activado
- Si las solicitudes pasan de 100 req/s a 10,000 req/s, las instancias de cómputo escalan, pero la base de datos no
- De hecho, esa es una razón principal por la que las apps Rails se caen por sobrecarga
- Sincronizar la base de datos SQLite en cada instancia de cómputo resuelve este problema al mantener todas las llamadas en local
¿Y qué pasa con las escrituras?
- Dividieron la app en rutas de lectura (evaluación de reglas) y escritura (reportes), y luego ignoraron la ruta de escritura
- Rediseñaron la ruta de escritura como 1) reportar conectándose de forma asíncrona a Wafris Hub, 2) enviar reportes por lotes y 3) eliminar por completo las escrituras a la base de datos del cliente
- Puede que esto no funcione para otros, pero a ellos solo les importan los usuarios que quieren un cliente de Wafris rápido y fácil de desplegar
Conclusión
- Están muy satisfechos con la arquitectura v2 basada en SQLite
- Ya está ayudando a muchos sitios a resistir ataques y mantenerse en línea
- Empezar ahora es mucho más fácil, lo que reduce el trabajo de soporte para ellos y las molestias para los usuarios
- Consideran que esto es una victoria para un internet más seguro y protegido
7 comentarios
SQLite es suficientemente bueno, pero en este caso da la impresión de que simplemente no era un caso de uso adecuado para Redis...
Que el benchmark se haya hecho en una M2 es como que...
Entonces, ¿hay que medirlo en cada instancia de AWS? Me parece que están pidiendo demasiado de un proyecto de código abierto.
¿Se hizo en el mismo entorno de servidor; eso sería un problema?
¿Y para el benchmark hay que usar un CPU específico...?
¿Qué aspectos de lo que se hizo en m2 podrían ser problemáticos? (aparte del hecho de que el entorno de servicio real no usa un procesador m2)
Ese es el problema. Hacer experimentos en el laboratorio y afirmar: ¡esto es perfecto para uso comercial!