Herausforderungen bei der Versionsverwaltung und Strategien zur Lösung in den Durable Functions

Die Versionsverwaltung in Durable Functions ist unerlässlich, da Funktionen zwangsläufig über die Lebensdauer einer Anwendung hinzugefügt, entfernt und geändert werden. mit Durable Functions können Sie Funktionen so verketten, dass sie nicht zuvor möglich waren, und diese Verkettung wirkt sich auf die Behandlung der Versionsverwaltung aus.

Dieser Artikel hilft Ihnen:

Schneller Strategievergleich

Wenn Sie bereits wissen, dass Ihre Änderung nicht mehr funktioniert, verwenden Sie diese Tabelle, um eine Entschärfungsstrategie auszuwählen:

Strategie Am besten geeignet für: Details
Orchestrierungsversionierung (empfohlen) Die meisten Anwendungen mit unterbrechungsweisen Änderungen. Eingebaute Laufzeitfunktion, funktioniert mit jedem Speicher-Backend. Zum Abschnitt springen
Parallele Bereitstellungen Apps, die keine Orchestrierungsversionsverwaltung verwenden können oder die eine vollständige Isolierung über separate Task-Hubs oder Speicherkonten benötigen. Zum Abschnitt springen
Beenden aller In-Flight-Instanzen Prototyperstellung und lokale Entwicklung, bei der der Verlust von In-Flight-Orchestrierungen akzeptabel ist. Zum Abschnitt springen

Tipp

Wenn Sie nach der integrierten Orchestrierungsversionierung suchen, die automatische Versionsisolation auf Laufzeitebene bietet, lesen Sie Orchestrierungsversionierung.

Von Bedeutung

Überprüfen Sie vor der Bereitstellung, ob Ihre Änderung eine bahnbrechende Änderung ist:

  • Haben Sie den Namen, den Eingabetyp oder den Ausgabetyp einer Aktivitäts- oder Entitätsfunktion geändert?
  • Haben Sie Aufrufe zu Aktivitäten, Unter-Orchestrierungen, Zeitgebern oder externen Ereignissen im Orchestratorcode hinzugefügt, entfernt oder neu angeordnet?
  • Haben Sie eine Funktion umbenannt oder entfernt , die in Flight-Orchestrierungen möglicherweise noch aufgerufen wird?

Wenn Sie auf eines dieser Elemente mit Ja geantwortet haben, verwenden Sie eine der unten aufgeführten Minderungsstrategien, um Fehler bei der Ausführung von Orchestrierungen zu vermeiden.

Arten von Breaking Changes

Es gibt mehrere Beispiele für bruchbrechende Änderungen. In diesem Artikel werden die am häufigsten verwendeten Typen erläutert. Das Hauptthema dahinter ist, dass Änderungen an Funktionscode sowohl neue als auch vorhandene Funktions-Orchestrierungen betreffen.

Signaturänderungen von Aktivitäts- oder Entitätsfunktionen

Eine Signaturänderung bezieht sich auf eine Änderung des Namens, der Eingabe oder der Ausgabe einer Funktion. Wenn Sie diese Art von Änderung an einer Aktivitäts- oder Entitätsfunktion vornehmen, könnte sie jede Orchestratorfunktion unterbrechen, die davon abhängt. Dieses Verhalten gilt insbesondere für typsichere Sprachen. Wenn Sie die Orchestratorfunktion so aktualisieren, dass diese Änderung berücksichtigt wird, können Sie vorhandene In-Flight-Instanzen unterbrechen.

Betrachten Sie beispielsweise die folgende Orchestratorfunktion.

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool result = await context.CallActivityAsync<bool>("Foo");
    await context.CallActivityAsync("Bar", result);
}

Diese Funktion übernimmt das Ergebnis von Foo und übergibt sie an Bar. Angenommen, Sie müssen den Rückgabewert von Foo von einem booleschen In eine Zeichenfolge ändern, um eine größere Vielfalt von Ergebniswerten zu unterstützen. Das Ergebnis sieht wie folgt aus:

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    string result = await context.CallActivityAsync<string>("Foo");
    await context.CallActivityAsync("Bar", result);
}

Diese Änderung funktioniert für alle neuen Instanzen der Orchestratorfunktion ohne Probleme, aber für In-Flight-Instanzen können Fehler auftreten. Betrachten Sie z. B. den Fall, in dem eine Orchestrierungsinstanz eine Funktion namens Fooaufruft, einen booleschen Wert zurückgibt, und dann Prüfpunkte. Wenn die Signaturänderung an diesem Punkt bereitgestellt wird, schlägt die Instanz mit dem Prüfpunkt sofort fehl, wenn der Vorgang fortgesetzt und der Aufruf von Foo wiederholt wird. Dieser Fehler tritt auf, da das Ergebnis in der Verlaufstabelle ein boolescher Wert ist, aber der neue Code versucht, ihn in einen String-Wert zu deserialisieren, was zu unerwartetem Verhalten oder sogar einer Laufzeitausnahme für typsichere Sprachen führt.

Dieses Beispiel ist eine von vielen Möglichkeiten, wie eine Funktionssignaturänderung vorhandene Instanzen unterbrechen kann. Wenn ein Orchestrator im Allgemeinen die Art und Weise ändern muss, wie eine Funktion aufgerufen wird, ist die Änderung wahrscheinlich problematisch.

Orchestrator-Logikänderungen

Die andere Klasse von Versionsverwaltungsproblemen besteht darin, den Orchestratorfunktionscode so zu ändern, dass der Ausführungspfad für In-Flight-Instanzen geändert wird.

Betrachten Sie die folgende Orchestratorfunktion:

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool result = await context.CallActivityAsync<bool>("Foo");
    await context.CallActivityAsync("Bar", result);
}

Gehen Sie nun davon aus, dass Sie einen neuen Funktionsaufruf zwischen den beiden vorhandenen Funktionsaufrufen hinzufügen möchten.

[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
    bool result = await context.CallActivityAsync<bool>("Foo");
    if (result)
    {
        await context.CallActivityAsync("SendNotification");
    }

    await context.CallActivityAsync("Bar", result);
}

Diese Änderung fügt einen neuen Funktionsaufruf zu SendNotification zwischen Foo und Bar hinzu. Es sind keine Signaturänderungen vorhanden. Das Problem tritt auf, wenn die Ausführung einer vorhandenen Instanz nach dem Aufruf von Bar fortgesetzt wird. Wenn bei der Wiederholung der ursprüngliche Aufruf von Foo den Wert true zurückgegeben hat, ruft die Wiederholung des Orchestrators SendNotification auf, was sich nicht im Ausführungsverlauf befindet. Die Laufzeit erkennt diese Inkonsistenz und löst einen nicht deterministischen Orchestrierungsfehler aus, weil sie einen Aufruf an SendNotification festgestellt hat, als sie einen Aufruf an Bar erwartete. Dieselbe Art von Problem kann auftreten, wenn API-Aufrufe zu anderen dauerhaften Vorgängen hinzugefügt werden, z. B. das Erstellen dauerhafter Zeitgeber, das Warten auf externe Ereignisse oder das Aufrufen von Unter-Orchestrierungen.

Entschärfungsstrategien

Warnung

Das Bereitstellen fehlerhafter Änderungen ohne eine Risikominderungsstrategie (der Ansatz "Nichts tun") kann dazu führen, dass Orchestrierungen mit nicht deterministischen Orchestrierungsfehlern fehlschlagen, in einem Running Status unbegrenzt hängen bleiben oder Laufzeitfehler auf niedriger Ebene auslösen, die die Leistung beeinträchtigen. Verwenden Sie immer eine der folgenden Strategien, wenn Sie bahnbrechende Änderungen bereitstellen.

Im Gegensatz zu den anderen Strategien in diesem Abschnitt ist die Orchestrierungsversionierung eine integrierte Laufzeitfunktion, die automatische Versionsisolation bereitstellt. Sie müssen keine separaten Bereitstellungen, Aufgabenhubs oder Speicherkonten verwalten. Stattdessen verfolgt die Laufzeit selbst Versionsinformationen und stellt sicher, dass Orchestrierungsinstanzen von kompatiblen Workern verarbeitet werden.

Mit Orchestrierungsversionierung:

  • Jede Orchestrierungsinstanz erhält eine Version, die bei der Erstellung dauerhaft damit verknüpft ist.
  • Orchestrator-Funktionen können ihre Version untersuchen und die Ausführung entsprechend verzweigen, wodurch alte und neue Codepfade in derselben Codebasis gehalten werden.
  • Mitarbeiter, die neuere Orchestratorfunktionsversionen ausführen, können weiterhin Orchestrierungsinstanzen ausführen, die von älteren Versionen erstellt wurden.
  • Die Laufzeit verhindert, dass Mitarbeiter, die ältere Orchestratorfunktionsversionen ausführen, Orchestrierungen neuerer Versionen ausführen.

Dieser Ansatz erfordert eine minimale Konfiguration (eine Versionszeichenfolge und eine optionale Übereinstimmungsstrategie) und ist mit jedem Speicheranbieter kompatibel. Dies ist die empfohlene Strategie für Anwendungen, die Breaking Changes unterstützen müssen und gleichzeitig Bereitstellungen ohne Ausfallzeiten gewährleisten sollen.

Ausführliche Anleitungen zur Konfiguration und Implementierung finden Sie unter Orchestration Versioning.

Beendigung aller ausgeführten Instanzen

Eine andere Möglichkeit besteht darin, alle ausgeführten Instanzen zu beenden. Wenn Sie den Standardanbieter Azure Storage für Durable Functions verwenden, beenden Sie alle Instanzen, indem Sie den Inhalt der internen control-queue und workitem-queueWarteschlange löschen. Alternativ können Sie die Funktions-App beenden, diese Warteschlangen löschen und die App neu starten. Die Warteschlangen werden automatisch neu erstellt, sobald die App neu gestartet wird. Die vorherigen Orchestrierungsinstanzen bleiben möglicherweise auf unbestimmte Zeit im Zustand "Ausführen", aber sie überladen Ihre Protokolle nicht mit Fehlermeldungen oder verursachen Schäden an Ihrer App. Dieser Ansatz eignet sich ideal für die schnelle Prototypentwicklung, einschließlich der lokalen Entwicklung.

Warnung

Dieser Ansatz erfordert direkten Zugriff auf die zugrunde liegenden Speicherressourcen und ist nicht für alle Speicheranbieter geeignet, die von Durable Functions unterstützt werden.

Parallele Bereitstellungen

Die beste Möglichkeit zur Garantie, dass wichtige Änderungen auf sichere Weise bereitgestellt werden, ist die parallele Bereitstellung mit Ihren älteren Versionen. Sie können eine der folgenden Techniken verwenden:

  • Anderes Speicherkonto: Stellen Sie alle Updates als neue Funktions-App mit einem anderen Speicherkonto bereit. Dadurch wird der Zustand der neuen Version vollständig von der alten Version isoliert.
  • Unterschiedlicher Aufgabenhub: Stellen Sie eine neue Kopie der Funktions-App mit demselben Speicherkonto, aber mit einem aktualisierten Aufgabenhubnamen bereit. Dieser Ansatz erstellt neue Speicherartefakte für die neue Version, während die alte Version weiterhin ihre vorhandenen Artefakte verwendet.

Bei parallelen Bereitstellungen in Azure können Sie Deployment-Slots verwenden, um beide Versionen gleichzeitig mit nur einer als aktiven productionPlatz auszuführen. Wenn Sie bereit sind, die neue Orchestrierungslogik einzusetzen, schalten Sie die neue Version in den Produktions-Slot um.

Hinweis

Dieser Leitfaden verwendet Azure Storage spezifische Ausdrücke, gilt jedoch im Allgemeinen für alle unterstützten Durable Functions-Speicheranbieter.

Hinweis

Bereitstellungsplatz-Swaps funktionieren am besten mit HTTP- und Webhook-Triggern. Bei Nicht-HTTP-Triggern wie Warteschlangen oder Event Hubs sollte die Triggerdefinition von einer App-Einstellung abgeleitet werden, die als Teil des Swapvorgangs aktualisiert wird.

Nächste Schritte