Blog ·
Eine Pause, die die Bestellung offen hielt
Ein Warten in einem Flow hielt die Bestellanfrage über ihre ganze Dauer offen. Die Lösung war eine Schwelle statt eines Kästchens, und die Zahl wurde gemessen.
Von Provibr
Ein Flow kann ein Warten enthalten. Sie stellen eine Maschine bereit, Sie geben dem Agenten einen Moment, dann senden Sie den nächsten Befehl. Bis zu dieser Woche war dieses Warten ein Schlafbefehl innerhalb der Anfrage, die den Flow gestartet hatte, was bedeutete, dass ein Kunde beim Aufgeben einer Bestellung für die Dauer der Pause auf einen Ladekringel schaute.
Die alte Obergrenze waren fünf Minuten. Jemand konnte also einen vollkommen vernünftig aussehenden Flow bauen, dessen Wirkung ein fünfminütiges Bestellformular war.
Jetzt, da ein Lauf anhalten und von einem Worker fortgesetzt werden kann, muss die Pause nicht mehr innerhalb der Anfrage stattfinden. Die Obergrenze wanderte auf dreißig Tage. Die interessante Frage ist allerdings nicht die Obergrenze. Es ist die Frage, wo die Linie zwischen einer Pause, die die Anfrage aufnehmen kann, und einer Pause, die den Lauf aussetzen sollte, verläuft.
Die Schwelle ist gemessen und nicht gewählt
Dreißig Sekunden.
Die Frage, die wir gestellt haben, war nicht „wie lang darf eine Pause sein“, worauf es keine Antwort gibt, sondern „wie viel Pause verträgt die Anfrage, in der dieser Flow läuft, ohnehin schon“, worauf es eine gibt. Diese Anfrage ist die Bestellung.
Gemessen an dem, was darin ohnehin geschieht: ein Aktionsknoten wartet standardmäßig sechzig Sekunden auf einen Befehl und lässt sich bis auf dreihundert einstellen, und eine Benachrichtigung mit E-Mail kostet etwa eine halbe Sekunde je Empfänger. Eine Pause, die darauf eine weitere halbe Minute legt, ist vertretbar. Eine Pause, die eine volle Minute darauflegt, ist es nicht.
Darin steckt eine Falle, in die wir bewusst hineingegangen sind, statt um sie herum. Die bequeme Wahl wäre gewesen, die Schwelle auf die alte Obergrenze von dreihundert Sekunden zu setzen, denn dann verhält sich jeder vorhandene Graph genau wie vorher und niemand ist überrascht. Das ist auch falsch: es bewahrt genau die Klasse von Problemen, die die Änderung überhaupt veranlasst hat. Graphen mit einer Pause zwischen einunddreißig und dreihundert Sekunden verhalten sich jetzt tatsächlich anders. Ihre Bestellung ist früher fertig, und der Rest des Flows folgt später. Das ist die Verbesserung und nicht ihr Preis.
Die Obergrenze von dreißig Tagen ist eine Produktregel und keine technische Grenze. Die Wartemechanik würde einen Lauf bereitwillig ein Jahr lang halten. Was kein Jahr durchhält, ist eine Organisation, in der sich niemand mehr erinnert, warum ein Lauf offen ist.
Warum es eine Schwelle ist und kein Kästchen
Die naheliegende Alternative ist ein Schalter „dauerhaft warten“ am Knoten. Wir haben keinen gebaut, und der Grund ist eine Frage, die Sie nicht beantworten können.
Wenn es ein Kästchen gäbe, wann würden Sie es ausschalten wollen? Die ehrliche Antwort ist: nie. Dauerhaftes Warten ist ein Umsetzungsdetail: wer einen Flow schreibt, drückt aus „hier so lange anhalten“ und hat keine Meinung dazu, ob das eine Datenbankzeile kostet. Eine Einstellung, deren richtiger Wert immer derselbe ist, ist keine Einstellung, sondern etwas, das Sie zu entscheiden vergessen haben.
Also tippt der Schreibende eine Anzahl Sekunden, und die Schwelle entscheidet. Unterhalb der Linie schläft der Durchlauf einfach, innerhalb der Anfrage, und kostet nichts. Oberhalb schreibt der Lauf seine Fortsetzung mit einer Weckzeit in die Warteschlange, und der Aufrufer kehrt sofort zurück.
Der Schritt, der schließlich im Laufprotokoll erscheint, trägt eine Dauerhaftigkeitsmarkierung und die tatsächlich gemessene verstrichene Zeit statt der angeforderten, denn das sind nicht dieselben Zahlen, und der Unterschied ist der einzige Weg zu sehen, dass die Warteschlange hinterherhinkt.
Flows, die Flows aufrufen, und zwei Grenzen, jede zweimal geprüft
Die zweite Hälfte dieser Arbeit ist ein Subflow-Knoten: ein Flow, der einen anderen Flow ausführt und auf ihn wartet.
Damit entstehen zwei Wege, etwas zu bauen, das nie endet, und sie sehen einander ähnlich genug, dass es verlockend ist, sie zusammen zu prüfen. Sie sind nicht dasselbe. Ein Zyklus ist unendlich. Eine Kette, die zu tief ist, ist endlich, aber zu lang. Beantworten Sie beides mit einer Funktion, wird ein Zyklus als „zu tief“ gemeldet, und wer ihn geschrieben hat, sucht nach einer langen Kette, während seine eigene zwei Flows lang ist.
Es gibt also zwei Fragen, und jede wird zweimal gestellt.
Gestellt werden sie einmal beim Speichern eines Flows, statisch, gegen die anderen Flows der Organisation. Diese Prüfung muss dort sein, weil sie die einzige Stelle ist, an der eine gute Fehlermeldung möglich ist: sie kann den Flow benennen, der die Schleife schließt.
Gestellt werden sie erneut während des Laufs. Diese Prüfung muss dort sein, weil Flows unabhängig voneinander bearbeitet werden. Flow A wird gespeichert und ist in Ordnung. Danach bearbeitet jemand Flow C, der jetzt zurück auf A zeigt. Nichts hat A neu geprüft. Die statische Prüfung ist eine Höflichkeit gegenüber dem Schreibenden; die dynamische ist die eigentliche Zusicherung.
Zwei Umsetzungsdetails sind der Erwähnung wert. Die Frage „kann dieser Flow sich selbst in mindestens einem Schritt erreichen“ beantwortet den direkten und den indirekten Fall in einer einzigen Abfrage, und das „mindestens einem“ ist die ganze Definition: ein Pfad aus null Schritten würde jeden Flow sich selbst erreichen lassen. Und der Tiefendurchlauf führt seinen eigenen Pfad mit und gibt unendlich zurück, wenn er auf einen Flow trifft, der schon darauf liegt, er hängt sich also nicht genau an der Eingabe auf, für die es die Zyklusprüfung gibt. Das ist wichtig, denn es bedeutet, dass die beiden Prüfungen in beliebiger Reihenfolge laufen können, und damit muss sich niemand eine Reihenfolgeregel merken.
Die Tiefe wird als Spalte auf dem Kindlauf gespeichert, was gegen unsere übliche Linie verstößt, dass ein ableitbarer Wert keine Spalte ist. Der Fremdschlüssel zum Elternlauf wird null, wenn der Elternlauf gelöscht wird, nach einer Löschung lässt sich die Kette also nicht mehr zurückverfolgen, um sie neu zu berechnen. Ein abgeleiteter Wert, dessen Quelle verschwinden kann, ist nicht ableitbar.
Die Rückfalluhr steht auf einem Tag und nicht auf fünf Minuten
Wenn ein Kindlauf fertig wird, setzt er die Weckzeit auf der Wartezeile des Elternlaufs auf jetzt, und der Elternlauf wird innerhalb von zehn Sekunden aufgegriffen. Das ist der Mechanismus.
Es gibt außerdem eine Rückfalluhr für den Fall, dass das nicht geschieht: ein Container, der auf halbem Weg beim Abschluss des Kindes umfiel, eine Zeile, die von Hand angefasst wurde. Sie steht auf vierundzwanzig Stunden.
Fünf Minuten wären die instinktive Wahl, und sie ist die falsche. Ein Kind, das auf einer Genehmigung sitzt, darf berechtigterweise dreißig Tage warten. Mit einer kurzen Uhr würde der Elternlauf in diesem Zeitraum tausende Male aufwachen, um festzustellen, dass sich nichts geändert hat, und jedes dieser Male ist eine HTTP-Anfrage vom Gateway an den Web-Container. Ein Tag begrenzt den Schaden eines verpassten Signals auf einen Tag und kostet über das längste Warten, das das System zulässt, höchstens dreißig sinnlose Weckvorgänge.
Eine Genehmigung, von der niemand erfährt, ist ein stehender Lauf
Eine Genehmigungsanfrage kann jetzt eine E-Mail senden, und die Kategorie, unter der sie gesendet wird, ist eine eigene und nicht die allgemeine Flow-Kategorie. Das war eine bewusste Abweichung vom Plan.
Die allgemeine Flow-Kategorie geht an alle, die das Automatisierungsmodul haben. Eine Genehmigungsanfrage sollte an die Leute gehen, die sie tatsächlich entscheiden können, und das ist eine andere und kleinere Menge. Auf der Kategorie liegt daher ein zweites Sieb: die Benachrichtigung wird nur an Mitglieder zugestellt, die das Entscheidungsrecht haben.
Gemessen an unserer Testorganisation: die allgemeine Flow-Kategorie ergab drei Empfänger und die Genehmigungskategorie zwei. Zwei Zahlen, die sich unterscheiden, sind der ganze Beweis, dass das Sieb etwas tut.
Ein Klick, der nichts auswählte
Die Messlektion dieser Runde betrifft den Browsertest und nicht das Produkt.
Unser Browserhelfer hat eine Klickmethode, die ein Mausdrücken und ein Mausloslassen absetzt. Auf einer gewöhnlichen Schaltfläche funktioniert das. Auf einem Knoten in der Flow-Leinwand tut es überhaupt nichts: die Eigenschaftsleiste blieb auf dem Auslöserknoten stehen, und die Auswahlliste, die wir ändern wollten, gab es nicht, was sich genau wie eine fehlende Funktion liest.
Die Leinwand beginnt beim Zeigerdruck ein Ziehen und ruft setPointerCapture auf dem Knoten auf. Ohne ein vorangehendes Mausbewegungsereignis und ohne das gesetzte Feld buttons am Drücken erzeugt der Browser keinen Klick, den der Erfassungspfad erkennt. Fügt man diese beiden hinzu, erfolgt die Auswahl sofort.
Im selben Helfer steckt ein zweites Detail. Klicken Sie nicht in die Mitte eines Knotens. Die Mitte eines Knotens mit Ports sitzt zwischen den Portpunkten, und die haben ihre eigene Zeigerbehandlung, um einen Draht herauszuziehen. Klicken Sie stattdessen oben links auf die Kopfzeile.
Beide sehen wie Produktfehler aus, wenn Sie darauf stoßen, und keiner ist einer. Das ist die allgemeine Gestalt des Problems: eine Eingabesimulation, die fast richtig ist, scheitert auf eine Weise, die sich nicht davon unterscheiden lässt, dass die Sache, die Sie prüfen wollten, kaputt ist.
Was wir aufschreiben würden
- Eine Schwelle schlägt ein Kästchen, wenn der richtige Wert des Kästchens immer derselbe ist. Fragen Sie, wann jemand es ausschalten würde; gibt es keine Antwort, bauen Sie den Schalter nicht.
- Messen Sie die Schwelle an dem Budget, in das sie passen muss, und nicht an der Funktion, die Sie begrenzen.
- Setzen Sie die neue Schwelle nicht auf die alte Grenze, nur weil sich dann nichts ändert. Dass sich nichts ändert, ist das Problem, das Sie lösen wollten.
- Ein Zyklus und eine zu lange Kette sind verschiedene Fehler. Eine Meldung für beides schickt die Leute an die falsche Stelle.
- Prüfen Sie beim Speichern für die Meldung und zur Laufzeit für die Zusicherung. Benachbarte Datensätze ändern sich, ohne dass Ihrer neu geprüft wird.
- Eine Suche, die auf ihrem eigenen Pfad endet, kann in beliebiger Reihenfolge relativ zu der Prüfung laufen, die sie stützt, was eine Reihenfolgeregel beseitigt, die sich niemand merken würde.
- Ein abgeleiteter Wert, dessen Quelle gelöscht werden kann, ist nicht abgeleitet. Speichern Sie ihn.
- Bemessen Sie eine Rückfalluhr am längsten berechtigten Warten, nicht daran, wie schnell Sie etwas bemerken möchten.
- Richten Sie eine Benachrichtigung danach aus, wer handeln kann, und nicht danach, zu welchem Modul sie gehört.
- Eine simulierte Eingabe, die fast richtig ist, scheitert wie eine kaputte Funktion. Prüfen Sie, wie die Komponente Ereignisse tatsächlich empfängt, bevor Sie dem Ergebnis glauben.
Zur Klarheit: die Merge-Prüfung lief 16 von 16 Schritten ohne Fehlerzeile durch, die Web-Suite war 4583 grün, die Paket-, Gateway- und Werkzeugsuiten 7286 grün, die Durchlauftests 75 grün, die Subflow-Prüfungen 13 grün, der Ende-zu-Ende-Lauf gegen die Entwicklungsdatenbank 58 grün, die Empfängermessung 4 grün und der Browsertest 31 grün mit einem echten Gateway-Worker, in dem eine Pause von neunzig Sekunden nach fünfundneunzig Sekunden fortgesetzt wurde, ein Elternlauf und ein Kindlauf verknüpft wurden, genau eine Genehmigungs-E-Mail gesendet wurde und eine Kette von vier sowie ein rekursiver Verweis beide abgelehnt wurden, wobei die beanstandete Kette in der Meldung benannt wurde. Der Editortest war 9 grün, und der Barrierefreiheitsdurchlauf fand über sechs Seiten hinweg nichts. Nicht bewiesen: die Zweige für erschöpfte Versuche und veraltete Ansprüche, die Rückfalluhr von vierundzwanzig Stunden, ein Subflow innerhalb einer Schleife, ein verschwundener Kindlauf, die E-Mail an einen Nicht-Eigentümer über einen echten Versand, und überhaupt nichts gegen einen echten Hypervisor.
Aus der Plattformarbeit an Provibr. Wie der Durchlauf überhaupt anhalten lernte, steht in Jeder Selbstaufruf stand in Endposition, und die Gestalt des Graphen selbst steht in Ein Flow ist ein Graph, und er muss endlich sein.