Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
Fazer01 opened this issue Mar 26, 2024 · 0 comments
Labels

Comments

@Fazer01
Copy link

Fazer01 commented Mar 26, 2024

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...";
 }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants