5 Punkte von GN⁺ 2025-01-17 | 1 Kommentare | Auf WhatsApp teilen
  • rqlite ist eine leichtgewichtige Open-Source-Distributed-Relational-Datenbank, in Go geschrieben und auf SQLite und Raft aufgebaut
  • Die Entwicklung begann 2014, mit Fokus auf Zuverlässigkeit und Qualität; selbst nach mehr als zehn Jahren Entwicklung und Deployment wurden in Produktionsumgebungen weniger als 10 Panic-Fälle gemeldet
  • Das Testen verteilter Systeme erfordert sorgfältige Überlegungen auf mehreren Ebenen und folgt einer Philosophie, die Qualität trotz Einfachheit bewahrt

Die Testpyramide: ein effektiver Ansatz

  • Die Tests von rqlite folgen der „Testpyramide“
    • Testpyramide: eine Struktur, die auf Unit-Tests basiert und Integrations-Tests sowie ein Minimum an End-to-End-Tests (E2E) umfasst
  • Sie liefert eine effiziente, leicht zu debuggende und zielgerichtete Test-Suite

Unit-Tests: der Kern der Qualität

  • Unit-Tests prüfen isolierte Komponenten und bieten ein Gleichgewicht aus Geschwindigkeit und Genauigkeit
  • Dank SQLite und einer „Shared-Nothing“-Architektur lässt sich der Großteil der Funktionen mit Unit-Tests abdecken
  • Vom gesamten rqlite-Code (ca. 75.000 Zeilen) entfallen rund 27.000 Zeilen auf Unit-Tests
  • Die Tests laufen in wenigen Minuten durch, was häufiges Testen während der Entwicklung ermöglicht

Tests auf Systemebene: Konsens validieren

  • Tests auf Systemebene validieren das Zusammenspiel zwischen dem Raft-Konsensmodul und SQLite
  • Wichtige Testpunkte:
    • Replikation von SQLite-Statements zwischen Nodes
    • Leseoperationen bei unterschiedlichen Konsistenzstufen
    • Validierung von Cluster-Fehlerbehebung und Leader-Wahl
  • Mit etwa 7000 Zeilen Testcode werden Interaktionen in Single-Node- und Multi-Node-Konfigurationen umfassend abgedeckt

End-to-End-Tests: eine minimale Schicht

  • End-to-End-Tests dienen als Smoke-Tests, die Systemstart, Clustering und grundlegendes Verhalten überprüfen
  • Sie sind in Python geschrieben und validieren zentrale Funktionen, indem ein echtes rqlite-Cluster ausgeführt wird
  • Beispiel: Validierung von Backups nach AWS S3
  • Mit rund 5000 Zeilen Testcode wird ein bewusst begrenzter Ansatz verfolgt, um Debugging-Kosten zu minimieren

Performance-Tests: die Grenzen ausloten

  • Performance-Tests bewerten Metriken wie:
    • maximale INSERT-Geschwindigkeit
    • Verarbeitung gleichzeitiger Queries
    • Vergleich von Speicher-, CPU- und Festplattennutzung
  • Dazu gehören Tests mit SQLite-Datenbanken von mehr als 2 GB, um Speicherverwaltung und Engpässe bei Schreibvorgängen auf die Festplatte zu analysieren
  • So werden Performance-Probleme erkannt und durch Optimierungen Stabilität sichergestellt

Erkenntnisse

  • Früh mit dem Testen beginnen
    • Unit-Tests sind der effektivste Weg, Vertrauen in das System aufzubauen
    • Das Schreiben von Unit-Tests sollte während der Entwicklung nicht aufgeschoben werden, da sich Bugs damit schneller finden lassen als mit Integrations- oder E2E-Tests
  • Testcode einfach halten
    • Eine Test-Suite ist nicht der Ort für komplexes Refactoring oder das starre Festhalten am DRY (Don't Repeat Yourself)-Prinzip
    • Wichtig ist, leicht verständlichen Code zu schreiben; zusätzlicher Boilerplate-Code sollte dabei akzeptiert werden
  • Tests selbst validieren
    • Beim Schreiben von Tests die erwarteten Ergebnisse vorübergehend ins Gegenteil verkehren und die Tests erneut ausführen
    • Ein korrekt geschriebener Test muss in diesem Fall fehlschlagen, wodurch Fehler im Testcode frühzeitig verhindert werden können
  • Testfehlschläge nicht ignorieren
    • Auch schwer verständliche oder seltene Testfehlschläge liefern wichtige Informationen über die Software
    • Schwer zu debuggende Fehlfälle sind oft eine Chance, kritische Schwächen im Code zu entdecken
  • Determinismus maximieren
    • Mechanismen schaffen, mit denen automatische Prozesse des Systems manuell ausgeführt werden können
    • Beispiel: Die Raft-Snapshot-Funktion läuft normalerweise halbautomatisch, wurde aber so entworfen, dass sie sich in Tests explizit auslösen lässt
  • Bewusst vorgehen (Be Deliberate)
    • Höherwertige Integrations- oder E2E-Tests werden nur dann ergänzt, wenn ihre Notwendigkeit klar belegt ist
    • Zu viele Tests können Entwicklungs- und Debugging-Geschwindigkeit verringern
  • Anwenden und iterieren
    • In Performance-Tests wurde der fsync-Aufruf als zentraler Engpass identifiziert; deshalb werden Raft-Log-Entries vor dem Schreiben auf die Festplatte komprimiert, um die Festplattennutzung zu optimieren
  • Effizienz priorisieren
    • Eine Test-Suite, die in wenigen Minuten durchläuft, ermöglicht schnelle Entwicklungsiterationen
    • Das ist ein entscheidender Vorteil für die Pflege und Lebendigkeit eines Open-Source-Projekts

Qualität zuerst

  • Die Testpyramide wird eingehalten, und jede Testebene ist so gestaltet, dass sie einen klaren Zweck erfüllt
  • Mit zunehmender Komplexität verteilter Systeme ist es entscheidend, die Einfachheit der Tests zu bewahren
  • Ziel ist es, eine zuverlässige und einfach zu betreibende Datenbank zu bauen

1 Kommentare

 
GN⁺ 2025-01-17
Hacker-News-Kommentare
  • Der erste Test ist am schwierigsten, lohnt sich aber; danach werden weitere Tests einfacher.

    • Parametrisierte Tests reduzieren Wiederholungen im Code und ermöglichen vielfältigere Tests.
    • Sie sind nützlich, wenn sich Constraints gründlich validieren lassen.
    • Property-Tests helfen dabei, Konsistenz und Invarianten zu überprüfen.
    • Es ist wichtig, Mutation Testing zu verwenden, um sicherzustellen, dass tatsächlich getestet wird.
  • Das Engagement für das Projekt ist beeindruckend.

  • Die Testpyramide ist nachvollziehbar, aber oft fehlen Ebenen, sodass man das schnell verbessern muss.

    • Wenn mehrere Teams zusammenarbeiten, ist das Auffüllen der E2E-Schicht oft eine Aufgabe, die niemand übernimmt.
    • Wenn man Authentifizierungsmechanismen wie Auth0 verwendet, werden Tests schwierig.
    • Ohne E2E-Tests kann das System leicht kaputtgehen.
    • Automatisierte E2E-Tests machen es leicht, Probleme zu identifizieren und ein Rollback durchzuführen.
  • In den FAQ scheint es einen Copy-paste-Fehler zu geben.

  • Man freut sich auf einen Jepsen-Bericht.

  • Das Videoformat hat ebenfalls gefallen.

  • Das Setup für Performance-Tests ist beneidenswert.

  • Man hat rqlite ausprobiert, und es vermittelt seine Einfachheit gut.

  • Es wird nach Meinungen zu deterministischen Simulationstests gefragt.

  • Man fragt sich, ob rqlite in realen Umgebungen eingesetzt wird.

  • rqlite ist ein originelles und geniales Projekt.