Backend-Operationen

TL;DR

  • Ein backend ist keine statische Datei — es ist das Ergebnis einer Sequenz typisierter Mutationen, angewandt auf einen leeren Ausgangszustand. Das Operationslog ist die Quelle der Wahrheit; der Graph-Zustand ist abgeleitet.
  • Jede Oberfläche — App, CLI, SDK, REST-API — spricht dieselbe Operationsmenge. Eine über die CLI vorgenommene Änderung erscheint identisch über die App; es gibt eine Historie, unabhängig davon, welche Oberfläche sie erzeugt hat.
  • Die Operationen bilden ein kleines typisiertes Vokabular: add-vertex, connect, change-parameter, add-file, change-endpoint-alias, change-version, delete-vertex, disconnect und eine Handvoll weiterer. Jede wird mit ihrer Inversen aufgezeichnet, sodass das Log inspizierbar und rückgängig machbar ist.
  • Drei Meta-Operationen decken die Historienverwaltung ab: undo nimmt die letzten N Operationen zurück, compact schreibt das Log in die minimale Sequenz um, die denselben finalen Graphen erzeugt, operations durchstöbert, was passiert ist.
  • Parameterwerte sind pipelang-Literale (kein JSON), typisiert gegen die Deklaration, die die component in ihrem config_schema gemacht hat. Ein Wert, dessen Typ nicht zum Slot passt, wird zur Bearbeitungszeit abgelehnt, nicht beim deploy.

Was backend-Operationen sind

Ein backend ist ein event-sourced Graph: ein Strom kleiner typisierter Operationen, angewandt auf einen leeren Ausgangszustand, wobei der aktuelle Graph durch ihr Abspielen abgeleitet wird. Das Operationslog ist die Quelle der Wahrheit; der Graph ist die Projektion.

Jede Operation ist eng — ein vertex, eine Kante, ein Parameter — und wird in dem Moment validiert, in dem sie landet. Die Plattform zeichnet jede Operation zusammen mit ihrer Inversen auf, sodass das Log sowohl inspizierbar als auch rückgängig machbar ist. Weil der Graph das Abspielen des Logs ist statt einer mit ihm synchron gehaltenen Datei, mergen gleichzeitige Bearbeitungen als unabhängige Operationen, statt einander zu überschreiben, und der Graph zu einem beliebigen vergangenen Zeitpunkt ist genau das Präfix der Operationen bis zu diesem Punkt.

Das ist der Standard-Event-Sourcing-Kompromiss. Die Plattform besitzt die Mutationsoberfläche — es gibt keine Out-of-Band-Datei zum direkten Bearbeiten — und im Gegenzug wird jede Änderung validiert, bevor sie landet, mit ihrer Inversen aufgezeichnet und ist umkehrbar. Undo nimmt aufgezeichnete Operationen zurück; die Historie beantwortet, wer was wann geändert hat; "der Graph wie er letzten Dienstag war" ist ein konkretes Abspielen statt einer Vermutung.

Die CLI-Verben, der Graph-Editor der App, die REST-API und jedes SDK erzeugen alle dieselben Operationszeilen gegen dasselbe Log. Keine Oberfläche hat einen separaten Low-Level-Pfad, der es umgeht. Diese Einheitlichkeit ist das, was dich ein backend von einem Tool bearbeiten lässt, ohne kaputtzumachen, was ein anderes Tool sieht.

Mentales Modell

   empty
     │  add-vertex          v1  (pinned to release X)
     │  add-vertex          v2  (pinned to release Y)
     │  add-vertex          v3  (pinned to release Z)
     │  connect             v1.out0 → v2.in0
     │  connect             v2.out0 → v3.in0
     │  change-parameter    v2.threshold = 0.7
     │  add-file            v2.weights  = <file_id>
     │  change-endpoint-alias  v3.image_out → preview
     ▼
   current backend graph

Jede Zeile ist eine aufgezeichnete Operation. Der Graph, den die App rendert, der Graph, den ppl backend get zurückgibt, und der Graph, den der deploy-Schritt validiert, sind alle das Abspielen dieses Logs. Dasselbe backend von der CLI, der UI oder einem Skript zu bearbeiten, schreibt neue Zeilen in dasselbe Log; die Oberfläche, die eine Änderung initiierte, wird nicht als besonders aufgezeichnet.

Validierung passiert bevor eine Operation landet. Ein connect, dessen Quell- und Zieltypen nicht unifizieren, wird abgelehnt und nicht aufgezeichnet; der Graph bleibt by construction gültig. Ein change-parameter, dessen Literalwert nicht zum deklarierten Typ passt, wird beim Aufruf abgelehnt. Ein add-file, dessen Dateityp nicht zum Slot passt, wird abgelehnt. Das Log enthält stets nur Operationen, die die Plattform akzeptiert hat, weshalb "ist der aktuelle Graph gültig" dieselbe Frage ist wie "was ist im Log gelandet".

Siehe Backends für den Graphen, den das Log erzeugt, und Types für den Validierungsvertrag.

Das Operationsvokabular

Verwende dies als Katalog der "welche Verben existieren".

Die Operationsmenge ist absichtlich klein. Fast jede backend-Bearbeitung passt in eines der unten stehenden Verben, und die Plattform lehnt alles außerhalb dieses Vokabulars ab.

Graph-Topologie — Operationen, die ändern, welche vertices existieren und wie sie sich verdrahten. add-vertex fügt eine component-Instanz zum Graphen hinzu (pinnt eine bestimmte Release-Version). delete-vertex entfernt einen vertex und kaskadiert alle Disconnects. connect verdrahtet (from-vertex, from-output) zu (to-vertex, to-input). disconnect entfernt die eingehende Kante auf einem bestimmten Input — Inputs akzeptieren höchstens eine Upstream-Kante, weshalb disconnect das Ziel entgegennimmt. change-version migriert einen vertex auf ein anderes Release derselben component und behält seine bestehenden Bindungen, solange die Typen des neuen Release kompatibel sind.

vertex-Bindungen — Operationen, die die Konfiguration eines vertex ändern. change-parameter setzt einen typisierten config-Parameter auf ein pipelang-Literal. add-file bindet eine Datei an einen der file_schema-Slots des vertex (--file "" zu übergeben löscht die Bindung). add-constraint pinnt oder verfeinert die Generic-Type-Bindungen eines vertex, wenn die Typinferenz nicht von allein entscheiden kann.

vertex-Metadaten — Operationen, die beeinflussen, wie der vertex benannt oder gerendert wird. change-alias benennt den Anzeige-Alias des vertex um. change-layout setzt die (x, y, w, h)-Position in Graph-Ansichts-Tools. change-metadata setzt einen freiformigen Metadaten-Schlüssel.

endpoint-Freilegung — Operationen, die beeinflussen, wie externe Aufrufer den vertex erreichen. change-endpoint-alias bildet einen component-deklarierten endpoint-Namen auf einen stabilen Rollen-String ab, den die application zur Laufzeit auflöst. change-host-port-binding bildet einen Container-Port auf einen Host-Port für direkten Zugriff ab. change-volume-mount mountet ein Volume in den Container.

runtime-Overrides — Operationen, die anpassen, wie ein vertex läuft, wenn er deployt ist. change-service-config überschreibt einen (service, key)-Wert auf den depends_on-Deklarationen eines vertex (z. B. den Modellnamen oder die Pull-Strategie eines Serving-Dienstes tauschen). Das sind Notausgänge für die Fälle, in denen die Defaults der component auf backend-Ebene überschrieben werden müssen.

Jede Operation ist eine Zeile im Log. Operationen, die die Plattform ablehnt (ein typ-inkompatibles connect, ein Parameter-Literal des falschen Typs, ein Alias auf einem endpoint, den die component nicht deklariert), werden überhaupt nicht aufgezeichnet — der Graph bleibt gültig, weil ungültige Operationen nie landen.

Undo, compact, durchstöbern

Verwende diese drei Verben, um auf dem Log selbst zu operieren statt auf dem Graphen.

ppl backend undo <bid> <count> nimmt die letzten N Operationen in umgekehrter Reihenfolge zurück. Das undo wird selbst als neue Operationen aufgezeichnet, sodass die Spur dessen, was getan, zurückgenommen und wiederholt wurde, bewahrt statt überschrieben wird. Verwende es, um die jüngsten Bearbeitungen zurückzugehen, ohne frühere zu stören.

ppl backend compact <bid> schreibt das aufgezeichnete Log in die minimale Sequenz von Operationen um, die denselben finalen Graphen erzeugt. Überholte Parameterwerte werden verworfen, zurückgenommene Änderungen werden verworfen, und connect/disconnect-Hin-und-Her kollabiert in die Kanten, die tatsächlich existieren; der Graph selbst bleibt unverändert. Nach compact sind die früheren Operationen nicht mehr im Log, sodass undo nicht mehr durch sie zurückgreifen kann. Compact eignet sich für ein backend, das eine lange Bearbeitungshistorie angesammelt hat und dessen Log zu verrauscht zum Überfliegen ist — der finale Graph wird bewahrt, die überholten Operationen werden verworfen.

ppl backend operations <bid> durchstöbert das Log: wer was wann geändert hat, wie viele Operationen zurückzunehmen sind und warum ein backend so aussieht, wie es aussieht.

Ein häufiges kombiniertes Muster: operations, um zu sehen, was kürzlich passiert ist, undo, um die schlechten Bearbeitungen zurückzugehen, vom korrigierten Zustand aus weiterbearbeiten.

Batch-Mutationen mit patch

Verwende dies, wenn viele kleine Operationen zusammen landen sollen oder gar nicht.

ppl backend patch <bid> --file ./changes.json wendet einen JSON-Batch von Operationen als einzelne Transaktion an. Der erste Fehler bricht ab und der gesamte Batch wird zurückgerollt, sodass entweder alle Bearbeitungen landen oder keine. --dry-run hinzuzufügen (oder dry_run: true in der Datei) zeigt das Ergebnis vorab, ohne zu committen — nützlich, wenn der Batch von Automatisierung generiert wird und der Aufrufer wissen will, ob er typchecken würde, bevor er ihn ernsthaft sendet.

Die transaktionale Form ist wichtig, weil das Ausgeben jeder Operation als eigener CLI-Aufruf keine Atomaritätsgarantie hat. Ein Skript, das zwölf Operationen einzeln ausgibt und bei der zehnten fehlschlägt, hinterlässt das backend mit neun angewandten; patch stellt sicher, dass entweder alle zwölf angewandt werden oder keine.

Wie Parameter-Literale funktionieren

Verwende diese Rahmung immer dann, wenn change-parameter ablehnt, was wie ein sinnvoller Wert aussieht.

Parameterwerte sind pipelang-Literale, kein JSON. Das --type-Flag deklariert, welchem Typ der Wert entsprechen soll; der --value ist das Literal in pipelang-Syntax. Die Plattform validiert, dass das Literal gegen den Typ parst und dass der Typ mit dem deklarierten Parametertyp der component aus ihrem config_schema übereinstimmt.

Die Grammatik ist klein und spezifisch: Strings in doppelten Anführungszeichen, Tupel in Klammern, Listen in eckigen Klammern, Records in geschweiften Klammern, kleingeschriebene Booleans, dezimale Floats. JSON-artige Werte wie "true" (ein zitierter String) oder 1 (ein Integer, wo ein Double erwartet wird) werden beim change-parameter-Aufruf abgelehnt. Die vollständige Grammatik liegt in der Typsyntax-Referenz.

Shell-Quoting ist ebenfalls wichtig. Setze das vollständige Literal in Shell-Aufrufen in einfache Anführungszeichen, damit die Shell Klammern, eckige Klammern oder Anführungszeichen nicht auffrisst, bevor der pipelang-Parser sie sieht:

ppl backend change-parameter <bid> --vertex 2 --name threshold \    --type Double --value '0.7'

Siehe Types für das Typsystem, gegen das die Validierung läuft.

Was das Log dir ermöglicht

Verwende diese Rahmung, um zu verstehen, welche Fähigkeiten nachgelagerte Konsequenzen des event-sourced Designs sind.

Die event-sourced Form ist das, was mehrere andere Plattformfähigkeiten möglich macht. Undo ist eine. Reproduzierbarkeit ist eine andere: dasselbe Log erzeugt beim Abspielen jedes Mal denselben Graphen. Audit ist eine dritte: das Log ist der maßgebliche Datensatz jeder Änderung, einschließlich wer sie wann gemacht hat. Test-and-Verify-Schleifen hängen davon ab, weil ein backend mit einer gepinnten Operationshistorie "die gestern getestete Version" adressierbar macht. Ein backend mit ppl backend create --source <bid> zu forken kopiert das Log in ein neues Objekt; der Fork startet identisch und divergiert durch seine eigenen nachfolgenden Operationen.

Das Design verlangt, dass jede Änderung durch eine Operation geht. Es gibt keinen Out-of-Band-Pfad zum Handbearbeiten des Graphen und keine Möglichkeit, das Log zu umgehen. Im Gegenzug ist das Log der einzige Ort, an dem man nachsieht, was sich geändert hat, und jede nachgelagerte Fähigkeit baut auf dieser Eigenschaft auf.

Wo das hineinpasst

backend-Operationen sind das atomare Bearbeitungsalphabet der Plattform. Jeder höhere Workflow — einen Graphen zusammensetzen, ein Modell tauschen, einen neuen endpoint freilegen, eine Änderung zurückrollen, für parallele Umgebungen forken — zerlegt sich in eine Sequenz dieser typisierten Operationen. Das Vokabular ist klein genug, um es einmal zu lernen, und groß genug, um die Arbeit auszudrücken, und das event-sourced Log ist der maßgebliche Datensatz dessen, was passiert ist.

Verwandt

  • Backends — die konzeptionelle Schicht, die diese Operationen bauen.
  • Types — wogegen connect und change-parameter validieren.
  • File-Binding — was add-file bindet und welche config es trägt.
  • Solutions — wie vertices und Kanten zu einem ausgelieferten Produkt werden.
  • Secrets — einen secret: true-Parameter an ein Workspace-secret binden.
  • Deployments — die runtime-Seite; das Log ist die Eingabe, das deployment ist die Ausgabe.
  • Typsyntax-Referenz — vollständige pipelang-Literal-Grammatik.

War diese Seite hilfreich?