Zum Inhalt springen

Blog ·

Um drei Uhr nachts aktualisiert, mit Absicht

Die automatische Agent-Aktualisierung läuft um 03:00. Interessant war, welche Lage einen Platz verbraucht und eine Nacht mit zweimal drei Uhr.

Von Provibr

Der Agent, der im eigenen Netz eines Kunden läuft, bekommt alle paar Tage einen neuen Build. Bis zu dieser Runde war das Aktualisieren eine Schaltfläche, die jemand drücken musste, in der Praxis geschah es also dann, wenn jemand zufällig hinsah. Der Wunsch war ein Satz: jeder kann bei seinem Agenten einen Aktualisierungsplan einstellen, mehrere Zeitpunkte pro Tag, standardmäßig aktiv um drei Uhr nachts.

Der Satz ist einfach. Zur Runde wurde das, weil ein geplanter Neustart von Infrastruktur viele Möglichkeiten hat, still falsch zu sein, und fast alle von außen in Ordnung aussehen.

Der Standard ist ein Spaltenvorgabewert, und das ist der ganze Wunsch

„Standardmäßig für alle aktiv“ hat zwei Lesarten. Im Anwendungscode gilt es für Agenten, die von jetzt an angelegt werden, und das schließt genau die Kunden aus, die schon einen haben. Als Spaltenvorgabewert in der Migration gilt es für jede bestehende Zeile in einer einzigen Anweisung.

Es ist also ein Spaltenvorgabewert: ein Wahrheitswert, der auf wahr beginnt, und ein JSON-Feld, das mit einem einzigen Eintrag 03:00 beginnt. Gemessen, nachdem die Migration auf der Entwicklungsdatenbank lief: jeder bestehende Agent aktiv, jeder um drei Uhr, und ein frisch angelegter Agent, dem keiner der beiden Werte mitgegeben wurde, bekam denselben Stand. Diese letzte Prüfung ist die, die beweist, dass der Vorgabewert die Arbeit tut und nicht ein Saatskript.

Die Zeitpunkte sind Wanduhrzeit in der Zeitzone der Plattform, nicht UTC. Es gibt keine Zeitzone pro Organisation und das Panel stellt alles in dieser einen Zone dar, ein in UTC gespeicherter Plan verschöbe sich im Sommer also um eine Stunde, und ein Kunde, der drei Uhr eingab, würde um fünf unterbrochen.

Zwei Schichten auf einem regulären Ausdruck

Ein Zeitpunkt ist HH:MM auf einem Fünf-Minuten-Raster, und dieses Muster existiert zweimal: einmal im Schemaprüfer, der ein Formularfeld mit einer lesbaren Meldung abweist, und einmal als Datenbankbedingung, die alles abweist, was nie durch dieses Formular kam.

Eine Prüfbedingung darf keine Unterabfrage enthalten, „jedes Element dieses Feldes ist ein gültiger Zeitpunkt“ kann also keine Existenzprüfung über das entpackte Feld sein. Möglich ist ein JSON-Pfad-Prädikat, das genau die Elemente auswählt, die falsch sind, verpackt in eine Prüfung, dass diese Auswahl leer ist.

Die erste Fassung dieses Prädikats war auf subtile Weise nutzlos, und sie sah vollständig aus:

text
$[*] ? ( !(@ like_regex "^([01][0-9]|2[0-3]):(00|05|10|15|20|25|30|35|40|45|50|55)$") )

Der Abgleich eines regulären Ausdrucks gegen etwas, das keine Zeichenkette ist, ist im JSON-Pfad nicht falsch. Er ist Unknown. Und die Verneinung von Unknown ist ebenfalls Unknown, und ein Filter behält nur Elemente, für die das Prädikat wirklich wahr ist. Die Auswahl der falschen Elemente kam für ein Feld mit Zahlen also leer zurück, und die Bedingung nahm es an. Gemessen gegen PostgreSQL 17.10 gingen [3], [null] und [{"a":1}] alle drei hindurch.

Die Reparatur ist eine Klausel davor: Elemente, deren Typ keine Zeichenkette ist, sind per Definition falsch. Alle drei werden jetzt abgewiesen. Das ist genau die Fehlerklasse, die uns immer wieder Runden kostet: eine Absicherung, die nichts abfängt und vollständig aussieht, und kein Test schlägt fehl, weil es nichts gab, woran er hätte scheitern können.

Fünf Schritte, und die Reihenfolge ist die Runde

Jede Minute fragt ein Arbeiter, wer einen Plan hat, gibt die Zeilen an einen reinen Planer, der entscheidet, welcher Platz fällig ist, und geht dann fünf Schritte pro Agent. Der Anspruch ist Schritt vier und nicht Schritt eins, und jeder Schritt davor steht aus eigenem Grund dort.

SchrittWenn die Antwort nein lautet
Gibt es einen neueren Build?Der Platz wird trotzdem beansprucht, als nichts zu tun markiert
Ist der Agent verbunden?Es wird nichts beansprucht, und die nächste Minute versucht es erneut
Läuft schon eine Aktualisierung?Es wird nichts beansprucht; die erste löst den Platz noch ein
Den Platz beanspruchenEin eindeutiger Index entscheidet; der Verlierer sendet nichts
Das Kommando sendenDieselbe Funktion wie die Schaltfläche im Panel

Die ersten beiden Zeilen sind bewusste Gegenstücke, und diese Asymmetrie ist die interessanteste Entscheidung der Runde.

Nichts zu tun verbraucht den Platz, denn die Antwort kann sich innerhalb eines Platzes nicht mehr ändern: das Freigabemanifest auf der Platte ist für das ganze Fenster dasselbe, jede Minute neu zu urteilen kostet also Abfragen und bringt nichts. Und es gibt dem Kunden etwas zu sehen. Eine Agentenseite, auf der „03:00, bereits aktuell“ steht, ist von einem Plan, der nicht läuft, zu unterscheiden. Eine leere Liste ist das nicht.

Offline verbraucht nichts, denn ein Platz, der beansprucht würde, während der Host gerade neu startet, ist für immer verbraucht, und dann verpasst dieser Kunde still eine Nacht. Die nächste Minute versucht es erneut, bis das Nachholfenster geschlossen ist; danach nimmt der Agent einfach seinen nächsten Platz.

Fünfzehn Minuten, und nicht sechs Stunden

Sicherungen haben in dieser Plattform ein Nachholfenster von sechs Stunden. Dieses hier hat fünfzehn Minuten, und der Unterschied ist das, was schiefgeht, wenn man großzügig ist.

Eine Sicherung, die um neun Uhr morgens doch noch gemacht wird, ist eine Sicherung. Ein Agent, der um neun Uhr morgens doch noch neu startet, ist eine Unterbrechung mitten am Arbeitstag, und drei Uhr nachts ist genau das, was der Kunde gewählt hat, um das zu vermeiden. Fünfzehn Minuten decken das ab, wofür dieses Fenster wirklich da ist, eine gerade neu gestartete Gateway oder ein paar übersprungene Runden, und sonst nichts.

Der Planer schaut zwei Kalendertage zurück und nicht einen, und das ist kein Vertun. Ein Zeitpunkt von 23:55 fällt mit einem Nachholfenster kurz nach Mitternacht in den Kalendertag von gestern; mit nur heute im Blick käme dieser Platz konstruktionsbedingt nie an die Reihe.

Zwei Bremsen, aus zwei verschiedenen Gründen

Fünf Agenten pro Organisation und Runde, und fünfundzwanzig insgesamt.

Die Bremse pro Organisation betrifft die Bandbreite des Kunden und nicht unsere. Die Binärdatei ist rund zweiundzwanzig Megabyte groß, ein Hoster mit vierzig Agenten hat sie üblicherweise alle vierzig auf 03:00 stehen, weil das der Standard ist, und vierzig gleichzeitige Abrufe verlassen dasselbe ausgehende Netz. Fünf pro Minute verteilen eine Flotte von vierzig über acht Minuten, bequem innerhalb des Fünfzehn-Minuten-Fensters.

Die globale Bremse existiert, weil die erste pro Organisation wirkt: bei hundert Organisationen wären das fünfhundert Aktualisierungen in einer Minute. Der Rest kommt in der nächsten Minute, immer noch innerhalb des Fensters.

Auf all dem liegt keine Ausrollflagge, was für uns ungewöhnlich ist. Der Schalter steht bereits beim Kunden, auf der Spalte; eine zweite, plattformweite Flagge gäbe ein Versprechen, das die Spalte schon gibt. Was es gibt, ist eine Notbremse für uns: das Abfrageintervall auf null zu setzen stoppt den Zeitgeber, ohne dass sich eine einzige Kundeneinstellung ändert.

Die Nacht mit zwei Dreien

Am letzten Sonntag im Oktober existiert die Wanduhrzeit 03:00 zweimal, und am letzten Sonntag im März existiert 02:30 überhaupt nicht. Beide beantwortet eine gemeinsame Hilfsfunktion, die nicht in dieses Modul kopiert wurde, gerade weil ein an zwei Stellen gepflegter Sommerzeitalgorithmus an einem Tag im Jahr auseinanderläuft.

Diese Hilfsfunktion beantwortet eine mehrdeutige Wanduhrzeit mit dem ersten der beiden Zeitpunkte und eine nicht existierende mit dem nächsten, der existiert. Die Folgen sind genau die gewünschten. In der langen Nacht läuft 03:00 einmal und nicht zweimal; gemessen: ein Platz. In der kurzen Nacht wird 02:30 einfach 03:30 Ortszeit, statt übersprungen zu werden, denn nicht überspringen ist die sichere Seite dieser Münze. Beides ist in Tests festgehalten.

Tage werden als Kalendertage durchlaufen und nicht durch das Addieren einer festen Zahl von Millisekunden: vierundzwanzig Stunden über einen Wechsel hinweg zu addieren überspringt einen Tag oder macht einen doppelt.

Wem es gesagt wird, und wie oft

Der Abgleich, der Ergebnisse meldet, liegt im Arbeiter und nicht auf dem Versandpfad, und nur der Plan meldet. Bei einem Klick von Hand sah bereits jemand zu.

Meldungen werden pro Organisation gruppiert, denn vierzig Agenten am Freigabetag sollen nicht vierzig Zeilen in einem Postfach werden. Ein Fehlschlag wird höchstens einmal pro Agent und Kalendertag gemeldet. Und der Zeitstempel, der festhält, dass eine Meldung erwogen wurde, wird auch geschrieben, wenn die Entscheidung war zu schweigen, damit Stille von einem Arbeiter, der nie dort ankam, zu unterscheiden ist.

Eine geplante Aktualisierung erscheint bewusst nicht als Wurzeloperation in der Aufgabenlade: um drei Uhr nachts wartet niemand, und eine Lade voller nächtlicher Zeilen ist für das, wofür sie da ist, unbrauchbar. Die Spur sind eine Laufzeile, ein Ereignis und eine Meldung. Dieses Ereignis hat eine eigene Art, denn der Verlauf darf nicht behaupten, der Kunde habe um drei Uhr selbst etwas gestartet.

Was wir festhalten würden

  1. „Standardmäßig für alle“ ist ein Spaltenvorgabewert. Im Anwendungscode heißt es „für alle ab jetzt“, und das schließt genau die Leute aus, an die Sie gedacht haben.
  2. Unknown ist nicht falsch. Im JSON-Pfad ist ein Prädikat auf einem Wert des falschen Typs weder wahr noch falsch, und Verneinen hilft nicht. Prüfen Sie zuerst den Typ.
  3. Entscheiden Sie je Lage, ob sie den Platz verbraucht. Nichts zu tun und nicht erreichbar sind gegenteilige Antworten, und beide sind unsichtbar, wenn man sie vertauscht.
  4. Ein Nachholfenster ist ein Versprechen über Unterbrechung, nicht über Vollständigkeit. Übernehmen Sie die Zahl aus einem System mit anderem Fehlverhalten, haben Sie die falsche.
  5. Bremsen Sie dort, wo es wirklich spürbar ist. Vierzig Agenten in einem Kundennetz teilen sich eine Anbindung, und das ist die Grenze, nicht unsere Abrufroute.
  6. Lösen Sie die Sommerzeit einmal. Zwei Kopien derselben Überlegung sind sich einig bis zum letzten Sonntag im Oktober.

Zur Klarheit darüber, was das ist: der Plan ist aktiv und standardmäßig eingeschaltet. Mehrere Dinge sind unbewiesen. Der Arbeiter wurde nicht in einer wirklich laufenden Gateway erprobt, kein echter Agent hat bisher eine geplante Aktualisierung ausgeführt, die Fehlermeldung mit ihrer Tagesentdopplung wurde nicht durchgängig beobachtet, und der Bildschirm wurde in zwei von neun Sprachen geprüft.

Aus der Plattformarbeit von Provibr. Warum ein Agent eine Aktualisierung ablehnt, die er nicht prüfen kann, steht in wie eine signierte Agent-Aktualisierung sich selbst ablehnt, und warum der Agent ausgehend verbindet, statt zu lauschen, steht in ein ausgehender Agent statt Portweiterleitung.

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.