Ruf mich vielleicht an: SolrCloud, Jepsen und schlaffe Netzwerke
TL;DR; Wir haben SolrCloud mit Bridge-, zufälligen transitiven und festen transitiven Netzwerkpartitionen unter Verwendung von Jepsen getestet und keine Probleme…
TL;DR;
Wir haben SolrCloud mit Bridge-, zufälligen transitiven und festen transitiven Netzwerkpartitionen unter Verwendung von Jepsen getestet und keine Probleme mit Datenverlusten sowohl bei Vergleichen-und-Setzen-Operationen als auch bei Einfügungen festgestellt. Es wurden ein größerer und ein paar kleinere Fehler gefunden. Die meisten davon wurden in Solr 4.10.2 behoben und andere werden in Kürze behoben werden. Wir arbeiten daran, bessere Jepsen-Tests für Solr zu schreiben und sie in die Build-Server von Solr zu integrieren. Dies ist ein Prozess, der noch nicht abgeschlossen ist.
Seit Kyle Kingsbury, bekannt durch sein Twitter-Handle @aphyr, seine ausgezeichnete Serie von Blogbeiträgen über Netzwerkpartitionen und das Verhalten verteilter Systeme unter diesen Partitionen veröffentlicht hat, fragen sich die Benutzer von Apache Solr, wie Solr angesichts solcher Partitionen funktioniert. Viele von Jepsen getestete verteilte Systeme fielen mit katastrophalen Datenverlusten, nichtlinearen Verläufen, Cluster-Lockups und „sofortigen“ Leader-Wahlen aus, die gar nicht so schnell waren. In einigen Fällen waren die Gründe für die Fehler einfache Bugs, in anderen Fällen gab es architektonische Probleme, bei denen verteilte Systeme ohne einen tatsächlichen, von Experten geprüften verteilten Konsensalgorithmus geschrieben wurden.
Als wir die verteilten Funktionen von Solr entwickelten, hatten wir gute Gründe, uns für Apache ZooKeeper zu entscheiden. Er basiert auf einer soliden Theorie und das Ausmaß an Tests und Skalierung, das Apache ZooKeeper durchlaufen hat, während es von Apache Hadoop, Apache HBase und anderen genutzt wurde, ist in der Open Source-Welt außergewöhnlich. Als Jepsen den Apache ZooKeeper testete und ihn als ausgereiftes, gut konzipiertes und kampferprobtes System empfahl, war dies eine starke Bestätigung für unsere Designentscheidungen, aber wir hatten dennoch unsere Zweifel. Dieser Beitrag und die damit verbundene Arbeit zielt darauf ab, diese Zweifel zu beantworten.
Was ist Jepsen und warum sollte mich das interessieren?
Jepsen ist ein Tool, das Netzwerkpartitionen simuliert und testet, wie sich verteilte Datenspeicher unter diesen Bedingungen verhalten. Einfach ausgedrückt, schneidet Jepsen einen oder mehrere Knoten von der Kommunikation mit anderen Knoten in einem Cluster ab, während sie während der Partition weiterhin Daten einfügen, aktualisieren oder nachschlagen, sowie nach der Heilung der Partition, um festzustellen, ob sie Daten verlieren, inkonsistente Daten lesen oder nicht mehr verfügbar sind.
Warum ist das wichtig? Dies ist wichtig, weil Netzwerke unzuverlässig sind. Es ist nicht nur das Netzwerk. Pausen bei der Garbage Collection in der JVM oder starke Aktivitäten Ihres Nachbarn in einem Server, der in der Cloud läuft, können sich ebenfalls in Verlangsamungen äußern, die von einer Netzwerkpartition kaum zu unterscheiden sind. Selbst in den am besten verwalteten Rechenzentren gehen Dinge schief: Festplatten fallen aus, Switches funktionieren nicht, Stromversorgungen haben Kurzschlüsse, RAM-Module sterben ab und ein verteiltes System, das in großem Maßstab läuft, sollte versuchen, solche Probleme so weit wie möglich zu umgehen.
Jepsen richtet den zu testenden Datenspeicher auf 5 verschiedenen Hosts ein (der Einfachheit halber typischerweise Linux-Container auf einem einzigen Host). Es erstellt einen Client für den zu testenden Datenspeicher, der auf jeden der 5 Knoten zeigt, um Anfragen zu senden. Es erstellt auch spezielle Clients, die als „Nemesis“ bezeichnet werden und die, anstatt mit einem Datenspeicher zu kommunizieren, Chaos im Cluster anrichten, indem sie z.B. die Verbindungen zwischen den Knoten mit iptables kappen. Dann stellt er gleichzeitig Anfragen an verschiedene Knoten, während er das Netzwerk abwechselnd partitioniert und wiederherstellt. Am Ende des Testlaufs heilt es den Cluster, wartet, bis sich der Cluster erholt hat und überprüft dann, ob der Zwischen- und Endzustand des Systems den Erwartungen entspricht. Eigentlich ist es noch komplizierter, aber für die Zwecke dieser Diskussion reicht das aus.
Die SolrCloud Architektur in Kürze
Bevor wir uns mit den Jepsen-Tests befassen, ist es hilfreich, die SolrCloud-Architektur ein wenig zu verstehen.
Ein SolrCloud-Cluster enthält einen oder mehrere verteilte Indizes, die als Collections bezeichnet werden. Jede Collection ist in Shards unterteilt (um die Schreibkapazität zu erhöhen) und jeder Shard hat ein oder mehrere Replikate (um die Abfragekapazität zu erhöhen). Ein Replikat aus jedem Shard wird als Leader gewählt, der die zusätzliche Aufgabe hat, jeder Aktualisierung eine ‚Version‘ hinzuzufügen, bevor sie an die verfügbaren Replikate gestreamt wird. Das bedeutet, dass der Schreibverkehr für einen bestimmten Shard zuerst den Leader des Shards erreicht und dann synchron auf alle verfügbaren Replikate repliziert wird. Ein Solr-Knoten (eine JVM-Instanz) kann mehrere Replikate hosten, die zu verschiedenen Shards oder sogar zu verschiedenen Sammlungen gehören.
Alle Knoten in einem SolrCloud-Cluster kommunizieren mit einem Apache ZooKeeper-Ensemble (eine ungerade Anzahl von Knoten, typischerweise 3 oder mehr für einen Produktionseinsatz). ZooKeeper speichert unter anderem:
- Der Cluster-Status des Systems (Details zu Sammlungen, Scherben, Replikaten und Anführern)
- Die Menge der aktiven Knoten zu einem bestimmten Zeitpunkt (ermittelt durch Heartbeat-Nachrichten, die von Solr-Knoten an ZooKeeper gesendet werden)
- Der Status jeder Replik (aktiv, wiederherstellend oder ausgefallen)
- Warteschlangen für die Wahl des Anführers (eine Warteschlange mit Live-Replikaten für jeden Shard, so dass der erste in der Liste versucht, der Anführer zu werden, sofern er bestimmte Bedingungen erfüllt)
- Konfigurationen (Schema usw.), die von allen Replikaten in einer Sammlung gemeinsam genutzt werden.
Jede Solr-Replik hält einen Lucene-Index (aus Leistungsgründen teilweise im Speicher und auf der Festplatte) und ein Transaktionsprotokoll mit Vorausschau. Eine Aktualisierungsanfrage an SolrCloud wird erstens in das Transaktionsprotokoll geschrieben, zweitens in den Lucene-Index und drittens, wenn die Replik ein Leader ist, synchron an alle verfügbaren Replikate desselben Shards gestreamt. Mit einem Commit-Befehl werden die Lucene-Indizes auf die Festplatte geschrieben und die neuen Dokumente für die Suchenden sichtbar gemacht. Daher sind die Suchen letztendlich konsistent. Echtzeitabfragen von Dokumenten können jederzeit unter Verwendung des eindeutigen Schlüssels des Dokuments durchgeführt werden und liefern den Inhalt entweder aus dem Lucene-Index (für bestätigte Dokumente) oder aus dem Transaktionsprotokoll für nicht bestätigte Dokumente. Solche Echtzeitabfragen werden immer vom Leader für Konsistenz beantwortet.
Wenn ein Leader stirbt, wird ein neuer Leader aus den ‚Live‘-Replikaten gewählt, solange der letzte veröffentlichte Status des Replikats aktiv war. Die gewählte Replik synchronisiert sich mit allen anderen Live-Replikaten (in beide Richtungen), um sicherzustellen, dass sie über die neuesten Aktualisierungen aller Replikate im Cluster verfügt, bevor sie die Führungsrolle übernimmt.
Wenn ein Leader-Knoten nicht in der Lage ist, eine Aktualisierung an ein Replikat zu senden, aktualisiert er ZooKeeper, um das Replikat als inaktiv zu veröffentlichen, und startet gleichzeitig einen Hintergrund-Thread, um das Replikat zur Wiederherstellung aufzufordern. Beide Aktionen zusammen stellen sicher, dass ein Replikat, das eine Aktualisierung verliert, weder zum Leader wird noch Verkehr von anderen Solr-Knoten und Smart Clients erhält, bevor es sich von einem Leader-Knoten erholt.
Solr-Knoten (und Solrj-Clients) leiten Suchanfragen an aktive Replikate weiter. Die aktiven Replikate bedienen weiterhin den Suchverkehr, auch wenn sie die Verbindung zu ZooKeeper verlieren. Wenn der gesamte ZooKeeper-Cluster nicht mehr verfügbar ist, wird der Zustand des Clusters praktisch eingefroren, d.h. Suchanfragen werden weiterhin von demjenigen bedient, der zum Zeitpunkt des Ausfalls von ZooKeeper aktiv war, aber eine Wiederherstellung der Replikate kann in dieser Zeit nicht erfolgen. Replikate, die nicht aktiv sind, dürfen jedoch Ergebnisse zurückgeben, wenn sie auf irgendeine Weise von einer nicht verteilten Anfrage erreicht werden. Dies ist für die Synchronisierung während der Leader-Wahl erforderlich und manchmal auch für die Fehlersuche nützlich.
Zusammenfassend lässt sich sagen, dass SolrCloud bei Schreibvorgängen die Konsistenz der Verfügbarkeit vorzieht und bei Suchvorgängen die Konsistenz bevorzugt, aber unter schwerwiegenden Bedingungen, z.B. wenn ZooKeeper nicht verfügbar ist, dazu übergeht, möglicherweise veraltete Daten zu liefern. In der Welt des CAP-Theorems macht dies Solr zu einem CP-System mit einigen abschwächenden Heuristiken, die versuchen, die Verfügbarkeit unter bestimmten Umständen aufrechtzuerhalten.
Jepsen Umgebung
Die Jepsen-Testumgebung besteht aus der Einrichtung von 5 Hosts für den Datenspeicher. Wir haben diese als Linux-Container auf einem Ubuntu-Host eingerichtet und ihnen die Namen n1, n2, n3, n4 und n5 gegeben. Auf jedem Knoten läuft Java 1.8.0_25 mit Solr 4.10.2 und ZooKeeper 3.4.6. Sowohl ZooKeeper als auch Solr-Cluster haben also jeweils 5 Knoten. Der Grund für diese Topologie war, sicherzustellen, dass ZooKeeper zusammen mit den Solr-Knoten partitioniert wird, um eine genauere Partitionierung zu simulieren, bei der Solr nicht nur die Fähigkeit verliert, mit anderen Solr-Knoten zu kommunizieren, sondern auch mit einem ZooKeeper-Knoten, der das Quorum verloren hat, zu sprechen.
Die einzige Konfigurationsänderung gegenüber Solr 4.10.2 war die Einstellung von Verbindungs- und Lesezeitüberschreitungen für Abfrage- und Aktualisierungsanfragen zwischen den Shards. Dies ist eine wichtige und empfohlene Einstellung für alle produktiven Implementierungen. Hier ist die vollständige solr.xml von einem der Solr-Knoten:
Ich habe die schemalose Konfiguration für Solr verwendet. Diese wurde mit dem folgenden Befehl auf ZK hochgeladen:
Die Solr-Knoten wurden mit den neuen Solr bin-Skripten gestartet:
Bei jedem Test wurde zunächst die vom vorherigen Test erstellte Sammlung gelöscht und dann eine neue Sammlung mit 5 Shards und 3 Replikaten erstellt. Wir haben die gleichen Tests mit einer Sammlung mit nur 1 Shard und 5 Replikaten durchgeführt und die Ergebnisse waren ähnlich.
Flux, der Clojure-Client für Solr, wurde verwendet, um Anfragen an Solr zu stellen. Der Flux-Client umhüllt den Java-basierten Solrj-Client und wir erstellen das Äquivalent eines einfachen, dummen HTTP-Clients für Solr, d.h. einen Client, der nicht clusterfähig ist und keine intelligente Routing-Logik enthält.
Wir haben zwei Jepsen-Clients für Solr geschrieben, die auf dem basieren, was bereits für andere Datenspeicher existiert:
- cas-set-client – Dieser Client simuliert eine Menge von Ganzzahlen, indem er ein Dokument mit Hilfe von get in Echtzeit liest, einem mehrwertigen Feld eine Ganzzahl hinzufügt und es mit der zuletzt aus Solr gelesenen Version aktualisiert.
- create-set-client – Dieser Client implementiert eine Menge von Ganzzahlen, indem er ein neues Dokument für jede Ganzzahl als eindeutigen Schlüssel schreibt.
Nun, da unsere Umgebung eingerichtet ist, können wir damit beginnen, Solr gegen verschiedene Nemesis zu testen, beginnend mit der Bridge-Nemesis.
Nicht-transitive Netzwerkpartitionen, auch bekannt als die Brücke
Die Brücke Nemesis wird in der offiziellen Dokumentation des Codes selbst am besten beschrieben:
„Ein Groll, der das Netzwerk in zwei Hälften teilt, aber einen Knoten in der Mitte beibehält, der eine ununterbrochene bidirektionale Konnektivität zu beiden Komponenten hat.“
Bei einem Cluster mit fünf Knoten {n1, n2, n3, n4, n5} wird die Bridge Nemesis beispielsweise zwei gleichwertige Netzwerkpartitionen {n1, n2} und {n4, n5} erstellen, so dass der Knoten n3 mit beiden Partitionen kommunizieren kann. ZooKeeper tut in diesem Szenario das Richtige und erstellt eine Mehrheitspartition, indem er n3 entweder mit {n1, n2} oder mit {n4, n5} kombiniert. In ähnlicher Weise wird auch unser SolrCloud-Cluster in einer der beiden oben genannten Konfigurationen in eine Mehrheits- und eine Minderheitspartition unterteilt.
Lassen Sie uns also sehen, wie sich SolrCloud unter einer solchen Partition schlägt:
Wiederholte Durchläufe ergaben ähnliche Ergebnisse. Alle bestätigten Schreibvorgänge waren verfügbar, nachdem die Partitionen geheilt waren (aufgezeichnet in der ok-fraction in der obigen Ausgabe). Einige erfolglose Anfragen (Verbindungs- und Lesezeitüberschreitungen) waren ebenfalls erfolgreich (aufgezeichnet in der recovered-fraction).
Wenn Sie in diesem Szenario versuchen, mehrere Updates mit der gleichen Version zu senden, ist nur eines erfolgreich und die anderen schlagen mit einer Fehlermeldung wie der folgenden fehl:
Optimistische Gleichzeitigkeit ist also in SolrCloud gut implementiert und wenn Sie bei einer Aktualisierung die _Version_ angeben, können Sie sicher sein, dass nur eine Aktualisierung unter Gleichzeitigkeit erfolgreich ist und die Konsistenz unter der Bridge-Partition erhalten bleibt.
Was passiert, wenn wir anfangen, mehrere (neue) Dokumente zu senden, anstatt versionierte Updates zu verwenden? Da Einfügungen nicht miteinander in Konflikt geraten, sollten wir in der Lage sein, jede Einfügung zu lesen, für die SolrCloud eine Bestätigung gesendet hat.
Die Ergebnisse waren wie erwartet:
Sobald eine Partitionierung stattfindet, sehen wir einige Lesezeitüberschreitungen, da laufende Anfragen betroffen sind und neue Anführer gewählt werden. Die meisten Ausfälle sind auf Zeitüberschreitungen bei Verbindungen zurückzuführen, da die Knoten nicht in der Lage sind, Anfragen an Anführer weiterzuleiten, die sich in einer anderen Partition befinden.
Interessant ist hier, dass alle Solr-Knoten mit mindestens einer ZooKeeper-Instanz (n3) kommunizieren können, die Teil der ZooKeeper-Mehrheitspartition ist, was bedeutet, dass alle Knoten „live“ sind (aus Sicht von ZooKeeper), aber nicht unbedingt verfügbar. Das bedeutet auch, dass man angesichts einer solchen Partition plötzlich die Möglichkeit verlieren kann, einige oder alle Replikate zu kontaktieren (je nach Zustand des Clusters) sowie die Möglichkeit zu verlieren, die Anfrage von beliebigen Knoten im Cluster an den Leader weiterzuleiten. Denken Sie daran, dass wir stumme Clients verwenden, die nicht automatisch an den Leader weiterleiten. Vermutlich kann die Verfügbarkeit viel besser sein, wenn wir CloudSolrServer verwenden, einen clusterfähigen Solr-Client, der Anfragen automatisch an den richtigen Leader weiterleiten kann. Wir planen, dies zu testen und Ihnen bald darüber zu berichten.
Zufällige transitive Partitionen
Okay, Bridge-Partitionen sind vielleicht etwas exotisch und selten, aber transitive Partitionen, bei denen der Cluster vollständig in zwei Hälften geteilt wird, sind häufiger. Wir erstellen also während des Testlaufs mehrere Partitionen, und jedes Mal wird eine andere Gruppe von Knoten ausgewählt, die vom Rest abgeschnitten wird. Zum Beispiel kann die Menge der Knoten in einem Lauf in {n3, n5} und {n1, n2, n4} aufgeteilt werden, wodurch eine Mehrheits- und eine Minderheitspartition sowohl von ZooKeeper- als auch von SolrCloud-Knoten entsteht.
Wie gut kommt SolrCloud mit Vergleichen und Setzen sowie mit Einfügeoperationen zurecht?
Hier ist ein typischer Durchlauf für compare-and-set:
Hier sind die Ergebnisse für die Einsätze: (denken Sie daran, dass es sich um Verhältnisse handelt):
In diesem Szenario würden wir erwarten, dass nur die Mehrheitspartition Schreibvorgänge akzeptiert und die Minderheitspartition alle Schreibvorgänge ablehnt, da sie nicht mit ZooKeeper kommunizieren kann. Wie wir aus den Protokollen des obigen create-set-client Tests ersehen können, passiert genau das. In der Zeit, in der der Leader gewählt wird, werden einige Anfragen abgebrochen und alle Aktualisierungen auf einem Knoten in der Minderheitspartition werden mit folgendem Fehler abgebrochen:
Feste transitive Partitionen
Wie wäre es statt einer wechselnden Menge isolierter Knoten mit einer festen Menge von Knoten, die jedes Mal neu partitioniert werden? Zugegeben, das ist etwas einfacher, aber da eine solche Nemesis in Jepsen bereits existierte, dachten wir, dass wir sie der Vollständigkeit halber ausführen sollten.
Erneut keine Probleme beim Vergleichen und Einstellen von Updates:
als auch für Beilagen:
Partitionen mit einem Knoten
Ich habe versucht, diese Tests auszuführen, aber Jepsen schlägt mit einem Fehler fehl, den ich noch nicht beheben konnte. @aphyr sagte, dass ich dafür ein passwortloses sudo benötige, aber auch das hilft nicht. In Anbetracht der Tatsache, dass SolrCloud seit dem ersten Tag über Leader Failover- und ChaosMonkey-ähnliche Tests verfügt und dass wir Zookeeper für die Zustandsverwaltung verwenden und einige der komplexeren Szenarien bereits bestanden hatten, habe ich diese Tests im Interesse der Zeit übersprungen. Aber das ist etwas, das ich mir später der Vollständigkeit halber noch ansehen möchte.
Wanzen, Wanzen und Wanzen
Es lief nicht immer alles glatt. Ich bin auf Bugs gestoßen, einige kleinere und einige nicht so kleine. Einige der Probleme, die ich mit Jepsen gefunden habe, waren:
SOLR-6530 – Commits unter der Netzwerkpartition können jeden Knoten in den Zustand ‚down‘ versetzen
Dies war ein schwerwiegender Fehler, der bei unseren Tests gefunden wurde. Er betrifft Solr-Versionen nach 4.8 und tritt nur in Clustern auf, in denen explizite Commit-Anfragen gestellt werden. Benutzer, die autoCommit in ihren Konfigurationen haben, sind nicht betroffen. Ich habe diesen Fehler gefunden, weil die erste compare-and-set Testimplementierung explizite Commits verwendet hat, um die neueste Version eines Dokuments zu lesen, bevor es aktualisiert wurde. Jetzt verwende ich die Echtzeit-Get-API, die keine Übertragungen erfordert. Dieser Fehler ist in Solr 4.10.2 behoben.
SOLR-6583 – Wiederaufnahme der Verbindung mit ZooKeeper führt zur Wiederholung des Protokolls
Dies ist ein kleineres Problem, bei dem die Wiederaufnahme der Verbindung mit ZooKeeper zu einer erneuten Wiedergabe des Protokolls führt. Alle diese wiedergegebenen Aktualisierungen werden verworfen, da sie alt sind und bereits angewendet wurden. Dies kann jedoch dazu führen, dass ein bestimmter Knoten für kurze Zeit nicht aktiv wird. In den meisten Fällen ist dies kein Problem, da der Code für die Protokollwiedergabe die Namen der Transaktionsprotokolldateien beim Start im Speicher behält (er wurde schließlich geschrieben, um sich während des Starts von den Protokollen zu erholen) und sie nie aktualisiert. Die meisten Benutzer erhalten also nur eine Warnung in ihren Protokollen über fehlende Transaktionsdateien.
Abfrage-Threads hängen während einer Netzwerkpartition
Ich habe festgestellt, dass einige Threads mit Aktualisierungsanfragen so lange hängen bleiben, wie eine Netzwerkpartition andauert. Dies passiert bei Anfragen, die auf eine Netzwerkpartition treffen, nachdem sie das Dokument in das lokale Transaktionsprotokoll und den Lucene-Index geschrieben haben und dann bei der Aktualisierung eines oder mehrerer Replikate scheitern. Zu diesem Zeitpunkt versuchen einige dieser Aktualisierungs-Threads, ZooKeeper zu aktualisieren, um die fehlgeschlagenen Replikate in den ‚Down‘-Status zu versetzen, in dem sie stecken bleiben. Sobald die Partition geheilt ist, geben sie eine erfolgreiche Antwort an den Benutzer zurück. In der Zwischenzeit ist jedoch eine andere Replik zum Leader geworden (weil der vorherige Leader von ZK abgetrennt wurde) und der alte Leader erholt sich vom neuen Leader und wirft die nicht bestätigten Updates weg.
Das bedeutet, dass Clients, die bereit sind, viel länger als die ZooKeeper-Sitzungszeitüberschreitungen zu warten, *möglicherweise* eine erfolgreiche Antwort für ein Dokument erhalten, das verloren gegangen ist. Die meisten Clients haben vernünftige Lese-Timeouts, die weit unter dem Standard-Timeout für ZooKeeper-Sitzungen liegen, so dass dieses Problem nie auftreten wird. Ein ähnliches Problem wurde von der Community bemerkt und als SOLR-6511 im öffentlichen Jira veröffentlicht. Ich versuche immer noch, die Ursache herauszufinden und zu beheben.
Cluster-Status-API schlägt mit seltsamen Ausnahmen bei Netzwerkpartitionen fehl
Die Cluster-Status-API in Solr holt den neuesten Cluster-Status von einem Knoten ab, den Solr als „Overseer“ bezeichnet. Dabei handelt es sich um einen Knoten, der aus allen Knoten des Clusters ausgewählt wird und für die Veröffentlichung des neuen Cluster-Status in ZooKeeper verantwortlich ist. Diese API verwendet ZooKeeper als Warteschlange, um Anfragen zu übermitteln und Antworten zu lesen. Dadurch wird sichergestellt, dass der neueste Cluster-Status an den Client zurückgegeben wird. Das bedeutet jedoch auch, dass diese Status-API bei der Kommunikation mit einem ZooKeeper-Knoten, der sein Quorum verloren hat, mit den Ausnahmen Verbindungsverlust oder Sitzungszeitüberschreitung von ZooKeeper und einem riesigen Stack-Trace fehlschlägt. Das ist etwas, das wir verbessern können.
SOLR-6610 – Langsamer Start des Clusters
Das ist eines dieser Dinge, die Entwickler oft nicht so sehr bemerken wie Benutzer. Als ich den Solr-Cluster zum ersten Mal online stellte, fiel mir auf, dass die Knoten 30 Sekunden lang warteten, bevor sie dem Cluster beitraten. Dann habe ich den Cluster neu gestartet und er war sofort wieder da. Ich fügte einen Ruhezustand in meine Initskripte ein, um das Problem zu umgehen, machte aber eine mentale Notiz, um es später zu untersuchen, weil ich mich darauf konzentrierte, zuerst die Jepsen-Tests laufen zu lassen. Ich hatte es fast vergessen, bis jemand in der Solr-Community SOLR-6610 öffnete, was mich dazu veranlasste, es noch einmal zu untersuchen. Es stellte sich heraus, dass es sich um einen dummen Fehler handelte, der Solr dazu veranlasste, darauf zu warten, dass sein eigener Status als „down“ veröffentlicht wurde, während er davon ausging, dass ein Overseer-Knoten existierte, um die Veröffentlichung zu verarbeiten. Dieser Fehler tritt nur bei der Initialisierung eines neuen Clusters auf und wird per Definition von Benutzern gesehen, die einen Single-Node-Cluster zum Spielen mit Solr aufsetzen. Dieses Problem ist nun behoben und wird in der nächsten Bugfix-Version – Solr 4.10.3 – veröffentlicht.
Wo ist der Code?
Alle unsere Änderungen an Jepsen sind auf unserem Jepsen-Fork bei Github im Zweig solr-jepsen verfügbar.
Fazit
Diese Tests zeigen, dass bei diesen speziellen Arten von Partitionen kein Datenverlust auftrat, aber wie Edgar W. Dijkstra sagte: „Testen zeigt das Vorhandensein, nicht das Fehlen von Fehlern“. Wenn wir die Art von Fehlern, nach denen wir suchen, nicht gefunden haben, bedeutet das nur, dass wir uns mehr Mühe geben und an mehr Stellen suchen müssen.
Zwar erfordert Solr anfangs etwas mehr Arbeit bei der Einrichtung von Zookeeper, aber wie Sie anhand dieser Tests sehen können, können wir damit eine wesentlich stabilere Umgebung schaffen, wenn es darauf ankommt: in der Produktion. Die Solr-Gemeinschaft hat sich bewusst dafür entschieden, ein kleines bisschen Leichtigkeit beim Einstieg gegen eine ganze Menge „fertig werden“ einzutauschen. Dies sollte zu deutlich weniger Datenverlusten und einem zuverlässigeren Betrieb im Allgemeinen führen.
Was kommt als Nächstes?
Das ist der lustige Teil!
Unser nächster Schritt ist die Integration mit der kontinuierlichen Integrationsumgebung für Solr und Fusion, unserer kommerziellen, batteriebetriebenen Plattform für Suche und Analyse. Wir arbeiten an den Feinheiten unserer Jenkins-Einrichtung, um Jepsen gegen jede Subversion-Commit laufen zu lassen und uns über etwaige Fehler zu informieren. Wir planen, unsere Jenkins-Einrichtung zu veröffentlichen, damit die breitere Lucene/Solr-Gemeinschaft die Auswirkungen ihrer Änderungen auf die Stabilität von Solr erkennen kann.
Jepsen ist viel leistungsfähiger, als wir es im Moment nutzen. Es kann die Uhren im gesamten Cluster verdrehen, auf der Festplatte etwas durcheinander bringen, Netzwerke verlangsamen und alle möglichen Verwüstungen anrichten. Wir möchten weitere Solr-Funktionen wie Löschungen, Shard-Splits, Migrationen usw. unter verschiedenen Nemesis-Strategien sowie verschiedenen Cluster-Topologien testen. Außerdem haben wir die Linearität von compare-and-set Operationen nicht bewiesen, sondern nur, dass es im Endzustand keine Datenverluste gibt. Der Knossos Checker benötigt eine Menge Ressourcen, um zu laufen. Wir müssen unseren Test also auf einigen großen AWS-Maschinen durchführen, um mehr zu erfahren.
SolrCloud hat einen langen Weg hinter sich, und mit der Unterstützung von Lucidworks und den gebündelten Ressourcen der riesigen Community werden wir Solr und SolrCloud weiter zum stabilsten, skalierbarsten und zuverlässigsten Open Source-Suchsystem der Welt machen.
Ich möchte meinen Kollegen Tim Potter und Matt Hoffman für ihre Hilfe danken, Jepsen mit Solr zum Laufen zu bringen. Besondere Erwähnung verdient Matt Hoffman, unser ansässiger Clojure-Guru, der einem Clojure-Neuling wie mir auf die Sprünge geholfen und meine unaufhörlichen Fragen zur Clojure-Syntax und den APIs beantwortet hat. Ein großes Dankeschön an Matt Mitchell, ebenfalls mein Kollege, der flux geschrieben hat – den Clojure Solr Client, der für diese Tests verwendet wurde. Ich möchte Anshum Gupta, Chris Hostetter, Grant Ingersoll, Jim Walker, Noble Paul, Tim Potter und Steve Rowe für ihr Feedback zu diesem Beitrag danken. Es ist großartig, mit so tollen Leuten an Dingen arbeiten zu können, die ich liebe.
Schließlich möchte ich @aphyr selbst dafür danken, dass er Jepsen erstellt und über Netzwerkpartitionen und verteilte Systeme geschrieben hat. Ich habe seine Beiträge sehr genossen und viel von ihnen gelernt. Ich denke, Solr hat sich durch sie verbessert und wird sich weiter verbessern.