-
Notifications
You must be signed in to change notification settings - Fork 53
Description
Hi there,
Context / issue
We came across an API change in the WaitForExternalEvent<T> in the Microsoft.DurableTask package as we had the code below in place for in-process, wait for external events for 7 days, or when the cancellationtoken has ben cancelled when the winner prevails:
Task<string> event1Waiter = context.WaitForExternalEvent<string>(Constants.Event1, TimeSpan.FromDays(7), ctsEvent1.Token);
Task<string> event2Waiter = context.WaitForExternalEvent<string>(Constants.Event2, TimeSpan.FromDays(7), ctsEvent2.Token);Now, in isolated mode, we don't have the option to cancel the token externally, and are forced to use a timer to achieve the 'same' functionality as we had in the in-process mode. The only options we have are:

Expected result
To be able to pass in the cancellationtoken next to the timespan as we could in the in-process variant, to cancel the other tasks when one task prevails as winner.
Below some code snippets to demonstrate the issue I try to describe here. Feel free to inform me to clarify or elaborate on certain parts.
Regards,
Tom
Not working:
[Function(Constants.OrchestratorName)]
public async Task<string> NotWorkingWithTimespan([OrchestrationTrigger] TaskOrchestrationContext context)
{
var input = context.GetInput<string>();
_logger.LogDebug("Started orchestrator with input: '{input}'", input);
context.SetCustomStatus("Awaiting events");
Task<string> event1Waiter = context.WaitForExternalEvent<string>(Constants.Event1, TimeSpan.FromDays(7));
Task<string> event2Waiter = context.WaitForExternalEvent<string>(Constants.Event2, TimeSpan.FromDays(7));
var winner = await Task.WhenAny(event1Waiter, event2Waiter);
if (winner == event1Waiter)
{
//Because event1Waiter is the winner, we need to cancel the event2Waiter task via a CTS
// This is not possible anymore from the current implementation...
// In the in-proc mode we cancel the token for event2 here to cancel the event2Waiter..
_logger.LogDebug("Wait for event1 winner: '{data}'", event1Waiter.Result);
return $"OrchestratorCompleted with result: '{event1Waiter.Result}'";
}
if (winner == event2Waiter)
{
//Because event2Waiter is the winner, we need to cancel the event1Waiter task via a CTS
// This is not possible anymore from the current implementations...
// In the in-proc mode we cancel the token for event1 here to cancel the event1Waiter..
_logger.LogDebug("Wait for event2 winner: '{data}'", event2Waiter.Result);
return $"Orchestrator completed with result: '{event2Waiter.Result}'";
}
return "Received timeout...";
}Not working with timespan and timertask either
[Function(Constants.OrchestratorName)]
public async Task<string> NotWorkingWithTimeSpanAndTimerEither([OrchestrationTrigger] TaskOrchestrationContext context)
{
var input = context.GetInput<string>();
_logger.LogDebug("Started orchestrator with input: '{input}'", input);
context.SetCustomStatus("Awaiting events");
using var timerCts = new CancellationTokenSource();
Task<string> event1Waiter = context.WaitForExternalEvent<string>(Constants.Event1, TimeSpan.FromDays(7));
Task<string> event2Waiter = context.WaitForExternalEvent<string>(Constants.Event2, TimeSpan.FromDays(7));
Task timerTask = context.CreateTimer(TimeSpan.FromSeconds(10), timerCts.Token);
var winner = await Task.WhenAny(event1Waiter, event2Waiter, timerTask);
if (winner == event1Waiter)
{
timerCts.Cancel();
_logger.LogDebug("Wait for event1 winner: '{data}'", event1Waiter.Result);
return $"OrchestratorCompleted with result: '{event1Waiter.Result}'";
}
if (winner == event2Waiter)
{
timerCts.Cancel();
_logger.LogDebug("Wait for event2 winner: '{data}'", event2Waiter.Result);
return $"Orchestrator completed with result: '{event2Waiter.Result}'";
}
if (winner == timerTask)
{
return "Received timeout...";
}
return "Received timeout...";
}Working with timertask:
[Function(Constants.OrchestratorName)]
public async Task<string> WorkingWithTimer([OrchestrationTrigger] TaskOrchestrationContext context)
{
var input = context.GetInput<string>();
_logger.LogDebug("Started orchestrator with input: '{input}'", input);
context.SetCustomStatus("Awaiting events");
using var timerCts = new CancellationTokenSource();
Task<string> event1Waiter = context.WaitForExternalEvent<string>(Constants.Event1);
Task<string> event2Waiter = context.WaitForExternalEvent<string>(Constants.Event2);
Task timerTask = context.CreateTimer(TimeSpan.FromSeconds(20), timerCts.Token);
var winner = await Task.WhenAny(event1Waiter, event2Waiter, timerTask);
if (winner == event1Waiter)
{
timerCts.Cancel();
_logger.LogDebug("Wait for event1 winner: '{data}'", event1Waiter.Result);
return $"OrchestratorCompleted with result: '{event1Waiter.Result}'";
}
if (winner == event2Waiter)
{
timerCts.Cancel();
_logger.LogDebug("Wait for event2 winner: '{data}'", event2Waiter.Result);
return $"Orchestrator completed with result: '{event2Waiter.Result}'";
}
if (winner == timerTask)
{
return "Received timeout...";
}
return "Exceptional situation...";
}