5 points par GN⁺ 2025-01-17 | 1 commentaires | Partager sur WhatsApp
  • rqlite est une base de données relationnelle distribuée open source légère, écrite en Go et développée sur la base de SQLite et de Raft
  • Son développement a commencé en 2014, avec la priorité donnée à la fiabilité et à la qualité ; après plus de 10 ans de développement et de déploiement, moins de 10 cas de panic seulement ont été signalés en production
  • Les tests des systèmes distribués exigent une attention minutieuse à plusieurs couches, selon une philosophie qui vise à préserver la qualité dans la simplicité

La pyramide des tests : une approche efficace

  • Les tests de rqlite respectent la « pyramide des tests »
    • Pyramide des tests : une structure fondée sur les tests unitaires, incluant des tests d’intégration et un minimum de tests end-to-end (E2E)
  • Elle fournit une suite de tests efficace, facile à déboguer et orientée vers des objectifs précis

Tests unitaires : le cœur de la qualité

  • Les tests unitaires vérifient des composants indépendants et offrent un équilibre entre rapidité et précision
  • Grâce à SQLite et à une architecture « shared nothing », la plupart des fonctionnalités peuvent être couvertes par des tests unitaires
  • Sur l’ensemble du code de rqlite (environ 75 000 lignes), les tests unitaires représentent environ 27 000 lignes
  • Les tests s’exécutent en quelques minutes, ce qui permet de les lancer fréquemment pendant le développement

Tests au niveau système : validation du consensus

  • Les tests au niveau système valident l’interaction entre le module de consensus Raft et SQLite
  • Principaux éléments testés :
    • Réplication des instructions SQLite entre les nœuds
    • Opérations de lecture à différents niveaux de cohérence
    • Validation de la reprise après panne du cluster et de l’élection du leader
  • Environ 7 000 lignes de code de test couvrent de manière approfondie les interactions dans des configurations à nœud unique et à plusieurs nœuds

Tests end-to-end : une couche minimale

  • Les tests end-to-end jouent le rôle de smoke tests en vérifiant le démarrage du système, la mise en cluster et le fonctionnement de base
  • Ils sont écrits en Python et valident les fonctions principales en exécutant un véritable cluster rqlite
  • Exemple : vérification des sauvegardes vers AWS S3
  • Le code de test est limité à environ 5 000 lignes, selon une approche volontairement restreinte pour minimiser les coûts de débogage

Tests de performance : éprouver les limites

  • Les tests de performance évaluent des métriques telles que :
    • Vitesse maximale des INSERT
    • Traitement des requêtes concurrentes
    • Comparaison de l’utilisation de la mémoire, du CPU et du disque
  • Ils incluent des tests de bases SQLite de plus de 2 Go afin d’analyser la gestion mémoire et les goulots d’étranglement liés aux écritures disque
  • Ils permettent de détecter les problèmes de performance et de garantir la stabilité grâce aux optimisations

Leçons retenues

  • Commencer les tests dès le départ
    • Les tests unitaires sont le moyen le plus efficace d’instaurer la confiance dans le système
    • Il ne faut pas remettre à plus tard l’écriture des tests unitaires pendant le développement, car ils permettent de détecter les bugs plus vite que les tests d’intégration ou les tests E2E
  • Garder le code de test simple
    • Une suite de tests n’est pas l’endroit où s’obstiner à faire de gros refactorings ou à appliquer à tout prix le principe DRY(Don't Repeat Yourself)
    • Il est important d’écrire un code facile à comprendre, et d’accepter aussi du code boilerplate supplémentaire
  • Valider les tests
    • Lors de l’écriture d’un test, réexécuter temporairement le test en inversant le résultat attendu
    • Un test correctement écrit doit alors échouer, ce qui permet d’éviter à l’avance les erreurs dans le code de test
  • Ne pas ignorer les échecs de test
    • Même les échecs de test rares ou difficiles à comprendre apportent des informations importantes sur le logiciel
    • Les cas d’échec difficiles à déboguer peuvent souvent devenir une occasion de découvrir des défauts critiques dans le code
  • Maximiser le déterminisme
    • Mettre en place des mécanismes permettant d’exécuter manuellement les processus automatiques du système
    • Exemple : la fonction de snapshot de Raft s’exécute généralement de manière semi-automatique, mais a été conçue pour pouvoir être déclenchée explicitement pendant les tests
  • Agir de manière délibérée (Be Deliberate)
    • N’ajouter des tests d’intégration de niveau supérieur ou des tests E2E que lorsque leur nécessité est clairement démontrée
    • Des tests excessifs peuvent ralentir le développement et le débogage
  • Appliquer et itérer
    • Les appels à fsync ont été identifiés comme un goulot d’étranglement majeur lors des tests de performance, et l’utilisation du disque a été optimisée en compressant les entrées du journal Raft avant leur écriture sur le disque
  • Mettre l’accent sur l’efficacité
    • Conserver une suite de tests capable de s’exécuter en quelques minutes permet un développement itératif rapide
    • C’est un avantage essentiel pour maintenir et faire vivre un projet open source

La qualité avant tout

  • La pyramide des tests est respectée, et chaque couche de test est conçue avec un objectif clair
  • À mesure que la complexité des systèmes distribués augmente, préserver la simplicité des tests devient essentiel
  • L’objectif est de construire une base de données fiable et facile à exploiter

1 commentaires

 
GN⁺ 2025-01-17
Commentaires sur Hacker News
  • Le premier test est le plus difficile, mais il vaut la peine d’être ajouté. Les tests suivants deviennent plus faciles.

    • Les tests paramétrés réduisent le code répétitif et permettent de couvrir divers cas.
    • C’est utile lorsqu’il est possible de valider rigoureusement les contraintes.
    • Les tests de propriétés aident à vérifier la cohérence et les invariants.
    • Il est important d’utiliser des tests de mutation pour vérifier que l’on teste réellement quelque chose.
  • L’engagement envers le projet est impressionnant.

  • La pyramide des tests est compréhensible, mais il arrive souvent qu’il manque certains niveaux, ce qui impose de corriger cela rapidement.

    • Lorsqu’on collabore avec plusieurs équipes, remplir la couche e2e devient une tâche dont personne ne se charge.
    • Lorsqu’on utilise un mécanisme d’authentification comme Auth0, les tests sont difficiles.
    • Sans tests e2e, le système peut facilement se casser.
    • Les tests e2e automatisés permettent d’identifier facilement les problèmes et d’effectuer un rollback.
  • Il semble y avoir une erreur de copier-coller dans la FAQ.

  • Le rapport Jepsen est attendu avec impatience.

  • Le format vidéo a aussi été apprécié.

  • La configuration des tests de performance fait envie.

  • Quelqu’un a utilisé rqlite et trouve qu’il transmet bien sa simplicité.

  • Quelqu’un demande des avis sur les tests de simulation déterministe.

  • Quelqu’un se demande si rqlite est utilisé en production.

  • rqlite est un projet original et génial.