Skip to content

WaitForExternalEvent<T>(eventName, timespan, cancellationToken) not supported (anymore?) in isolated mode #277

@Fazer01

Description

@Fazer01

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:
image

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...";
 }

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions