Overview
We have a multinational website which has localised content for the various countries it serves. This localisation is implemented using standard .Net resource files.
When our web application starts up or recycles under load on the production environment, sometimes it will display the wrong resources for a particular country. E.g. the UK site may show French content.
This continues to happen until the application is restarted.
Detail
The production environment is IIS 8 on Windows Server 2012.
The application is implemented in ASP.Net MVC 4.
The application decides which locale it is serving by the incoming URL. So www.mysite.com will be UK English www.mysite.fr will be French etc.
We have an implementation of IHttpModule which is registered via a Web.config.
In the Init method of the module, it attaches a handler to the BeginRequest event.
In this method, the incoming URL is examined and the thread's CurrentUICulture is set to an appropriate value. en-GB for www.mysite.com, fr-FR for www.mysite.fr etc.
This system works well for the most part. However, sometimes when the application starts up while it is receiving requests, it will consistently serve the wrong content for some of the resource files.
It continues to do this until the application is restarted. It may again restart serving the incorrect content. We have to keep restarting until it is serving the correct content, at which point it will remain stable.
Analysis
We have been able to reproduce this locally on a development PC by throwing requests at the application during startup (using Fiddler). The site was showing German content for some resource files on the UK version of the site.
Having checked the obvious culprits in our code (that the CurrentUICulture is set correctly by the HTTP module and remains correct throughout the processing of the request), we started to look at the resource manager.
With the application started in this incorrect state, we examined the contents of the _resourceSets property on the ResourceManager class.
This is a Dictionary keyed on the ISO culture code.
Examining the contents of en-GB, we found that it did actually contain the resource strings from the German version of the resource file.
It seems that sometimes, when the site is starting up while receiving requests, the ResourceManager class is loading the wrong resource file for a culture or it is incorrectly classifying the file in its Dictionary.
Has anyone else experienced this kind of behavior and is anyone aware of any workarounds?
Thanks.
This sounds like you are having thread safety problems in your handler. When you modify the threads current culture, you are modifying it for the current thread that could be processing multiple requests. When the responses are generated, another request could have altered the thread current language giving all responses the same language.
I have a few suggestions you can start off with:
- Make sure your handler is NOT reusable. I assume you've made implemented IHttpHandler, return false for the IsReusable property since multiple threads should hit it at the same time and a new instance will be created per request.
- Don't use a handler... A handler is not the ideal solution for something like this, The preferred place to set the thread culture is in Application_AcquireRequestState which will be correctly fired for each request without overlapping.
- Use a routing handler instead: http://adamyan.blogspot.com/2010/07/addition-to-aspnet-mvc-localization.html
Without seeing the handler you speak of the rest can only be speculation as to why responses are sharing thread cultures. The problem could easily lie in the dictionary you are using as well which is inherently not thread safe.
We hit the same problem recently. This appears to be a race condition in ResourceManager (or its helper code).
I've put together a repro at https://bitbucket.org/onyxmaster/resmanrc.
Also, I filed a bug on MS Connect site, at https://connect.microsoft.com/VisualStudio/feedback/details/806505/.
P.S. I'm not sure if this counts as an answer since there is no workaround I know of, but at least now there is a repro and a bug report.
For anyone else experiencing this problem, I never got a fix for the underlying cause of this problem, but did find a workaround.
When the app starts up, I had a routine go through each resource file sequentially and request a resource from each file in each supported language.
'Touching' each file in this way in a single threaded manner seemed to allow all the resources to load correctly every time.
Are you using async MVC actions ? If yes, when you await a call (with ConfigureAwait set to false), the processing thread is reused while it is in progress. the "returning" thread (the thread executing the code after the awaited call) is not the same and may loose all properties set before. ConfigureAwait must be set to false to prevent deadlocks, so there is no immediate solution.
Another thing to check is caching, if you use the [Cache] attrbute or if you cache the resouce manager.
I read this answer from other resources. And would like to wrap the ResourceManager as follows so the new RexourceManager() is not being raced under loaded multi-thread call:
public sealed class LocalizationHandler
{
[ThreadStatic]
private static ResourceManager _manager
private readonly ConcurrentBag<ResourceManager>
_localizationIdentityCollection = new ConcurrentBag<ResourceManager>();
private LocalizationHandler(){}
public static LocalizationHandler Load(ResourceType source)
{
switch (source)
{
case typeA:
//check if resource exist in the concurrent bag or create a new one
_manager = getManager();
break;
}
return this;
}
public string Get(string key)
{
return _manager.Get(key)
}
}
Then you can call like:
LocalizationHandler.Load(ResourceType.TypeA).Get("your resource Key String")
Further more, you can use type safe enum for the resource type with name for resource name, and wrap the resource manager together with the culture info into a private class the save it in the concurrent bag.