Ausführungssemantik, Einschränkungen und Sicherheit

Zuletzt aktualisiert: 21. Juli 2026

Diese Seite fasst zusammen, wie sich ein Script zur Laufzeit verhält (Reihenfolge, Transaktionen, Fehler, Sperren), welchen statischen Einschränkungen es bei der Speicherung unterliegt und sein Sicherheitsmodell. Zur Syntax siehe den Statement-Katalog und die Wertausdrücke; für praktische Kombinationen siehe das Cookbook.

Ausführungsreihenfolge und Modus

  • statements werden von oben nach unten sequenziell ausgeführt. Sobald die Ausführung ein Return erreicht, endet sie an diesem Punkt.
  • Sync läuft auf dem Weg der Anfrageverarbeitung, Async im Hintergrund. Das ist nur eine Unterscheidung, wo die Ausführung stattfindet; das Ergebnis ist in beiden Fällen der Return-Wert (zur Form der Aufrufantwort siehe Anfrage und Antwort in der Script-Übersicht und Ausführungsmodi).
  • Von der Fähigkeit (capability) zum Modus: Enthält der Statement-Baum eines von ExternalIo (ein Http-Aufruf nach außen), MediaIngest (Media-Datei-Ingest; { source, encoding } unter fields.file; gleichermaßen für url und base64) oder LongRunning (eine große Loop usw.), dann wird executionMode auf Async erzwungen. Diese drei sind eigenständige Fähigkeiten und unterscheiden sich darin, welchem Limit sie unten unter Statische Einschränkungen zugerechnet werden.

Ausführungssemantik

Guard (Vorbedingungen)

Es gibt kein dediziertes Guard-Statement. Man drückt es mit If und then:[Return] aus. Bei einer Verletzung der Bedingung wird ein Ergebnis zurückgegeben und die nachfolgenden Statements werden nicht ausgeführt (ein Script ohne Guard ist selbstverständlich ebenfalls möglich).

{ "type": "If", "condition": { "<": [ "{ /wallet/fields/balance/en-US }", "{ /payload/fields/cost }" ] },
  "then": [ { "type": "Return", "value": { "ok": false, "reason": "insufficient credit" }, "statusCode": 402 } ] }

Keine Transaktionen und Best-Effort-Kompensation

Ein Script ist keine Transaktion. Bei einem Fehler versucht die Engine, die bisher ausgeführten Vorgänge zu kompensieren (Kompensation), und gibt die Fehlerursache zurück, jedoch mit den folgenden Einschränkungen (als Designkompromiss in Kauf genommen).

  • Das Rückgängigmachen einer Löschung erzeugt eine neue sys.id, wodurch Referenzen, die darauf zeigten, brechen (das Zurückrollen einer Erstellung ist einfach, eine Änderung benötigt ein before-image).
  • Externe Effekte (Http) sind irreversibel (ein bereits abgesetzter Aufruf und dessen Kosten lassen sich nicht rückgängig machen).
  • Bei einem Prozessabsturz kann ein nicht kompensierter Zustand (ein Orphan) zurückbleiben.

Wenn Sie echte Atomarität benötigen, kompensieren Sie im Script selbst oder platzieren Sie nicht rückgängig zu machende Vorgänge (etwa externe Aufrufe) ganz am Ende. Am gefährlichsten ist die Reihenfolge, die "sich durchkettet, aber nicht zurückrollen lässt und dennoch sicher aussieht".

Optimistisches Sperren

Update-/Patch-Konkurrenz grenzen Sie über das version-Feld von ResourceUpdate und ResourcePatch ein. Geben Sie version (ein Wertausdruck, Int) an, wird nur dann aktualisiert, wenn es mit der aktuellen sys.version des Ziels übereinstimmt; bei Nichtübereinstimmung wird mit einem Versionskonfliktfehler abgebrochen (lokal mit Try/catch behandelbar). Lassen Sie es weg, gilt last-write-wins ohne Prüfung. Üblicherweise liest man zuerst mit ResourceRead oder ResourcePageRead und übergibt diese sys.version (siehe Optimistisches Sperren mit CAS im Cookbook).

Schreibvorgänge gehen an origin

Schreibvorgänge landen immer in origin (draft), und die Sichtbarkeit auf der Auslieferung (CDA/ACDA) wird über publish gesteuert (das publish von ResourceCreate/ResourceUpdate/ResourcePatch oder ResourcePublish/ResourceUnpublish).

Was als Fehler zählt

  • Ein echter Fehler ist ein Statement-Laufzeitfehler: ein finaler Http-Status von 400 oder höher (4xx·5xx; kein Fehler bei ignoreStatusCode: true) oder ein Timeout, ein Antwort-Body, der 10MiB überschreitet, oder ein fehlgeschlagener Ressourcenvorgang (kein Ziel vorhanden, ein Versionskonflikt, ein nicht unterstützter Vorgang usw.). Bei solchen Fehlern bricht die Engine ab und kompensiert, und Sie können sie lokal mit Try/catch/finally behandeln.
  • Ein Return ist kein Fehler, sondern ein normaler vorzeitiger Ausstieg. Es ist kein catch-Ziel (es gibt kein Konzept des benutzerseitigen throw).
  • Innerhalb eines catch referenzieren Sie { message, statement } über /error.

Keine serverseitige Aggregation

Es gibt keine dedizierten Servervorgänge für count, sum oder group-by. Berechnet wird durch Iteration mit ResourcePageRead und mit SetVar/JsonLogic, weshalb Sie an die Fetch-Größe und maxIterations gebunden sind (für das Aggregieren von Millionen von Datensätzen ungeeignet).

Kein Warten oder Verzögern

Ein Script hat kein Delay-Statement. Ein Script wird einmal ausgeführt und ist dann fertig, auf dem Anfrageweg (Sync) oder im Hintergrund (Async), und wartet oder pollt intern nicht, bis ein externer Job fertig ist (das Async-Ergebnis ist davon getrennt: Der Aufrufer pollt mit der im 202 erhaltenen requestId, um den Return-Wert zu erhalten).

Statische Einschränkungen (bei der Speicherung validiert)

Das Folgende wird geprüft, wenn ein Script gespeichert wird (beim Erstellen/Ändern). Bei einer Verletzung wird die Speicherung abgelehnt (der Fehler tritt zum Autorenzeitpunkt auf, nicht zur Laufzeit).

EinschränkungStandard
Bei externem I/O muss executionMode Async seinNicht zutreffend
Innerhalb eines Loop-Body sind Http-Aufrufe nach außen und Media-Datei-Ingest verbotenNicht zutreffend
Maximale Http-Aufrufe nach außen pro Definition3 (maxExternalIo)
Maximale SetVar pro Definition (inklusive verschachtelter)5 (maxSetVar)
Maximale Gesamtzahl an Statements pro Definition (inklusive verschachtelter)15 (maxStatements)
Obergrenze für Http.retry2 (maxHttpRetry)

Die Limits lassen sich über Servereinstellungen (weegloo.core.script.*) anpassen (die obigen Werte sind die Standardwerte).

Der Media-Datei-Ingest ist die MediaIngest-Fähigkeit und wird, anders als ein Http-Aufruf nach außen (ExternalIo), nicht auf das maxExternalIo-Limit (3) angerechnet. Die Async-Pflicht und das Verbot im Loop-Body gelten für ihn jedoch genauso wie für Http.

Zeitbudget (Laufzeit)

ModusStandardbudget
Sync10 Sekunden (syncTimeoutMs)
Async60 Sekunden (asyncTimeoutMs)

Anzahllimits pro Plan

Script ist eine Billable-Ressource, und die Anzahl pro Organization ist je nach Plan begrenzt.

PlanScript-Anzahl
Free3
Basic10
Pro50
EnterpriseUnbegrenzt

Wenn das Limit erreicht ist, wird das Erstellen eines neuen Script abgelehnt (derselbe Weg wie bei anderen Billable-Ressourcen).

Sicherheitsmodell

Secret-Header

Ein Eintrag in Http.headers mit secret:true ist nur für die CMA (Administrator) bestimmt, wird dem Endnutzer (ServiceUser) nicht offengelegt und erst unmittelbar vor dem Senden entschlüsselt. Legen Sie hier Geheimnisse wie einen LLM-API-Schlüssel ab (selbst wenn er in ein App Bundle gepackt wird, wird der Secret-Wert maskiert und verlässt niemals den ursprünglichen Space).

Ausführungsidentität und Autorisierung

  • Ausführungsidentität: Während der Ausführung wird jeder Ressourcenvorgang unter der Identität des Benutzers ausgeführt, der /execute aufgerufen hat. Der createdBy/updatedBy einer erstellten oder geänderten Ressource ist der Aufrufer, und auch ein createdBy: ":self"-Scope wird anhand des Aufrufers aufgelöst.
  • Es gibt zwei Autorisierungsgrenzen, und zur Laufzeit prüft die Engine die Ressourcenberechtigungen nicht pro Statement erneut.
    1. Zum Autorenzeitpunkt (Speicherung): Beim Speichern eines Script wird geprüft, ob der Autor die von seinen Statements verwendeten Ressourcen- und Aktionsberechtigungen tatsächlich besitzt. Fehlt auch nur eine, wird die Speicherung abgelehnt (WGL403015). Mit anderen Worten: Ein Script, das einen unautorisierten Vorgang enthält, wird von vornherein nicht gespeichert.
    2. Zum Aufrufzeitpunkt (/execute): Es wird nur die Execute-Berechtigung des Aufrufers auf das Script geprüft. Ohne sie ergibt sich 403. Nach bestandener Prüfung werden die Ressourcenberechtigungen pro Statement zur Laufzeit nicht erneut überprüft, und die Ausführung läuft. Das funktioniert wie die Berechtigung zum Ausführen einer Funktion in der Programmierung. Wer die Berechtigung hat, die Funktion auszuführen, wird nach der Berechtigung für die einzelnen Vorgänge darin nicht erneut gefragt.
  • Eigentümer-Scope: createdBy: ":self" in einem where-Filter bedeutet "nur das, was der aktuelle Aufrufer erstellt hat" (z. B. nur die eigene Wallet abfragen).
  • Delegierte Berechtigungen (Vorsicht für Autoren): Verbindet man die beiden obigen Grenzen, ist das Ausführen eines Script gleichbedeutend mit einem Handeln mit den an das Script delegierten Berechtigungen des Autors. Der Aufrufer braucht nur Execute, und die Statements innerhalb des Script laufen genau in dem Umfang, für den der Autor bei der Speicherung autorisiert war. Dadurch kann ein Ressourcenvorgang, den der Aufrufer selbst nicht durchführen könnte, dennoch über das Script geschehen. Da die dem Autor erteilten Berechtigungen die effektive Reichweite dieses Script bestimmen, überlegen Sie sorgfältig, welche Vorgänge Sie in ein Script aufnehmen.

Zusammenfassende Checkliste

Prüfen Sie vor dem Speichern Folgendes.

  • Gibt es einen externen Aufruf (Http) oder einen Media-Datei-Ingest, ist executionMode gleich "Async".
  • Sie haben keinen externen Aufruf in einen Loop-Body gesetzt.
  • Externe Aufrufe sind höchstens 3, SetVar höchstens 5 und die Gesamtzahl der Statements höchstens 15.
  • Secret-Werte wurden nur über secret:true in Http.headers eingetragen.
  • Nicht rückgängig zu machende Vorgänge (externe Aufrufe) wurden möglichst weit hinten platziert.
  • Wenn Sie Update-/Patch-Konkurrenz befürchten, verwenden Sie das version-Feld von ResourceUpdate oder ResourcePatch.
  • Um ein Ergebnis zurückzugeben, haben Sie Return.value angegeben.