title | description | services | author | manager | editor | tags | keywords | ms.service | ms.devlang | ms.topic | ms.tgt_pltfrm | ms.workload | ms.date | ms.author |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
Custom orchestration status in Durable Functions - Azure |
Learn how to configure and use custom orchestration status for Durable Functions. |
functions |
kadimitr |
cfowler |
functions |
multiple |
article |
multiple |
na |
04/24/2018 |
azfuncdf |
Custom orchestration status lets you set a custom status value for your orchestrator function. This status is provided via the HTTP GetStatus API or the DurableOrchestrationClient.GetStatusAsync
API.
Clients can poll the status end point and display a progress UI that visualizes the current execution stage. The following sample demonstrates progress sharing:
[FunctionName("E1_HelloSequence")]
public static async Task<List<string>> Run(
[OrchestrationTrigger] DurableOrchestrationContextBase context)
{
var outputs = new List<string>();
outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Tokyo"));
context.SetCustomStatus("Tokyo");
outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "Seattle"));
context.SetCustomStatus("Seattle");
outputs.Add(await context.CallActivityAsync<string>("E1_SayHello", "London"));
context.SetCustomStatus("London");
// returns ["Hello Tokyo!", "Hello Seattle!", "Hello London!"]
return outputs;
}
[FunctionName("E1_SayHello")]
public static string SayHello([ActivityTrigger] string name)
{
return $"Hello {name}!";
}
And then the client will receive the output of the orchestration only when CustomStatus
field is set to "London":
[FunctionName("HttpStart")]
public static async Task<HttpResponseMessage> Run(
[HttpTrigger(AuthorizationLevel.Function, methods: "post", Route = "orchestrators/{functionName}")] HttpRequestMessage req,
[OrchestrationClient] DurableOrchestrationClientBase starter,
string functionName,
ILogger log)
{
// Function input comes from the request content.
dynamic eventData = await req.Content.ReadAsAsync<object>();
string instanceId = await starter.StartNewAsync(functionName, eventData);
log.LogInformation($"Started orchestration with ID = '{instanceId}'.");
DurableOrchestrationStatus durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
while (durableOrchestrationStatus.CustomStatus.ToString() != "London")
{
await Task.Delay(200);
durableOrchestrationStatus = await starter.GetStatusAsync(instanceId);
}
HttpResponseMessage httpResponseMessage = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new StringContent(JsonConvert.SerializeObject(durableOrchestrationStatus))
};
return httpResponseMessage;
}
}
Another interesting scenario is segmenting users by returning customized output based on unique characteristics or interactions. With the help of custom orchestration status, the client-side code will stay generic. All main modifications will happen on the server side as shown in the following sample:
[FunctionName("CityRecommender")]
public static void Run(
[OrchestrationTrigger] DurableOrchestrationContextBase context)
{
int userChoice = context.GetInput<int>();
switch (userChoice)
{
case 1:
context.SetCustomStatus(new
{
recommendedCities = new[] {"Tokyo", "Seattle"},
recommendedSeasons = new[] {"Spring", "Summer"}
});
break;
case 2:
context.SetCustomStatus(new
{
recommendedCity = new[] {"Seattle, London"},
recommendedSeasons = new[] {"Summer"}
});
break;
case 3:
context.SetCustomStatus(new
{
recommendedCity = new[] {"Tokyo, London"},
recommendedSeasons = new[] {"Spring", "Summer"}
});
break;
}
// Wait for user selection and refine the recommendation
}
The orchestrator can provide unique instructions to the clients via the custom state. The custom status instructions will be mapped to the steps in the orchestration code:
[FunctionName("ReserveTicket")]
public static async Task<bool> Run(
[OrchestrationTrigger] DurableOrchestrationContextBase context)
{
string userId = context.GetInput<string>();
int discount = await context.CallActivityAsync<int>("CalculateDiscount", userId);
context.SetCustomStatus(new
{
discount = discount,
discountTimeout = 60,
bookingUrl = "https://www.myawesomebookingweb.com",
});
bool isBookingConfirmed = await context.WaitForExternalEvent<bool>("BookingConfirmed");
context.SetCustomStatus(isBookingConfirmed
? new {message = "Thank you for confirming your booking."}
: new {message = "The booking was not confirmed on time. Please try again."});
return isBookingConfirmed;
}
In the following sample, the custom status is set first;
public static async Task SetStatusTest([OrchestrationTrigger] DurableOrchestrationContext ctx)
{
// ...do work...
// update the status of the orchestration with some arbitrary data
var customStatus = new { nextActions = new [] {"A", "B", "C"}, foo = 2, };
ctx.SetCustomStatus(customStatus);
// ...do more work...
}
While the orchestration is running, external clients can fetch this custom status:
GET /admin/extensions/DurableTaskExtension/instances/instance123
Clients will get the following response:
{
"runtimeStatus": "Running",
"input": null,
"customStatus": { "nextActions": ["A", "B", "C"], "foo": 2 },
"output": null,
"createdTime": "2017-10-06T18:30:24Z",
"lastUpdatedTime": "2017-10-06T19:40:30Z"
}
Warning
The custom status payload is limited to 16 KB of UTF-16 JSON text because it needs to be able to fit in an Azure Table Storage column. Developers can use external storage if they need larger payload.
[!div class="nextstepaction"] Learn about HTTP APIs in Durable Functions