Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Het is onvermijdelijk dat functies worden toegevoegd, verwijderd en gewijzigd gedurende de levensduur van een toepassing. Durable Functions maakt het mogelijk om functies te koppelen op manieren die eerder niet mogelijk waren en deze keten beïnvloedt hoe u versiebeheer kunt afhandelen.
Typen brekende wijzigingen
Er zijn verschillende voorbeelden van belangrijke wijzigingen waar u rekening mee moet houden. In dit artikel worden de meest voorkomende besproken. Het belangrijkste thema achter dit alles is dat zowel nieuwe als bestaande functieorkestraties worden beïnvloed door wijzigingen in functie-code.
Handtekeningen voor activiteits- of entiteitsfuncties wijzigen
Een handtekeningwijziging verwijst naar een wijziging in de naam, invoer of uitvoer van een functie. Als dit type wijziging wordt aangebracht in een activiteit of entiteitsfunctie, kan deze elke orchestratorfunctie verbreken die ervan afhankelijk is. Dit geldt met name voor type-veilige talen. Als u de orchestrator-functie bijwerkt om deze wijziging aan te passen, kunt u bestaande exemplaren in flight breken.
Stel dat we de volgende orchestratorfunctie hebben.
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Deze simplistische functie neemt de resultaten van Foo en geeft deze door aan Bar. Stel dat we de retourwaarde van Foo moeten wijzigen van een Booleaanse waarde in een tekenreeks om een grotere verscheidenheid aan resultaatwaarden te ondersteunen. Het resultaat ziet er als volgt uit:
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
string result = await context.CallActivityAsync<string>("Foo");
await context.CallActivityAsync("Bar", result);
}
Deze wijziging werkt prima voor alle nieuwe exemplaren van de orchestratorfunctie, maar kan eventuele in-flight-exemplaren breken. Denk bijvoorbeeld aan de situatie waarin een orchestration-exemplaar een functie aanroept met de naam Foo
, een Booleaanse waarde terugkrijgt en vervolgens controlepunten weergeeft. Als de wijziging van de handtekening op dit moment wordt geïmplementeerd, mislukt het controlepuntexemplaar onmiddellijk wanneer deze wordt hervat en wordt de aanroep naar Foo
opnieuw afgespeeld. Deze fout treedt op omdat het resultaat in de geschiedenistabel een Booleaanse waarde is, maar de nieuwe code deze probeert te deserialiseren in een tekenreekswaarde, wat resulteert in onverwacht gedrag of zelfs runtime-uitzondering voor typeveilige talen.
Dit voorbeeld is slechts een van de vele verschillende manieren waarop een wijziging van een functie-signatuur bestaande exemplaren kan breken. Als een orchestrator in het algemeen de manier moet wijzigen waarop een functie wordt aangeroepen, is de wijziging waarschijnlijk problematisch.
Orchestratorlogica wijzigen
De andere klasse versiebeheerproblemen zijn afkomstig van het wijzigen van de orchestrator-functiecode op een manier die het uitvoeringspad voor in-flight-exemplaren wijzigt.
Houd rekening met de volgende orchestratorfunctie:
[FunctionName("FooBar")]
public static Task Run([OrchestrationTrigger] IDurableOrchestrationContext context)
{
bool result = await context.CallActivityAsync<bool>("Foo");
await context.CallActivityAsync("Bar", result);
}
Stel nu dat u een wijziging wilt aanbrengen om een nieuwe functie-aanroep toe te voegen tussen de twee bestaande functie-aanroepen.
[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);
}
Met deze wijziging wordt een nieuwe functie-aanroep toegevoegd aan SendNotification tussen Foo en Bar. Er zijn geen handtekeningwijzigingen. Het probleem treedt op wanneer een bestaand exemplaar hervat wordt nadat een oproep is gedaan naar Bar. Als tijdens het opnieuw afspelen de oorspronkelijke aanroep van Foo terugkeert met true
, dan roept de orchestrator-replay SendNotification aan, die zich niet in de uitvoeringsgeschiedenis bevindt. De runtime detecteert deze inconsistentie en genereert een niet-deterministische indelingsfout omdat er een aanroep naar SendNotification is gedaan wanneer er een aanroep naar Bar werd verwacht. Hetzelfde type probleem kan optreden bij het toevoegen van API-aanroepen aan andere duurzame bewerkingen, zoals het maken van duurzame timers, het wachten op externe gebeurtenissen, het aanroepen van subindelingen, enzovoort.
Risicobeperkingsstrategieën
Hier volgen enkele strategieën voor het oplossen van problemen met versiebeheer:
- Niets doen (niet aanbevolen)
- Orkestratieversiebeheer (aanbevolen in de meeste gevallen)
- Alle in-flight-instances stoppen
- Implementaties naast elkaar
Niets doen
De naïeve benadering van versiebeheer is om niets te doen en lopende orkestratie-instanties te laten falen. Afhankelijk van het type wijziging kunnen de volgende typen fouten optreden.
- Orkestraties kunnen mislukken met een niet-deterministische orkestratiefout.
- Orchestraties kunnen voor onbepaalde tijd vastlopen en een
Running
status weergeven. - Als een functie wordt verwijderd, kan een functie die deze probeert aan te roepen mislukken met een fout.
- Als een functie wordt verwijderd nadat deze is gepland om te worden uitgevoerd, kan de app runtimefouten op laag niveau ondervinden in de Durable Task Framework-engine, wat kan leiden tot ernstige prestatievermindering.
Vanwege deze mogelijke fouten wordt de strategie 'niets doen' niet aanbevolen.
Orchestratieversiebeheer
Opmerking
Orchestratieversiebeheer is momenteel in openbare preview.
Met de functie voor orkestratieversies kunnen verschillende versies van orkestraties gelijktijdig worden uitgevoerd zonder conflicten en problemen met niet-determinisme, zodat updates kunnen worden geïmplementeerd terwijl in-flight orkestratie-instanties zonder handmatige tussenkomst kunnen worden voltooid.
Met orchestratie-versiebeheer:
- Elke orkestratie-instantie krijgt een versie die er permanent aan is gekoppeld bij het maken ervan.
- Orchestrator-functies kunnen hun versie evalueren en dienovereenkomstig de uitvoering vertakken
- Werknemers die nieuwere versies van de orchestratorfunctie uitvoeren, kunnen doorgaan met het uitvoeren van orkestratie-instanties die zijn gemaakt door oudere versies.
- De runtime voorkomt dat werknemers met oudere orchestratorfunctieversies orchestrations van nieuwere versies uitvoeren
Deze strategie wordt aanbevolen voor toepassingen die belangrijke wijzigingen moeten ondersteunen, terwijl implementaties zonder downtime behouden blijven. De functie is momenteel beschikbaar voor .NET-geïsoleerde apps.
Zie Orchestration-versiebeheer in Durable Functions voor gedetailleerde configuratie- en implementatierichtlijnen.
Alle in-flight-instances stoppen
Een andere optie is om alle in-flight exemplaren te stoppen. Als u de standaard Azure Storage-provider voor Durable Functions gebruikt, kunt u alle instanties stoppen door de inhoud van de interne control-queue en workitem-queue te wissen. U kunt de functie-app ook stoppen, deze wachtrijen verwijderen en de app opnieuw starten. De wachtrijen worden automatisch opnieuw gemaakt zodra de app opnieuw wordt opgestart. De vorige orkestratie-instanties kunnen voor onbepaalde tijd de status "In uitvoering" behouden, maar ze zullen uw logboeken niet vullen met foutberichten of schade aan uw app toebrengen. Deze aanpak is ideaal bij snelle prototypeontwikkeling, waaronder lokale ontwikkeling.
Opmerking
Deze aanpak vereist directe toegang tot de onderliggende opslagresources en is mogelijk niet geschikt voor alle opslagproviders die worden ondersteund door Durable Functions.
Implementaties naast elkaar
De meest fail-proof manier om ervoor te zorgen dat belangrijke wijzigingen veilig worden geïmplementeerd, is door ze naast uw oudere versies te implementeren. U kunt dit doen met behulp van een van de volgende technieken:
- Implementeer alle updates als volledig nieuwe functies en laat bestaande functies as-isongewijzigd. Dit wordt over het algemeen niet aanbevolen vanwege de complexiteit die gepaard gaat met het recursief bijwerken van de aanroepen naar de nieuwe versies van de functies.
- Implementeer alle updates als een nieuwe functie-app met een ander opslagaccount.
- Implementeer een nieuwe kopie van de functie-app met hetzelfde opslagaccount, maar met een bijgewerkte taakhubnaam . Dit resulteert in het maken van nieuwe opslagartefacten die kunnen worden gebruikt door de nieuwe versie van uw app. De oude versie van uw app blijft worden uitgevoerd met behulp van de vorige set opslagartefacten.
Naast elkaar implementeren is de aanbevolen techniek voor het implementeren van nieuwe versies van uw functie-apps.
Opmerking
Deze richtlijnen voor de implementatiestrategie naast elkaar maken gebruik van azure Storage-specifieke termen, maar zijn over het algemeen van toepassing op alle ondersteunde Durable Functions-opslagproviders.
Implementatiesites
Wanneer u naast elkaar implementaties in Azure Functions of Azure App Service uitvoert, wordt u aangeraden de nieuwe versie van de functie-app te implementeren in een nieuwe implementatiesite. Met implementatie-slots kunt u meerdere kopieën van uw functie-app naast elkaar uitvoeren, waarbij slechts één als de actieve productieslot fungeert. Wanneer u klaar bent om de nieuwe orkestratielogica beschikbaar te maken voor uw bestaande infrastructuur, kan het net zo eenvoudig zijn als het uitrollen van de nieuwe versie in de productieslot.
Opmerking
Deze strategie werkt het beste wanneer u HTTP- en webhooktriggers gebruikt voor orchestratorfuncties. Voor niet-HTTP-triggers, zoals wachtrijen of Event Hubs, moet de triggerdefinitie worden afgeleid van een app-instelling die wordt bijgewerkt als onderdeel van de wisselbewerking.