Zum Inhalt springen

Blog ·

Projizieren, niemals ersetzen

Eine Fortschrittsansicht über Migrationen, Backups, Wiederherstellungen und Neuinstallationen, ohne zweite Buchführung. Zwei Fehler verliefen völlig still.

Von Provibr

Ein Hostingpanel sammelt langlaufende Arbeit an. Eine Migration zwischen zwei Verwaltungspanels, ein Backup, eine Wiederherstellung in eine neue Maschine, ein Container, der ersetzt wird. Jedes bekam eine eigene Tabelle, einen eigenen Wortschatz für Zustände und einen eigenen Bildschirm, und das trägt, bis jemand die naheliegende Frage stellt: was läuft gerade auf meinem Konto, und hängt davon etwas fest.

Die verlockende Antwort ist ein Auftragssystem. Eine Tabelle, eine Zustandsliste, alles andere schreibt hinein. Die verlockende Antwort ist auch der Weg zu zwei Quellen der Wahrheit über dieselbe Tatsache und zu einem Supportgespräch ein Jahr später, in dem der Auftrag fehlgeschlagen sagt und die Backuptabelle sagt, das Archiv liege auf dem Speicher.

Die Vorgangsansicht ist deshalb eine Projektion. Sie liest die bestehenden Zeilen und schreibt eine Zusammenfassung darüber. Es geht nie etwas in die andere Richtung: keine einzige Funktion im Spiegelmodul berührt die Backupläufe, die Wiederherstellungsläufe, die Migrationsläufe oder die Neuinstallationen. Diese Tabellen bleiben die Wahrheit, und die Zusammenfassung darf veraltet, falsch oder abwesend sein, ohne dass irgendetwas dahinter zerbricht.

Ein Spiegel je Tabelle, und ein Test, der das Schema liest

Vier Tabellen liegen auf dieser Naht, und jede hat genau eine Funktion, die sie projiziert.

QuelltabelleZustandsspalteVorgangsart
backup_runsstatusbackup.run
backup_restore_runsstatusbackup.restore
migration_runsstatusmigration.run
container_reinstall_intentsstatecontainer.reinstall

Die letzte Zeile ist der Grund, warum die Spaltennamen als Text aufgeschrieben und nicht angenommen werden. Diese Tabelle nennt die Spalte state, und ein Spiegel, der status liest, bekommt nichts zurück und fällt ohne jede Beschwerde in den Fehlschlagzweig.

Diese Tabelle ist keine Dokumentation. Ein Test leitet aus dem Datenbankschema ab, welche dieser Tabellen eine Zustandsspalte haben, verlangt je Tabelle einen Eintrag hier, verlangt, dass die genannte Funktion wirklich existiert, und verlangt, und das ist der Teil mit der eigentlichen Wirkung, dass jede Datei, die eine solche Zustandsspalte schreibt, auch ihren Spiegel nennt. Eine fünfte Tabelle, die ohne Projektion auf die Naht kommt, ist damit ein roter Test statt einer Schublade, die still stehen bleibt.

So streng muss es sein, weil die Schreiber verstreut liegen: ein Backuplauf wechselt seinen Zustand an sechs Stellen einer einzigen Datei, drei davon auf dem Weg, der die Antworten des nachts laufenden Abgleichs verarbeitet. Jeden Schreiber die Projektion selbst aufrufen zu lassen heißt, dass der sechste es vergisst, und Vergessen ist unsichtbar. Die Zeile stimmt. Nur der Bildschirm aktualisiert sich nie.

Die Senke, die zweimal existierte

Das Spiegeln wohnt in dem Paket, dem die Datenbankschreibungen gehören, denn sowohl eine Serveraktion in der Webanwendung als auch eine Gateway-Komponente rufen dieselben Funktionen auf. Dieses Paket kann kein Live-Ereignis veröffentlichen: es hängt an der Datenbank und an gemeinsamem Code und sonst an nichts, und der Nachrichtenbus ist auf jeder Seite etwas anderes.

Es bietet also eine Senke an. Jede Anwendung registriert eine, und das Paket weiß nicht, wer sie gefüllt hat. Tut es niemand, läuft die Datenbankschreibung trotzdem und es geht kein Live-Ereignis hinaus, und die Schublade aktualisiert sich beim nächsten Seitenaufruf. Das ist ehrliche Verschlechterung, und die Funktion wirft absichtlich nie, denn eine fehlende Registrierung darf kein Backup umreißen.

Die Senke war eine Modulvariable, und das war auf eine Weise falsch, die überhaupt keinen Fehler erzeugte. Next führt seine Instrumentierungsdatei als eigenes Bündel aus, getrennt von den Routenprozessen. Die Variable existierte dadurch zweimal: die Registrierung landete in der einen Ausfertigung und die sendende Funktion las die andere. Die Zeilen stimmten, die Zustände liefen weiter, die Schublade stand still, und in keinem Protokoll stand eine einzige Zeile.

Gefunden wurde das nicht durch Nachdenken. Gefunden wurde es, indem ein Zuhörer auf den Nachrichtenbus gesetzt und gezählt wurde: ein Ereignis kam an, wo drei hätten stehen müssen. Die Reparatur ist ein Platz auf globalThis hinter einem Symbol.for, der eine Bündelgrenze innerhalb eines Prozesses überlebt, dieselbe Form und derselbe Grund wie bei der gemeinsamen Datenbankverbindung und der gemeinsamen Redis-Verbindung.

Zwei Lehren, die mehr wert sind als der Fehler. Ein Ausfall ohne Fehlerzeile verlangt ein Messgerät außerhalb des Codes, den man verdächtigt. Und „dieses Modul hat eine Ausfertigung“ ist eine Annahme über einen Bündler, keine Eigenschaft der Sprache.

Dieselbe Runde fand einen zweiten stillen Fall. Das Abschließen und das Blockieren eines Vorgangs ließen beide die Stufenspalte unberührt, ein perfekt gelungenes Backup blieb also für immer auf „archiviert wird“ stehen. Es schlug nichts fehl; der letzte Schritt kam nur nie an.

Abbrechen ist eine Bitte, und eine einzige Änderung ist der Beweis

Beim Abbrechen ist eine Projektion am ehesten geneigt zu lügen. Die Schublade kennt den Vorgang, der Vorgang hat eine Abbruchschaltfläche, und die billigste Umsetzung setzt ein Merkmal und schließt das Fenster.

Jede Art erklärt deshalb, ob Abbrechen überhaupt Bedeutung hat. Ein laufendes vzdump können wir nicht abbrechen: der Befehl liegt beim Agenten, die Aufgabe beim Hypervisor, und niemand weiß, was ein halbes Archiv auf dem Speicher des Kunden darstellt. Eine Wiederherstellung ist derselbe Fall, mit einem Grund obendrauf: die Wiederherstellungszeile hält eine Reservierung auf die Zielkennung, solange sie offen ist, und sie früh zu schließen gibt diese Kennung frei, während vielleicht gerade ein Gast darauf landet. Beide sind als nicht unterstützt erklärt, und es gibt keinen Weg daran vorbei.

Wo Abbrechen möglich ist, entscheidet weiterhin die Zeile. Eine Migration lässt sich abbrechen, solange sie geplant ist, und nicht mehr, sobald ein Hilfsprogramm Server auf dem Wirtssystem eines anderen schreibt. Eine Container-Neuinstallation lässt sich bis zur Vorschau abbrechen und nicht mehr, sobald der Agent zu ersetzen begonnen hat.

Wenn es gelingt, ist der Beweis, dass nichts geschehen ist, eine einzige bedingte Änderung: setze den wartenden Befehl auf abgebrochen, dort wo sein Zustand noch wartend ist. Wurde er bereits gesendet, berührt diese Anweisung null Zeilen und die Bitte bleibt ehrlich auf „erbeten“ stehen. Nachsehen und dann schreiben wären zwei Anweisungen und damit zwei Gelegenheiten, dazwischenzukommen.

Und dann zählt der Satz. Das Ereignis sagt, der Befehl sei aus der Warteschlange genommen worden, bevor der Agent ihn erhielt, und nicht, er sei abgebrochen worden. Es wurde nichts unterbrochen, denn es hatte nichts begonnen. Diese Zeile geht in das allgemeine Ereignisprotokoll und nicht in den Vorgangsfortschritt, denn Fortschritt wird nach neunzig Tagen aufgeräumt, und „ein Mensch griff in die Infrastruktur ein“ ist kein Fortschritt.

Vier Blockaden, und keine davon ist ein Fehlschlag

Vier Lagen setzen einen Vorgang auf Aufmerksamkeit nötig statt auf fehlgeschlagen.

Ein vzdump meldete OK und das Archiv steht nicht im Verzeichnis. Ein Backupversuch lief in eine Zeitgrenze und wir wissen nicht, ob das Archiv existiert. Eine Wiederherstellung lässt sich nicht zurücklesen oder der entstandene Gast konnte nicht verbucht werden. Eine Container-Neuinstallation strandete, nachdem der alte Container schon weg war.

Eine davon fehlgeschlagen zu nennen wäre eine Lüge mit einer Folge. Fehlgeschlagen lädt den Kunden ein, es noch einmal zu tun, und ein Backup erneut zu fahren sind wieder Dutzende Gigabyte auf dem Speicher von jemandem für ein Archiv, das vielleicht schon da ist. Eine Neuinstallation erneut zu fahren ist ein zweiter Container. Die Abgleichschleife schaut weiter, bis das Verzeichnis Auskunft gibt, und der Vorgang steht währenddessen sichtbar fest, was genau die richtige Menge Beunruhigung ist.

Die Blockade trägt auch, wem sie gehört. Drei davon sind unsere; ein Befehl, der wirklich auf der Seite des Kunden scheiterte, sagt das.

Ein Trockenlauf endet beim Planen, und das ist keine Lücke

Eine Migration im Trockenlauf hat fünf Schritte; eine echte hat acht, wobei die drei zusätzlichen das Schreiben und Nachzählen sind. Die Stufenliste kennt alle sechs Stufen, ein gelungener Trockenlauf zeigt seine letzten beiden also als nicht erledigt.

Das sieht aus wie ein Fehler in einem Fortschrittsbalken und ist die Wahrheit: ein Trockenlauf schreibt nicht und zählt nicht nach. Die Alternativen waren eine zweite Art, was neun Katalogeinträge in neun Sprachen für dieselbe Arbeit verdoppelt, oder eine Stufenliste, die je Zeile abweicht, wodurch Schublade und Detailseite uneins werden, wie viele Schritte es gibt. Die Wahrheit zu zeigen war billiger als beides.

Was wir aufschreiben würden

  1. Eine Projektion projiziert. Kann Ihre Zusammenfassungsschicht in die Quelle zurückschreiben, ist sie eine zweite Buchführung, und nun haben Sie zwei.
  2. Legen Sie die Naht in eine Tabelle, die ein Test lesen kann, aus dem Schema abgeleitet, und lassen Sie ihn prüfen, dass jeder Schreiber einer Zustandsspalte seinen Spiegel nennt. Eine vergessene Aufrufstelle meldet nichts.
  3. Nehmen Sie nicht an, dass ein Modul eine Ausfertigung hat. Ein Rahmenwerk, das einen Teil Ihres Codes als eigenes Bündel ausführt, gibt Ihnen zwei, und das Symptom ist Stille.
  4. Messen Sie außerhalb des Verdächtigen. Ein Zuhörer am Bus fand in Minuten, was Lesen gar nicht fand.
  5. Sagen Sie, was wirklich geschah. „Aus der Warteschlange genommen, bevor der Agent ihn erhielt“ ist länger als „abgebrochen“ und die einzige Fassung, die stimmt.
  6. Reservieren Sie fehlgeschlagen für Fehlschläge. Unbekannte Ausgänge brauchen einen eigenen Zustand, sonst wiederholen Menschen teure Handlungen, die vielleicht längst gelangen.

Zur Klarheit darüber, was das ist: die hier beschriebenen Arten liegen hinter Ausrollschaltern, die standardmäßig aus sind, Migrationen und Kundenbackups sind heute also nicht käuflich. Mehreres ist unbewiesen. Das Live-Fortschrittsereignis wurde nur für Migrationen gemessen. Die Gatewayseite der Senkenregistrierung wurde nicht mit einem wirklich laufenden Gateway erprobt. Die Agentenkette wurde mit SQL nachgestellt. Und eine Container-Neuinstallation vom Vorgangsbildschirm aus abzubrechen wurde nie in einem Browser durchgespielt.

Aus der Plattformarbeit an Provibr. Warum ein Fortschrittsbalken sich weigert, eine Endzeit zu schätzen, steht in ein Fortschrittsbalken, der nicht rät, und warum eine Migration, die still übersprang, schlimmer ist als eine gescheiterte, steht in die Migration, die nie lief.

Fangen Sie mit einem Host und einem Agenten an

Konto erstellen, den Agenten mit einem einzigen Befehl installieren und Ihren ersten Proxmox-Host verbinden. Die Testphase läuft 14 Tage und fragt nicht nach Zahlungsdaten.