We gaan verder waar we gebleven waren in het laatste deel: we hadden App Insights toegevoegd en we kunnen nu heel mooi zien wat er allemaal gebeurt. En alles is mooi en goed in onze wereld. Tot het onvermijdelijke gebeurt…. En dat is vaak een van de volgende dingen:
- Er is een bug gevonden in onze code
- De requirements veranderen
Wij doen niets fout, dus punt 1 is niet zo waarschijnlijk, niet waar? Nou ja, ok, ook wij maken fouten dus het kan gebeuren. Punt 2 komt uiteraard ook heel vaak voor. In ieder geval, we moeten een change doorvoeren…
API Versioning
We kunnen een aantal soorten changes herkennen in Durable Functions App.
- Changes in Activities die geen andere signature krijgen in de functions (dus bool ValidateInput(string email) blijft bool ValidateInput(string email)
- Changes in Activities die een andere signature veroorzaken (dus bool ValidateInput(string email) wordt ValidationResult ValideInput(Attendee attendee)
- Changes in de Orchestration.
Mogelijkheid 1 is het meest makkelijk: maak je changes, doe alle testen, en deploy de code (of eigenlijk: check in de code in master op je repository en zorg er voor dat je pipelines de deployment regelen) (nu ik de vorige zin lees, vraag ik me af hoeveel van mijn niet-IT vrienden überhaupt snappen wat ik in die zin zeg…)
Veranderingen in de orchestration code echter zijn wat lastiger. Dit kan zijn dat er een verandering in de orchestration zelf komt of dat de activities een andere signature krijgen. Dat laatste houdt natuurlijk in dat de resultaten in de TableStorage er opeens anders uit gaan zien.
Op zich is het allemaal geen probleem: we kunnen deployen waar en wanneer we maar willen. Maar wat als er op het moment van deployen net een workflow loopt? Wat gebeurt er dan? Stel je de volgende flow voor:

Deze flow kennen we. Ik heb hem even versimpeld: de resultaten van Validate Input en Notify Board zijn weggelaten. Maar dit is de basic flow zoals we die nu de hele tijd gebruiken.
We hebben dit op Azure staan en iemand meldt zich aan op de website. De flow begint te draaien. De input wordt gevalideerd, deze wordt goed bevonden. De orchestration begint opnieuw, haalt het resultaat op van de ValidateInput en gaat verder. De 4 usergroups worden geinformeerd. Ook hier weer begint daarna de flow opnieuw, de resultaten worden opgehaald en we gaan naar stap 3: Notify Board. Maar.. vlak voordat de flow opnieuw begint is er een nieuwe versie van NotifyGroups uitgerold. Dit keer doen we niet alleen een notificatie, maar krijgen we feedback terug vanuit de API’s van de andere groepen. Het resultaat is een OK of een ToManyNoShows als iemand te vaak niet komt zonder af te melden. Dat willen we graag weten: we kopen immers eten en drinken voor die persoon en als die niet komt dan hebben we een probleem. Die info willen we graag meegeven aan NotifyBoard.
Maar ja, de flow loopt al. De huidige attendee is door de NotifyGroups heen gekomen zonder dat daar een advies bij kwam kijken. Dus we hebben geen waardes om mee te geven aan Notify Board. Wat moeten we nu doen?
Er zijn een paar oplossingen.
- Doe een deployment. Alle lopende workflows krijgen een error en worden afgebroken. Niet zo heel netjes maar in development doen we niet anders.
- Geef de nieuwe versie een andere orchestration naam en roep die aan in plaats van de oude. De lopende flows zullen onder de oude orchestration door gaan, de nieuwe gaan naar de nieuwe flow toe. Op zich een goede oplossing, maar je krijgt wel veel verschillende Function Apps in Azure die eigenlijk niet meer ter zake doen. Een redelijke oplossing, maar niet zo mooi als:
- Maak een nieuwe hub aan. De huidige flows blijven lopen tot ze klaar zijn. De nieuwe requests gaan via de nieuwe flow. De oude dll’s worden gecached tot ze niet meer nodig zijn en gaan dan weg. De oude data blijft bewaard, apart van de nieuwe.
Die laatste optie klinkt als de beste optie. Dus laten we dat gaan doen.
En dat is veel eenvoudiger dan je zou verwachten.
In onze solution hebben we een file genaamd host.json. Deze is redelijk leeg:
{
"version": "2.0"
}
Verander deze tot hij er zo uitziet:
{
"version": "2.0",
"extensions": {
"durableTask": {
"hubName": "meetingReg"
}
}
}
De hub is de naam van de flow waarin we draaien. Dat is standaard DurableFunctionsHub. Die naam zien we dan ook steeds terugkomen in de Storage Explorer:

Als we echter de host.json aanpassen zoals hij nu hierboven staat (dus met als hubnaam meetingReg) en we draaien de flow nog een keer, zien we dit:

Je ziet dat er een paar tabellen bijgekomen zijn. Hetzelfde geldt voor de queues: alle queues die we hadden zijn er nog maar er 5 bijgekomen: 4 control queues en een workitems queue, maar nu met de prefix meetingReg.
Als we dit zo zouden deployen naar Azure, zullen de lopende flows blijven lopen op een gecachte versie van de assembly en de data staat in de ‘oude’ tabellen en queues. Met andere woorden: de lopende flows blijven doorlopen. Niets aan de hand.
Alle nieuwe requests echter gaan naar de flow met de nieuwe hub naam. Die krijgen de nieuwe assemblies en hun data wordt opgeslagen in de nieuwe tabellen en queues.
Het enige wat je om de zoveel tijd zou kunnen doen, is het opruimen van de oude data. Uiteraard is het voor veel bedrijven belangrijk (of zelfs verplicht) om data te bewaren dus dan moet je de tabellen niet weggooien. Maar je hebt in ieder geval de keuze.
Dit is een heel verhaal geworden, maar in feite komt het neer op het volgende:
Als je iets verandert in een orchestration function (volgorde of de manier van aanroepen van de activities), rol dan een nieuwe versie uit met een andere hub naam in de host.json.
That’s it.
Security
Wellicht het belangrijkste onderwerp van deze hele reeks is de veiligheid. En daar ga ik het allerminst over zeggen. Maar het is wel de afsluiting van deze reeks.
Durable Functions zijn secure by default. Alle data staat in een Storage Account die je, als het goed is, niet van buiten af kunt benaderen. De orchestration en activity functions kunnen alleen aangeroepen worden vanuit de Azure omgeving. Niemand anders kan daar bij. De enige functions die dat kunnen doen zijn de standaard Azure Functions, zoals onze StartRegistration, ApproveAttendee (vanuit de email) en StartDatabaseCleanup (die de eternal workflow opstart).
Deze functions moet je wel beveiligen. Zoals we gezien hebben, kun je deze op 3 manieren dichtkrijgen.
- Geen beveiliging, anonymous access is toegestaan
- Met key op Function App niveau (admin type security): alles binnen de function app werkt met dezelfde key
- Met key op function niveau: iedere function krijgt een eigen key.
Die key wordt voor je gegenereerd maar die kun je in de Azure Portal weggooien en nieuwe genereren. Ook kun je meerdere keys genereren voor dezelfde functie. Op die manier kun je iedere gebruiker een eigen key geven (en dus per gebruiker, klant of groep ook weer revoken).
In je source code heb je al aangegeven dat je de functie op functie niveau wilt beveiligen:
[FunctionName("StartRegistration")]
public static async Task<HttpResponseMessage> StartRegistration(
[HttpTrigger(
AuthorizationLevel.Function,
"get",
Route = "dotNed/register/{name}/{email}/{wantsTweet}")]
HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClient client,
string name,
string email,
bool wantsTweet,
ILogger log)
{
Je ziet op regel 4 dat we daar zeggen dat we de authorization level op Function willen.
In de Azure Portal ga je naar je Function App en vind je function daar. In de Manage tab van die functie vind je dan de keys en alle beheermogelijkheden daarvan:

Hier kun je keys maken, renewen en revoken.
Meer security
Is dit genoeg om je hele systeem te beveiligen?
Nee.
Maar het is een begin. Om de boel echt goed op slot te krijgen kunnen we beter gebruik maken van API Management. Daarmee kunnen we versie beheer doen van onze publieke functies, we kunnen revisions aanbrengen, we kunnen rechten toekennen op niveau van Active Directory en nog veel meer.
Sterker nog: dat is dusdanig veel meer dat ik daar een nieuwe serie over ga schrijven. Dus die hou je nog even tegoed.
Afsluitend
En dat is eigenlijk alles wat ik tot op dit moment wil en/of kan delen over Azure Durable Functions. Er komt een nieuwe versie aan met een hele hoop breaking changes maar die is nog niet beschikbaar. Althans: niet in een stabiele versie. Als die uitkomt zal ik daar uiteraard een hele hoop aandacht aan besteden. Een van de dingen die daar in komen is bijvoorbeeld Durable Entities. Met andere woorden: we hebben zo meteen een instance van onze Attendee class en die ‘zweeft’ ergens rond in Azure. Die kunnen we aanroepen en dingen mee laten doen. Maar die is er gewoon…
We hebben een hoop bekeken en een hoop gedaan. Ik hoop dat je er wat van geleerd hebt en dat je er je voordeel mee kunt doen. Maar vooral hoop ik dat je Durable Functions gaat toepassen in je dagelijkse werk.
Veel plezier!
Erg interessant en leuk geschreven! Ik kijk uit naar de blogs over API management.
LikeLike