Reputation: 3019
All of my functions are static methods. They look like the below sample with a static class and a static method to process the function.
public static class Function1
{
[FunctionName("Function1")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Function, "get", "post", Route = null)] HttpRequest req,
ILogger log)
{
log.LogInformation("C# HTTP trigger function processed a request.");
await Task.CompletedTask;
return new OkObjectResult("message");
}
}
For every function call, I'm setting Thread.CurrentThread.CurrentUICulture
based on a request parameter. This helps me return localized text based on the culture of the user device. (I'm using .resx files)
My question is, in a highly concurrent environment, is it possible for a user to receive text localized to another culture? If the same thread processes multiple functions simultaneously, then I believe this issue is possible. I'm trying to understand if Azure Functions allocates one function call per thread or not.
Upvotes: 4
Views: 2168
Reputation: 574
I'm trying to understand if Azure Functions allocates one function call per thread or not
The important question that I think you want answered is 'Is the same thread allocated for the whole lifetime of a single function call'. To which the answer is: It depends, which in your case I think is as good as a no.
In a non async world, applications flow from top to bottom so there is no chance for the thread to change. So if you never use the await
keyword, you can be pretty sure your function is going to run on a single thread.
Just using the await
keyword doesn't guarantee it'll change threads though. If the async
operation has already completed when you call await
, it will continue syncronously. An example of this would be in your example code where you do
await Task.CompletedTask;
Because Task.CompleteTask
is already complete, you won't get any thread swapping there.
On top of this, calling await
on a proper asyncronous call doesn't guarantee that the thread will change when it completes. When you call await
on a proper async call, it'll release the thread back into the thread pool so it can do other things. When the async call returns, two different things can happen.
If there is a SyncronizationContext
(There isn't one by default in Azure Functions), it will use that to continue the function on the same thread.
If there isn't one though, a random thread from the thread pool is used to continue the request. It's possible that it could pick the same thread that started it, but that's unlikely.
So basically in your situation, if you are actually doing async calls in your azure function you cannot rely on the same thread being used throughout the whole of a function call.
You said you are using Resx files for localization, while I've never had to do this it seems like it's a good way of doing it. I assume you were basing your solution off something like this post.
While you could manage the threads like this, I think it could potentially lead to unexpected issues if you have multiple Azure Functions inside your Azure Function App. Instead I'd recommend going off Microsoft's recommended localization practices and using the ResourceManager to get the localized resources without changing the thread culture.
In the comments of the post Rex says Azure Functions run independently from one another. They each run in their own little sandbox. This is true, but I think potentially the terminology/naming of things is confusing.
In Azure, you create a Azure Function App
, which contains Azure Function
's. I can't find an exact reference for how things work, but to me it seems like the Azure Function App
is the part that scales, not the individual Azure Function
's. This makes sense because all your individual Functions are part of the same assembly.
So all Azure Function
's inside a single Function App
share the same resources, including the thread pool. This is why I think changing the thread culture could cause unexpected issues in other Functions.
If I'm wrong on this though, let me know. I was basing what logically makes sense to me and this blog post. It says 'Functions loaded into memory' when discussing what happens during a cold start. I assume if each Function was its own sandbox, they would specify that somewhere.
Upvotes: 4