3

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.

user246392
  • 2,661
  • 11
  • 54
  • 96
  • Azure Functions run independently from one another. They each run in their own little sandbox. You do not have a single instance of an Azure Function running multiple requests simultaneously. Azure will scale up the number of instances to meet your demand. – Rex Henderson Apr 02 '21 at 04:19
  • @RexHenderson If I understand them correctly, that isn't answering their question. It sounds like they're worried about when an `await` returns, it could be on a different thread to what it started on. I can't say I'm familiar enough with `Thread.CurrentThread.CurrentUICulture` or Azure Functions to answer confidently myself. But I assume Azure Function's (like AspNet Core) doesn't have a `SynchronizationContext`, which means after an await, a random available thread will continue. Which means it wouldn't (always) have the same `Thread.CurrentThread.CurrentUICulture` value. – Lolop Apr 02 '21 at 04:27
  • We can leave it up to OP as to whether I am responding to their question accurately. As for Azure Functions, while az v1 may try to strip the sychronization context and v2 Core doesn't have one, you cannot determine what libraries someone is creating an az function with and whether those libraries have a synchronization context or not. – Rex Henderson Apr 02 '21 at 04:34
  • Rex's answer is in line with what I was hoping for as an answer, but @Lolop's input made me even less confident about using `Thread.CurrentThread.CurrentUICulture` so I'm more confused now. How does one properly localize text on the server side? – user246392 Apr 02 '21 at 04:51
  • Personally I'd recommend looking into ways to do it without messing with the threads culture. It'd be possible to manage it, but you could have threads in the thread pool with random cultures, which could cause issues elsewhere. Looks like you can use the [ResourceManager](https://learn.microsoft.com/en-us/dotnet/api/system.resources.resourcemanager?view=net-5.0) to do it without changing the thread culture, issue is I think you'd lose the strongly typed resource properties. – Lolop Apr 02 '21 at 05:10

1 Answers1

3

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.

What should you do

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.

*Extra note about Azure Functions

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.

Lolop
  • 514
  • 2
  • 9
  • 1
    Thanks. I ended up creating a custom implementation that returns the localized text based on the provided culture information. I won't bother with `ResourceManager` for the time being. I stopped relying on the thread UI culture due to potential async problems. – user246392 Apr 03 '21 at 21:53