lobste.rs passe de MariaDB à SQLite
(lobste.rs)- Après être passé de MariaDB à SQLite, lobste.rs a géré sans problème le pic de trafic du lundi, avec une baisse de l’utilisation CPU et mémoire et une réactivité perçue plus rapide
- L’arrêt du VPS MariaDB séparé permet de réduire de moitié les coûts de VPS, et clôt le chantier de migration lancé en 2019
- Lors du premier déploiement, le trafic en lecture seule a suffi à porter tous les CPU à 100 %, ce qui a entraîné un rollback ; le redéploiement a réussi après la correction de deux scans complets de grandes tables et d’un problème N+1
- Des fonctions utilisateur SQLite ont été utilisées pour implémenter
regexp,ifetstddev, tout en devant résoudre des contraintes de compatibilité comme l’absence de prise en charge des unsigned bigint etNOCASE, qui ne traite que l’ASCII - Changer la base de données sous-jacente sans accès à la base de production exige des données de test de taille réaliste, une suite de tests, des checklists de déploiement/rollback et une collaboration étroite
Des discussions de 2019 au passage à SQLite
- Les discussions sur la migration ont commencé en 2019 avec l’issue #539
- À l’époque, MySQL avait été proposé comme alternative pour tenir compte de la compatibilité avec MariaDB, mais une discussion sur une migration vers PostgreSQL était déjà en cours
- En 2025, Rahul a mentionné l’acquisition de MariaDB par K1, ce qui a relancé les discussions détaillées sur la migration vers PostgreSQL
- En février, Rahul a demandé si lobsters pouvait fonctionner sur SQLite, et la participation au projet s’est réellement accélérée à partir de juin 2025
- La première PR, en août 2025, a été interrompue puis fermée par GitHub comme PR ancienne, sans possibilité de la rouvrir
- La deuxième PR incluait des tests de performance, un script maison de migration entre bases de données et du debugging lié à l’intégrité des données
- Aucun des scripts existants de conversion MariaDB/MySQL→SQLite ne répondant aux besoins, un script dédié a été écrit
Échec du premier déploiement et correction des goulets d’étranglement
- Le premier déploiement, le 21 février, a été effectué avec une checklist de déploiement préparée à l’avance, mais juste après le déploiement, le seul trafic en lecture seule a fait monter tous les CPU à 100 %
- La cause n’ayant pas été trouvée, un rollback a été effectué
- Sans accès à la base de production, il était difficile de reproduire à l’avance les vrais problèmes de performance
- Deux jours après cet échec, une troisième et dernière PR a été ouverte pour corriger les problèmes liés à la recherche et ajouter un script de génération de données en volume permettant de créer en local un jeu de données représentant la moitié des données réelles de lobsters
- La génération des données a pris une semaine
- Les goulets d’étranglement du premier déploiement provenaient de deux requêtes provoquant des scans complets sur de grandes tables, ainsi que d’un problème N+1 distinct
- Les correctifs changement 1, changement 2 et changement 3 ont été intégrés
- Le matin du déploiement, un journal des requêtes lentes a été ajouté afin de pouvoir suivre d’éventuels problèmes de performance supplémentaires
Résultats du redéploiement et baisse des coûts d’exploitation
- Lors du deuxième déploiement, le 11 juillet, les checklists de déploiement et de rollback ont été préparées à nouveau ; après la bascule, le site a continué à fonctionner normalement et l’utilisation CPU et mémoire est restée stable
- En surveillant les métriques du site et IRC, les problèmes signalés ont été traités immédiatement via correctif 1 et correctif 2
- Le pic de trafic du lundi n’a posé aucun problème, et une baisse de l’utilisation CPU et mémoire ainsi qu’une réactivité perçue plus rapide ont été constatées
- L’arrêt du VPS MariaDB permettra de réduire de moitié les coûts de VPS, et l’issue de migration est également clôturée
Les contraintes de SQLite par rapport à MariaDB
- La prise en charge des fonctions définies par l’utilisateur (UDF) dans la gem SQLite a été utilisée pour implémenter dans SQLite les fonctions absentes
regexp,if,stddev- Cela a évité d’ajouter trop de contournements pour les migrations SQL
- SQLite ne prenant pas en charge les unsigned bigint utilisés pour certains ID dans MariaDB, ils ont été remplacés par des bigint
- Les règles de tri sont également plus limitées que dans MariaDB
- MariaDB utilisait
utf8mb4_general_ci, tandis que SQLite appliqueNOCASE NOCASEne prend pas en charge le repliement complet de casse UTF et ne traite que les caractères ASCII
- MariaDB utilisait
- Pour les tables de recherche plein texte de SQLite, les Contentless-Delete Tables, approche recommandée mais non activée par défaut, conviennent bien
- Les paramètres PRAGMA par défaut de Rails fonctionnent correctement pour lobsters
- Les migrations de base de données étant propres à chaque DB, les anciennes migrations ont été déplacées dans le répertoire old_migrations afin que
db:migratecontinue de fonctionner
- Les migrations de base de données étant propres à chaque DB, les anciennes migrations ont été déplacées dans le répertoire old_migrations afin que
Enseignements sur les tests et la collaboration
- Dans la codebase de lobsters, on trouve le parseur de recherche et heinous_inline_partials, un hack destiné à accélérer le rendu
- La suite de tests a permis de vérifier la justesse du passage à SQLite sans grands tests manuels
- Une communication fluide entre les participants a été déterminante pour la réussite de la migration
- Remplacer la base de données sous-jacente sans accès à la base de production a été très difficile
- Si le même travail devait être refait, il faudrait préparer à l’avance un volume de données réaliste
- Si les tests avaient pu être configurés pour échouer en cas de scan complet de table, le problème de performance du premier déploiement aurait pu être détecté à l’avance
- Il faut des outils permettant de créer facilement un jeu de données proche de la production, sans devoir l’écrire soi-même ni attendre une semaine
1 commentaires
Réactions sur Lobste.rs
@thomas0 a pris en charge avec soin la migration de grande ampleur, @355E3B a aidé à la planification et à l’exploitation, et grâce aux contributeurs liés à #539 ainsi qu’à #lobsters, même la période en lecture seule s’est passée dans la bonne humeur
À l’origine, PostgreSQL était prévu dans #539, mais comme le volontaire qui a réellement assuré le travail utilisait SQLite et qu’on voulait éviter une solution plus grosse et plus complexe que les besoins réels, le choix s’est porté sur SQLite. PostgreSQL reste le choix par défaut habituel, mais il implique la charge d’exploiter, d’ajuster et de maintenir un service séparé
Fait surprenant, après la migration, l’usage CPU et RAM a diminué, et des graphiques détaillés figurent à la fin de #539
Chaque fois que je vois la configuration par défaut de SQLite, je suis surpris. Ce serait bien d’avoir un mode pour les nouveaux projets qui active par défaut WAL, les clés étrangères,
synchronous=NORMAL, etc., et il y a probablement d’autres valeurs par défaut à améliorerJe suis curieux de connaître la stratégie de sauvegarde de la base SQLite. J’aimerais savoir s’ils utilisent un outil comme litestream ou une autre méthode
Je me demande comment lobste.rs gère les opérations de mise à jour
SQLite a déjà une fonction iif, mais le fait même d’exploiter des fonctions définies par l’utilisateur dans ce domaine est aussi intéressant
Je pensais que SQLite ne prenait pas en charge le même niveau de concurrence que PostgreSQL ou MySQL, mais à lire le document d’ensemble, faut-il comprendre qu’il peut désormais être utilisé par plusieurs processus, à condition que lobste.rs tourne sur un seul serveur ?
Je voudrais aussi savoir si les processus se synchronisent à l’intérieur de SQLite via de la mémoire partagée ou
mmap, avec une seule écriture simultanée mais plusieurs lectures autorisées, et si les écritures en attente patientent à leur tour dans une filePour l’attente des écritures, il semble qu’il n’y ait pas de file explicite en mémoire partagée, mais plutôt une courte attente suivie d’une nouvelle tentative. Il n’est pas certain que l’usage de SQLite sur plusieurs machines soit totalement impossible, mais comme l’approche simple consistant à partager le fichier via NFS est explicitement déconseillée, considérer qu’il s’agit d’un serveur unique est une approximation raisonnable, d’autant qu’une seule machine peut aujourd’hui déjà absorber une charge importante
busy_timeoutfonctionne Références : https://fractaledmind.github.io/2024/04/… , https://kerkour.com/sqlite-for-servers et https://www.sqlite.org/lang_transaction.html#immediate ; voir notamment la partieSQLITE_BUSYdu deuxième lien et le troisième lien pour plus de détailsUpgrade impressionnante, avec des détails très bien documentés dans la PR, l’issue et le Gist. Je me demande si ce changement a entraîné des différences fonctionnelles, par exemple pour la recherche du site, et quelle est actuellement la taille du fichier SQLite sur disque
Il me semble avoir vu quelque part que le fichier SQLite faisait 3,8 Go