Cannot use CancellationToken to end orchestrator function - .net-core

I am using an orchestration to await two sub-orchestrations which are executing in parallel or a timeout of 5min, whatever comes first. I am passing a CancellationToken to the sub-orchestrators and therein throw on cancellation using token.ThrowIfCancellationRequested(). Also, since I have created a timer in the main orchestrator function, I am responsible to clear it in the case the timeout is not triggered itself. This is documented here.
main orchestrator:
public class Fxbm_workflow
{
[FunctionName(nameof(Fxbm_workflow))]
public async Task Run([OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log)
{
log = ctx.CreateReplaySafeLogger(log);
var parentWorkflowId = ctx.InstanceId;
var trigger = ctx.GetInput<Trigger<OrchestrationInput2>>();
using var cts = new CancellationTokenSource();
var expireIn = ctx.CurrentUtcDateTime.AddMinutes(5);
var responseTasks = new [] {327, 41}.Select(id => {
var input = (id, cts.Token);
var childWorkflowId = $"{parentWorkflowId}_{nameof(Fxbm_receive_response_workflow)}_{id}";
return ctx.CallSubOrchestratorAsync<object>(nameof(Fxbm_receive_response_workflow), instanceId: childWorkflowId, input: input);
});
var timeoutTask = ctx.CreateTimer(expireIn, cts.Token);
// await tasks, choose winner
var winner = await Task.WhenAny(Task.WhenAll(responseTasks), timeoutTask);
if (winner == timeoutTask)
{
log.LogWarning("winner= timeout");
// no need to cts.Cancel() here
}
else
{
log.LogWarning("winner= all tasks have finished before the timeout");
cts.Cancel();
}
}
}
sub-orchestrator function:
public class Fxbm_receive_response_workflow
{
[FunctionName(nameof(Fxbm_receive_response_workflow))]
public async Task<object> Run([OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log)
{
log = ctx.CreateReplaySafeLogger(log);
var (clientId, token) = ctx.GetInput<(int, CancellationToken)>();
token.ThrowIfCancellationRequested();
log.LogWarning($"waiting for response of clientId= {clientId}");
var evnt = await ctx.WaitForExternalEvent<object>(name: clientId.ToString());
log.LogWarning($"response received for clientId= {clientId}");
await ctx.CallActivityAsync(nameof(Fxbm_notifyOfNewReport), evnt);
return evnt;
}
}
However, this doesn't work. In the screenshot below there's the execution history extracted using DfMon. One of the two sub-orchestrations completes successfully. Before the other one can finish, the 5min timeout is triggered. Still, neither does the remaining sub-orchestration fail (due to token.ThrowIfCancellationRequested()), nor does the parent orchestration complete, inspite of calling cts.Cancel(). Both are still in a Running state.
It is my understanding the sub-orchestration should have failed/thrown and the parent orchestration should have concluded successfully. Does someone know why this is not the case here? Since orchestrator functions are re-executed again and again until finished, I do worry about unncessary $$$ cost of this when deployed in Azure.

Related

Waiting for multiple external events in Durable Azure Functions and act on them as they occur

I'm using Durable Functions in a process to collect data from 12 different people. For this I chose the WaitForExternalEvent method. I need to get notified of those external events as they happen. All events must be received in the next 1h.
I have created the following orchestration. The behaviour is odd however. The orchestration neither completes, nor fails. I am using Durable Functions Monitor (dfMon) to inspect the logs. As you can see in the execution history all 12 events are in fact received (before the 1h timeout). Still the orchestrator:
didn't execute the Fxbm_notifyOfNewReport activity function after each received event
also didn't exit the while loop after all 12 events
Also, more than 1h has elapsed and every timeout timer has fired. Still, no exception was thrown. The orchestration is still in a running state.
I took inspiration for this from this doc and this blog.
Does someone know why I am not seeing the expected behaviour?
public class Fxbm_workflow
{
[FunctionName(nameof(Fxbm_workflow))]
public async Task Run([OrchestrationTrigger] IDurableOrchestrationContext ctx, ILogger log)
{
var id = ctx.InstanceId;
var trigger = ctx.GetInput<Trigger<OrchestrationInput2>>();
// sub-orchestration to distribute data to people
// return value is int[], these are the ids of the people
var input = (trigger, id);
var childWorkflowId = $"{id}_{nameof(Fxbm_prep_workflow)}";
var requiredCompanies = await ctx.CallSubOrchestratorAsync<int[]>(nameof(Fxbm_prep_workflow), instanceId: childWorkflowId, input: input);
// to every distributed data package, a string response is expected in 1h at the latest
var expiresIn = TimeSpan.FromHours(1);
var responseTasks = requiredCompanies.Select(id => ctx.WaitForExternalEvent<string>(id.ToString(), expiresIn)).ToList();
// all tasks need to complete or a timeout exception must be thrown
// I want to get notified of responses as they come in
// therefore Task.WhenAll() is not suitable
while (responseTasks.Any())
{
try
{
// responses as they occur
var receivedResponse = await Task.WhenAny(responseTasks);
responseTasks.Remove(receivedResponse);
var stringResponse = await receivedResponse;
// notify via mail
await ctx.CallActivityAsync(nameof(Fxbm_notifyOfNewReport), stringResponse);
}
catch (TimeoutException)
{
// break;
throw;
}
}
}
}

Implement Redis Cache in place of In Memory Cache?

I have a InMemory Cache method which I want to implement using Redis Cache, it uses the concept of Semaphore in the InMemory Cache, hence I have gone through many documentation's like the Redis documentation on Distributed Locking, and tried using different locking mechanisms in Redis such as RedLock.Net , RedLock-cs etc, but I haven't been able to completely go through with the technique, as even though I am able to recreate the method, but the running time for large number of processes for it in Redis is coming out to be huge.
The code that I am trying to recreate is as follows:
private readonly ConcurrentDictionary<string, Lazy<SemaphoreSlim>> _semaphores = new ConcurrentDictionary<string, Lazy<SemaphoreSlim>>();
public async ValueTask<TValue> GetMethod<TValue>(string key,Func<Task<TValue>> get,int time)
{
return await SemaphoreMethod(key, async() =>
{
var value = await get();
if(value != null)
cache.set(key,value,time);
return value;
});
}
private async ValueTask<TValue> SemaphoreMethod<TValue>(string key,Func<Task<TValue>> get)
{
if(cache.TryGetValue(key,out TValue result))
return result;
var semaphore = _semaphores.GetOrAdd(key,k => new Lazy<SemaphoreSlim>(() => new SemaphoreSlim(1,1))).Value;
await semaphore.WaitAsync();
TValue answer = default;
if(cache.TryGetValue(key,out TValue result2))
return result2;
answer = await get();
_semaphores.TryRemove(key,out _);
semaphore.Release();
return answer;
}
And the method which I run against it to check it's implementation and efficiency in comparison to InMemory Cache is as follows:
public async Task GetValueAsync_Tasks()
{
var callCount = 0;
async Task<string> Get()
{
Interlocked.Increment(ref callCount);
return "value";
}
const int count = 1000;
var valueTasks = new List<ValueTask<string>>(count);
for(var i=0;i<count;i++)
valueTask.Add(GetMethod("key",Get));
var tasks = valueTasks.Select(vt => vt.AsTask()).ToArray();
Task.WaitAll(tasks);
callCount.Should().Be(1);
}
One other thing that I am not able to understand that in which part of the test method, would the code run all the tasks created, i.e. in this one
valueTasks.Add(sut.GetValueAsync("key", FetchValue));
or in this one
Task.WaitAll(tasks);
I am using the StackExchange.Redis library, and Azure Cache for Redis for storing the respective data.

Dart Future functions with callbacks on requests

I have an issue with understanding Future functions in dart and async programming. The examples I've found in web do not answer my question, so I would be grateful for community help.
I have a function that asks for players messages list:
Future getMessagesInfo(response) async{
dialogs = []; // I need this list to be filled to proceed
messagesList = response["data"];
await getDialogsFunc(messagesList);
renderMessages();
}
It is a callback for a previous request, which doesn't really matters here.
So, here is getDialogsFunc, which purpose is to runs through the list and ask playerInfo for each ID in the response:
Future getDialogsFunc(messagesList) async{
for(var i=0;i<messagesList.length;i++){
if(messagesList[i]["player2"] != player.ID) await rm.getDialogInfo(messagesList[i]["player2"]);
if(messagesList[i]["player1"] != player.ID) await rm.getDialogInfo(messagesList[i]["player1"]);
}
}
Here is getDialogInfo, which actually sends a request for playerInfo and has a callback function that handles received info:
Future getDialogInfo(int id) async{
messages = querySelector("account-messages-tab");
var request = new Request();
Object data = {"id": ServerAPI.PLAYER_INFO, "data":{"id": id}};
await request.sendRequest(data,false, messages.onGotDialogInfo);
}
The request is a simple class, that handles the requests:
class Request{
Future sendRequest(Object data, bool action,Function callback) async{
HttpRequest request = new HttpRequest();
String url = "http://example.com";
await request
..open('POST', url)
..onLoadEnd.listen((e)=> callback(JSON.decode(request.responseText)))
..send(JSON.encode(data));
}
}
And finally here is a callback for the request:
Future onGotDialogInfo(response) async{
List dialog = new List();
dialog.add(response["data"]["id"]);
dialog.add(response["data"]["login"]);
dialogs.add(dialog);
}
In the first function I wanted to run renderMessages() after I have received information about all messages, so that dialogs List should contain relevant information. In my realisation which I tested with breakpoints the renderMessages() functions runs BEFORE onGotDialogInfo callback.
What should I do to wait for the whole cycle getDialogsFunc functions and only then go to renderMessages()?
whenComplete is like finally, it's called no matter whether the request returned normally or with an error. Normally then is used.
getDialogsFunc uses async but doesn't use await which is a bit uncommon. This might be your intention, but I'm not sure
Future getDialogsFunc(messagesList) async {
for(var i=0;i<messagesList.length;i++){
if(messagesList[i]["player2"] != player.ID)
await rm.getDialogInfo(messagesList[i]["player2"]);
if(messagesList[i]["player1"] != player.ID)
await rm.getDialogInfo(messagesList[i]["player1"]);
}
}
getMessagesInfo could then look like:
void getMessagesInfo(response) await {
dialogs = []; // I need this list to be filled to proceed
messagesList = response["data"];
await getDialogsFunc(messagesList)
renderMessages(messagesList);
}
I don't know where Request comes from, therefore hard to tell how that code should look like. It should at least use await for other awaits to work as expected.
Future getDialogInfo(int id) async {
messages = querySelector("account-messages-tab");
var request = new Request();
Object data = {"id": ServerAPI.PLAYER_INFO, "data":{"id": id}};
final response = await request.sendRequest(data,false);
messages.onGotDialogInfo(response)
}
update
class Request{
Future sendRequest(Object data, bool action) async{
Completer completer = new Completer();
HttpRequest request = new HttpRequest();
String url = "http://example.com";
await request
..open('POST', url)
..onLoadEnd.listen((_) {
if (request.readyState == HttpRequest.DONE) {
if (request.status == 200) {
// data saved OK.
completer.complete(JSON.decode(request.responseText)); // output the response from the server
} else {
completer.completeError(request.status);
}
}
})
..send(JSON.encode(data));
return completer.future;
}
}

How I can safely run async code, when handling messages from Messenger?

I am using MVVM, inparticular MVVMLight. For boradcasting to all of my modelviews, that no internet connection is available I am using Messenger class. The modelviews subscribe to this event in order to reload itself with offline data, inform user etc.
However, I have a problem. When I have the folowing handler:
private void HandleNoInternetMessage(NoInternetAccessMessage obj)
{
Task.Run(async () => await InitializeForOfflineInternalAsync());
}
public async Task InitializeForOfflineInternalAsync()
{
try
{
WaitingLayerViewModel.ShouldBeVisible = true;
WaitingLayerViewModel.IsBusy = true; //<--exception HRESULT: 0x8001010E (RPC_E_WRONG_THREAD)
bool switchToOffline = await CommonViewModelProvider.InformUserOfNoInternetAccessAndChangeAppState(); //<!- CoreWindow.GetForCurrentThread().Dispatcher is null
await FilterTestItemViewModel.InitializeForOfflineAsync();
await FilterTestItemViewModel.InitializeForOfflineAsync();
WaitingLayerViewModel.ShouldBeVisible = false;
WaitingLayerViewModel.IsBusy = false;
...
}
}
I got exception HRESULT: 0x8001010E (RPC_E_WRONG_THREAD), because in InitializeForOfflineInternalAsync I am changing some properties of the viewmodel wchich are bound in XAML (or at least I think it is because of that). However, it is weird, because I am changing in other code bound properties regularly and have no problems with it (and the thread is a working thread).
Now, how can i solve that?
The messanger let me provide only delegate which is not async (which make kind of sense), so I can not have the HandleNoInternetMessage method async
I am using async await ... no explicit spawning of threads
I dont have access in VM to Dispatcher, because I am in VM which should not know about platform dependent stuff. And when I tried to use it to show a message, NullPointer excpetion was thrown when calling CoreWindow.GetForCurrentThread().Dispatcher; And again when calling from other places, no such exception was thrown
I guess the question is How I can safely run async code, which changes boudn properties, when handling messages from Messenger?
You're responding to messages that are logically events, so this is an acceptable use case for async void.
private async void HandleNoInternetMessage(NoInternetAccessMessage obj)
{
await InitializeForOfflineInternalAsync();
}
public async Task InitializeForOfflineInternalAsync()
{
try
{
WaitingLayerViewModel.ShouldBeVisible = true;
WaitingLayerViewModel.IsBusy = true;
bool switchToOffline = await CommonViewModelProvider.InformUserOfNoInternetAccessAndChangeAppState();
await FilterTestItemViewModel.InitializeForOfflineAsync();
await FilterTestItemViewModel.InitializeForOfflineAsync();
WaitingLayerViewModel.ShouldBeVisible = false;
WaitingLayerViewModel.IsBusy = false;
...
}
}
Remember that Task.Run is for CPU-bound code (as I describe on my blog).

Preserve HttpContext when going async with WebAPI (Medium Trust)

I am building a set of ASP.Net hosted WebAPI services that must use an old library which depends heavily on HttpContext.Current. I am having trouble ensuring that context is preserved in all the methods that participate in an async call. I have tried several variations with await/Task.Wait and TaskScheduler.FromCurrentSynchronizationContext() on the below code.
[HttpGet]
public Task<IEnumerable<string>> ContinueWith()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); //or another culture that is not the default on your machine
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var output = new List<string> { TestOutput("Action start") };
var task = Task.Factory.StartNew(() =>
{
Thread.Sleep(1000);
return TestOutput("In Task");
}).ContinueWith(slowString =>
{
output.Add(slowString.Result);
output.Add(TestOutput("Action end"));
return output as IEnumerable<string>;
});
output.Add(TestOutput("Action Mid"));
return task;
}
private string TestOutput(string label)
{
var s = label + " ThreadID: " + Thread.CurrentThread.ManagedThreadId.ToString(CultureInfo.InvariantCulture);
s += " " + Thread.CurrentThread.CurrentCulture.EnglishName;
s += HttpContext.Current == null ? " No Context" : " Has Context";
Debug.WriteLine(s);
return s;
}
I would like to be able to ensure that the CurrentCulture is fr-FR, and that HttpContext.Current is not null at each point where TestOutput is called. I have not succeeded in doing that for the "In Task" call with anything I have tried. Also in some of my test thread id never varies suggesting that I have effectively removed the asynchronicity of the method. How can I ensure that the culture and HttpContext.Current are preserved at each call to TestOutput, and that the code is free to run on different threads?
Capturing HttpContext.Current in a closure and then simply setting it again will not work for me as I need to support Medium Trust which will throw a security exception when calling the HttpContext.Current setter.
A little noticed fact, HttpContext.Current is writable.
var context = HttpContext.Current;
var task = Task.Factory.StartNew(() => {
HttpContext.Current = context;
// You may want to set CultureInformation here too.
return TestOutput("In Task");
});
Context is preserved whenever you await tasks.
What you're seeing is that there's no context for thread pool tasks (Task.Run, TaskFactory.StartNew, or for that matter BackgroundWorker or Thread or Delegate.BeginInvoke). This is normal and expected.
So, don't use a thread pool task. Your example code seems to want to do parallel processing with multiple threads having the HttpContext, which simply isn't possible.
You can do concurrent async methods if you want, but this requires that your Thread.Sleep can actually be an async method instead of a CPU-based method:
[HttpGet]
public async Task<IEnumerable<string>> Test()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var output = new List<string> { TestOutput("Action start") };
var task = SlowStringAsync();
output.Add(TestOutput("Action Mid"));
output.Add(await task);
output.Add(TestOutput("Action end"));
return output;
}
public async Task<string> SlowStringAsync()
{
await Task.Delay(1000);
return TestOutput("In Task");
}
If your old library is out of your control and you can't make it async, then you'll have to call it synchronously. It's acceptable to call a synchronous method from an async method in situations like this:
[HttpGet]
public async Task<IEnumerable<string>> Test()
{
Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR");
Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture;
var output = new List<string> { TestOutput("Action start") };
output.Add(TestOutput("Action Mid"));
Thread.Sleep(1000);
output.Add(TestOutput("Not Really In Task"));
output.Add(TestOutput("Action end"));
return output;
}

Resources