Dela via


Versionsutmaningar och metoder i Durable Functions (Azure Functions)

Det är oundvikligt att funktioner läggs till, tas bort och ändras under programmets livslängd. Durable Functions möjliggör sammanlänkning av funktioner på sätt som inte tidigare var möjliga, och den här länkningen påverkar hur du kan hantera versionshantering.

Typer av brytande ändringar

Det finns flera exempel på icke-kompatibla ändringar att känna till. I den här artikeln beskrivs de vanligaste. Huvudtemat bakom dem är att både nya och befintliga funktionsorkestreringar påverkas av ändringar i funktionskoden.

Ändra aktivitets- eller entitetsfunktionssignaturer

En signaturändring refererar till en ändring i namnet, indata eller utdata för en funktion. Om den här typen av ändringar görs i en aktivitet eller entitetsfunktion kan den bryta alla orkestreringsfunktioner som är beroende av den. Detta gäller särskilt för typsäkra språk. Om du uppdaterar orchestrator-funktionen för att hantera den här ändringen kan du bryta befintliga instanser under flygning.

Anta till exempel att vi har följande orchestrator-funktion.

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

Den här förenklade funktionen tar resultaten från Foo och skickar den till Bar. Anta att vi måste ändra returvärdet för Foo från ett booleskt värde till en sträng för att stödja en bredare mängd resultatvärden. Resultatet ser ut så här:

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

Den här ändringen fungerar bra för alla nya instanser av orchestrator-funktionen men kan bryta alla instanser under flygning. Tänk till exempel på fallet där en orkestreringsinstans anropar en funktion med namnet Foo, hämtar tillbaka ett booleskt värde och sedan kontrollpunkter. Om signaturändringen distribueras vid denna tidpunkt, kommer kontrollpunktsinstansen att misslyckas direkt när den återupptas och spelar upp anropet igen till Foo. Det här felet inträffar eftersom resultatet i historiktabellen är ett booleskt värde, men den nya koden försöker deserialisera den till ett Strängvärde, vilket resulterar i oväntat beteende eller till och med körningsundantag för typsäkra språk.

Det här exemplet är bara ett av många olika sätt som en funktionssignaturändring kan bryta befintliga instanser på. I allmänhet, om en orkestrerare behöver ändra hur den anropar en funktion, kommer ändringen sannolikt att vara problematisk.

Ändra orkestreringslogik

Den andra klassen av versionsproblem uppstår från att ändra koden för orchestrator-funktionen på ett sätt som ändrar körvägen för pågående instanser.

Överväg följande orchestrator-funktion:

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

Anta nu att du vill göra en ändring för att lägga till ett nytt funktionsanrop mellan de två befintliga funktionsanropen.

[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);
}

Den här ändringen lägger till ett nytt funktionsanrop till SendNotification mellan Foo och Bar. Det finns inga signaturändringar. Problemet uppstår när en befintlig instans återupptas från anropet till Bar. Under uppspelningen, om det ursprungliga anropet till Foo returnerade true, kommer orchestratorns uppspelning att anropa SendNotification, som inte finns i sin körhistorik. Körningen identifierar den här inkonsekvensen och genererar ett icke-deterministiskt orkestreringsfel eftersom den påträffade ett anrop till SendNotification när ett anrop till Bar förväntades. Samma typ av problem kan uppstå när du lägger till API-anrop till andra varaktiga åtgärder, som att skapa varaktiga timers, vänta på externa händelser, anropa underorkestreringar osv.

Mildringsstrategier

Här är några av strategierna för att hantera versionsutmaningar:

  • Gör ingenting (rekommenderas inte)
  • Versionshantering för orkestrering (rekommenderas i de flesta fall)
  • Stoppa alla instanser under flygning
  • Sida vid sida driftsättningar

Gör ingenting

Den naiva metoden för versionshantering är att inte göra någonting och låta orkestreringsinstanser under flygning misslyckas. Beroende på typen av ändring kan följande typer av fel inträffa.

  • Orkestreringar kan misslyckas med ett icke-deterministiskt orkestreringsfel .
  • Orkestreringar kan fastna på obestämd tid och rapportera en Running status.
  • Om en funktion tas bort kan alla funktioner som försöker anropa den misslyckas med ett fel.
  • Om en funktion tas bort efter att den har schemalagts att köras kan appen uppleva körningsfel på låg nivå i Durable Task Framework-motorn, vilket kan leda till allvarlig prestandaförsämring.

På grund av dessa potentiella fel rekommenderas inte strategin "gör ingenting".

Versionshantering för orkestrering

Anmärkning

Orkestreringsversion är för närvarande i offentlig förhandsversion.

Med versionsfunktionen för orkestrering kan olika versioner av orkestrering samexistera och köras samtidigt utan konflikter och problem med icke-determinism, vilket gör det möjligt att distribuera uppdateringar samtidigt som orkestreringsinstanser under flygning kan slutföras utan manuella åtgärder.

Med versionshantering för orkestrering:

  • Varje orkestreringsinstans hämtar en version som är permanent associerad med den när den skapas
  • Orchestrator-funktioner kan undersöka deras version och grenkörning i enlighet med detta
  • Arbetare som kör nyare orchestrator-funktionsversioner kan fortsätta köra orkestreringsinstanser som skapats av äldre versioner
  • Körningen förhindrar att arbetare som kör äldre orchestrator-funktionsversioner kör orkestreringar av nyare versioner

Den här strategin rekommenderas för applikationer som behöver kunna stödja kritiska ändringar samtidigt som distributioner utan driftstopp bibehålls.

Detaljerad vägledning för konfiguration och implementering finns i Versionshantering av orkestrering i Durable Functions.

Stoppa alla instanser under flygning

Ett annat alternativ är att stoppa alla instanser under flygning. Om du använder Azure Storage-standardleverantören för Durable Functions, kan du stoppa alla instanser genom att rensa innehållet i de interna control-queue- och workitem-queue-köerna. Du kan också stoppa funktionsappen, ta bort dessa köer och starta om appen igen. Köerna återskapas automatiskt när appen startas om. De tidigare orkestreringsinstanserna kan förbli i tillståndet "Körs" på obestämd tid, men de kommer inte att fylla dina loggar med felmeddelanden eller orsaka några problem för din app. Den här metoden är idealisk för snabb prototyputveckling, inklusive lokal utveckling.

Anmärkning

Den här metoden kräver direkt åtkomst till de underliggande lagringsresurserna och kanske inte är lämplig för alla lagringsproviders som stöds av Durable Functions.

Sida vid sida driftsättningar

Det säkraste sättet att säkerställa att ändringar som bryter kompatibilitet distribueras tryggt och säkert är genom att distribuera dem sida vid sida med dina äldre versioner. Detta kan göras med någon av följande tekniker:

  • Distribuera alla uppdateringar som helt nya funktioner och lämna befintliga funktioner as-is. Detta rekommenderas vanligtvis inte på grund av komplexiteten i att rekursivt uppdatera anroparna för de nya funktionsversionerna.
  • Distribuera alla uppdateringar som en ny funktionsapp med ett annat lagringskonto.
  • Distribuera en ny kopia av funktionsappen med samma lagringskonto men med ett uppdaterat namn på aktivitetshubben . Detta resulterar i att nya lagringsartefakter skapas som kan användas av den nya versionen av din app. Den gamla versionen av din app fortsätter att köras med hjälp av den tidigare uppsättningen lagringsartefakter.

Distribution sida vid sida är den rekommenderade tekniken för att distribuera nya versioner av dina funktionsappar.

Anmärkning

Den här vägledningen för distributionsstrategin sida vid sida använder Azure Storage-specifika termer, men gäller vanligtvis för alla Durable Functions-lagringsproviders som stöds.

Implementeringsplatser

När du gör distributioner sida vid sida i Azure Functions eller Azure App Service rekommenderar vi att du distribuerar den nya versionen av funktionsappen till ett nytt distributionsfack. Med distributionsplatser kan du köra flera kopior av funktionsapplikationen sida vid sida med bara en av dem som den aktiva produktionsplatsen. När du är redo att exponera den nya orkestreringslogik för din befintliga infrastruktur kan det vara så enkelt som att byta den nya versionen till produktionsplatsen.

Anmärkning

Den här strategin fungerar bäst när du använder HTTP- och webhook-utlösare för orkestreringsfunktioner. För icke-HTTP-utlösare, till exempel köer eller händelsehubbar, ska utlösardefinitionen härledas från en appinställning som uppdateras som en del av växlingsåtgärden.

Nästa steg