- Wafris est une entreprise de pare-feu applicatif web open source qui propose un client middleware Rails
- Le client v1 nécessitait un stockage de données Redis local, mais la v2 utilise SQLite
- L’article explique le contexte de la décision de migrer de Redis vers SQLite, ainsi que les considérations de performance et les changements d’architecture
TL;DR
- SQLite a des points forts et des limites
- Redis a des points forts et des limites
- Les SGBDR traditionnels (Postgres/MySQL) ont des points forts et des limites
- Ces stockages de données ne sont pas interchangeables directement, et essayer de le faire mène à des difficultés
- Cet article décrit les tests et le processus de décision traversés lors de la réarchitecture du client v1 basé sur Redis vers un client v2 basé sur SQLite
Facteurs ayant forcé le changement
- L’objectif de Wafris est de permettre aux développeurs de protéger facilement leurs sites
- À cause des problèmes de déploiement de Redis, la v1 n’atteignait pas pleinement cet objectif
- Redis avait été choisi parce que l’équipe travaillait dans des environnements comme Heroku, où il est facile à utiliser, mais de nombreux utilisateurs rencontraient des problèmes de déploiement de Redis
- Forcer les utilisateurs à utiliser une base séparée comme Redis n’allait pas dans leur intérêt
Qu’est-ce que la « vitesse » ?
- Redis est « plus rapide » qu’un SGBDR traditionnel, mais il faut toujours gérer les connexions, la mémoire, les processus, etc.
- Dans le cloud, la latence réseau peut devenir un problème majeur
- Comme les règles Wafris doivent être évaluées à chaque requête HTTP entrante, la latence réseau peut ralentir l’application
Hypothèse monolithique
- Il existe des applications entièrement distribuées, mais la plupart des apps Rails sont des « Majestic Monoliths »
- Les applications déployées sur plusieurs zones, divisées entre serveurs aux fonctions qui se chevauchent, ou seulement partiellement en Rails rencontrent davantage de problèmes avec Redis
Repenser l’architecture
- Wafris est un pare-feu applicatif web installé comme middleware Rails
- En simplifiant, il se divise en 2 étapes : 1) comparer les requêtes HTTP aux règles, 2) remonter le résultat du traitement
- La « lecture » des règles (étape 1) est bien plus importante que l’« écriture » (étape 2)
- Les lectures doivent être traitées séquentiellement, ne pas échouer et ont un impact sur les performances perçues par l’utilisateur
- Les écritures peuvent être plus lentes, traitées par lots ou faites de manière asynchrone
Entrée en scène de SQLite
- D’autres ont déjà très bien expliqué dans quels cas SQLite est adapté
- SQLite n’est pas en concurrence avec les bases client/serveur, mais avec
fopen()
- En supprimant les allers-retours réseau, l’équipe s’attendait à ce qu’il soit bien plus rapide que Redis
- Elle a donc décidé d’évaluer SQLite et Redis au benchmark
Benchmark de SQLite et Redis
- Les benchmarks sont un art obscur qui permet de se tromper soi-même avec des chiffres précis
- Les benchmarks de stockages de données sont encore plus délicats
- L’objectif n’était pas de mesurer une vitesse absolue, mais de créer un benchmark spécifique à leurs données et à leur cas d’usage
- Les ajustements d’optimisation ont été ignorés : l’équipe voulait que Wafris fonctionne immédiatement une fois intégré à une app
- Au lieu de benchmarks théoriques, elle a testé les chemins critiques de son application et sa pire requête
- La pire requête consiste à demander une structure de données complexe de type « lexical decimal » qui mappe des plages d’IP (IPv4, IPv6) à des catégories
- Les recherches de plages ont été précalculées puis écrites dans une table SQLite et dans des ensembles triés Redis
- À chaque requête HTTP entrante, l’IP de la requête doit être comparée à des plages personnalisées d’autorisation/blocage, à des plages GeoIP et à des plages de réputation IP
Protocole de test
- Les tests ont été effectués sur un MacBook Air M2 avec Redis installé via Homebrew et une base SQLite locale
- Ils ont porté sur un jeu de données de plages existant (1,2 million d’entrées)
- Plusieurs ensembles d’IP ont été exécutés dans le même ordre sur SQLite et Redis
- À chaque multiple, le test a été exécuté 5 fois puis moyenné
Résultats des tests
- SQLite a largement battu Redis (dans ce cas d’usage très spécifique)
- SQLite était environ 3 fois plus rapide qu’une instance Redis locale
- Ces résultats sont obtenus avant même de prendre en compte la latence réseau
- Même si SQLite n’avait fait qu’égaler Redis, la suppression du temps réseau aurait déjà été un avantage
Ce qui manque au graphique
- Même avec des performances 2 fois moins bonnes dans le benchmark, SQLite pourrait rester plus rapide en pratique à cause de la latence réseau
- Même avec un serveur Redis très puissant, il reste des limites de bande passante réseau, de connexions et de latence inter-régions
- SQLite permet une montée en charge horizontale pratiquement infinie « gratuitement »
- L’onboarding est bien meilleur avec SQLite : les utilisateurs pourraient même ne pas se rendre compte de son utilisation
- Il était possible d’obtenir davantage de performances de Redis, mais il était difficile de convaincre les utilisateurs de modifier leur configuration Redis
Les résultats ne sont qu’un début
- Il a été démontré que SQLite est plus rapide que Redis, mais il existe de vrais compromis
- Les tests ci-dessus ne prennent pas en compte les écritures
- Pour gérer la concurrence entre lectures et écritures, il faut des connexions à la base, un pool de connexions, des transactions, etc.
- Comme une supercar électrique a du mal à transporter des blocs de béton, SQLite ne doit pas être utilisé pour un rôle auquel il n’est pas adapté
Construire une architecture de synchronisation
- Dans la v1 (Redis), lorsqu’un utilisateur mettait à jour des règles dans Wafris Hub, les règles du stockage Redis étaient mises à jour
- Avec SQLite, cela ne fonctionne pas, car il est impossible de « pousser » vers le serveur web
- Dans la v2 (SQLite), le fonctionnement devient : 1) l’utilisateur met à jour les règles dans Wafris Hub 2) le client vérifie à intervalles réguliers si les règles ont été mises à jour 3) si c’est le cas, il télécharge une base SQLite entièrement חדשה
- Cela réduit fortement la responsabilité des utilisateurs en matière d’installation et de configuration
- Le taux de réussite d’installation du client v2 a été multiplié par 3
Architecture distribuée avec SQLite
- Prenons une application Rails déployée chez un fournisseur cloud avec autoscaling activé
- Si les requêtes passent de 100 req/s à 10 000 req/s, les instances de calcul montent en charge, mais pas la base
- C’est en réalité l’une des principales raisons pour lesquelles les apps Rails tombent sous surcharge
- Synchroniser la base SQLite sur chaque instance de calcul résout ce problème en gardant tous les appels en local
Et pour les écritures ?
- L’application a été scindée entre un chemin de lecture (évaluation des règles) et un chemin d’écriture (reporting), puis le chemin d’écriture a été ignoré
- Le chemin d’écriture a été repensé ainsi : 1) reporting asynchrone vers Wafris Hub 2) envoi des rapports par lots 3) suppression complète des écritures en base côté client
- Cela ne fonctionnera peut-être pas pour d’autres, mais l’équipe se concentre uniquement sur les utilisateurs qui veulent un client Wafris rapide et simple à déployer
Conclusion
- L’équipe est très satisfaite de l’architecture v2 basée sur SQLite
- Elle aide déjà de nombreux sites à résister aux attaques et à rester en ligne
- Il est bien plus facile de démarrer, ce qui réduit le travail du support et les frictions côté utilisateur
- C’est une victoire pour un Internet plus sûr et mieux sécurisé
7 commentaires
SQLite est certes suffisamment bon, mais dans ce cas, j’ai quand même l’impression que ce n’était tout simplement pas un cas d’usage adapté à Redis...
Le fait que le benchmark ait été réalisé sur un M2, c’est un peu…
Alors il faut les mesurer toutes pour chaque instance AWS ? Vous en demandez vraiment beaucoup à l’open source.
Le test a été réalisé dans le même environnement serveur, est-ce que cela pose problème ?
Pour un benchmark, faut-il utiliser un CPU spécifique... ?
Quels problèmes cela pourrait-il poser avec ce qui a été fait sur un M2 ? (à part le fait que l’environnement de service réel n’utilise pas un processeur M2)
C’est bien ça, le problème. Expérimenter en laboratoire, puis affirmer : « ceci est parfait pour un usage commercial ! »