Hello,
We have a very strange problem first noticed in our live multi-tenant environment. Each tenant is able to store a number of theme variables, hex colour codes in a database. The bundle transformer code references a .less file and compiles to CSS with these theme variables (ModifyVariables). We noticed one tenant would randomly see theme colours of another tenant.
Back in development and during debugging we made sure on each Application BeginRequest to invalidate the cache and rebuild the theme CSS on every request. With multiple browsers running against different tenants we still found colours would bleed across tenants and across browser sessions.
After an awful lot of debugging and messing with IIS settings we found that the following process occurred in code with the following outcome:
...
LessTranslator is instantiated. This has an empty ModifyVariables property
LessTranslator.ModifyVariables is then updated with our values from the database. This has the correct values at this point
Some asset work occurs
LessTranslator.Translate is called which in turn calls CreateCompilationOptions. The ModifyVariables property now has values from another browser session.
So something is not thread safe, but we cannot work out what. The problem is always seen after an IISReset and we try to access different tenants from different browsers as concurrently as possible, i.e. clicking refresh in each browser as quickly as possible.
Has anyone seen this problem before? What information would you need to help us?
Regards,
Craig
Comments: Taritsyn, I'm very sorry for not getting back to you sooner and providing enough information. My priority was to get our live site up and running. We are currently working around the apparent thread issue. Our live site installed the following Nuget version packages: Microsoft.AspNet.Web.Optimisation 1.1.3 BundleTransformer.Core 1.9.69 BundleTransformer.Less 1.9.76 (sorry, a typo in my previous message to you) In order to debug through I took the version "Bundle Transformer 1.9.92 Alpha 3" from this site and opened the projects Core and Less within Visual Studio, alongside the last System.Web.Optimisation commit from CodePlex. Obviously our code then referenced this projects. In our code we created a ThemeBundleResult.cs with the following method: ``` public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (BundleTable.Bundles.BundleExists(ThemeVirtualPath)) { BundleTable.Bundles.Remove(BundleTable.Bundles.GetBundleFor(ThemeVirtualPath)); } BundleTable.Bundles.AddThemeBundle(ThemeVirtualPath, ThemeFiles.ToArray(), _themeBuilder); var bundleContext = new BundleContext(context.HttpContext, BundleTable.Bundles, ThemeVirtualPath); var bundle = BundleTable.Bundles.GetBundleFor(ThemeVirtualPath); var response = bundle.GenerateBundleResponse(bundleContext); context.RequestContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.RequestContext.HttpContext.Response.ContentType = response.ContentType; context.RequestContext.HttpContext.Response.Write(response.Content); } ``` We've found the resulting CSS in response.Content has values from a different tenant (browser session). So something is going wrong within bundle.GenerateBundleResponse(bundleContext). Within GenerateBundleResponse there is a Builder.BuildBundleContent(this, context, bundleFiles) line which calls our BaseThemeBuilder.cs which does the following: ``` var lessTranslator = bundle.Transforms.OfType<StyleTransformer>() .Where(x => x != null) .Select(x => x.Translators.OfType<LessTranslator>().FirstOrDefault()) .FirstOrDefault(); ``` and ``` lessTranslator.ModifyVariables = <this now goes to our database to get the theme colour variables> ``` Up to this point the database returns the correct string and the LessTranslator ModifyVariables is set correctly. Moving on we get to an ApplyTransforms method and within this is transform.Process. Up to this point everything still looks OK. Moving through the Process methods we eventually get to a Transform method in TransformerBase. This then leads to Translate in TransformerBase.cs. Eventually we get to a Translate method in the LessTranslator. We can see LessTranslator has been instantiated with an empty ModifyVariables property and then the property is set as I've mentioned above. So now we're in Translate in LessTranslator.cs. This calls a method CreateCompilationOptions(enableNativeMinification). This sets up a new CompilationOptions type using the ModifyVariables property of the LessTranslator instance. The ModifyVariables property value is now incorrect and contains the values from an entirely different browser session. So between us setting the ModifyVariables property with our database values and the creation of CompilationOptions which reads the ModifyVariables property value something has gone wrong. Yet nothing between setting and getting the value references ModifyVariables. I hope this helps. Regards, Craig
We have a very strange problem first noticed in our live multi-tenant environment. Each tenant is able to store a number of theme variables, hex colour codes in a database. The bundle transformer code references a .less file and compiles to CSS with these theme variables (ModifyVariables). We noticed one tenant would randomly see theme colours of another tenant.
Back in development and during debugging we made sure on each Application BeginRequest to invalidate the cache and rebuild the theme CSS on every request. With multiple browsers running against different tenants we still found colours would bleed across tenants and across browser sessions.
After an awful lot of debugging and messing with IIS settings we found that the following process occurred in code with the following outcome:
...
LessTranslator is instantiated. This has an empty ModifyVariables property
LessTranslator.ModifyVariables is then updated with our values from the database. This has the correct values at this point
Some asset work occurs
LessTranslator.Translate is called which in turn calls CreateCompilationOptions. The ModifyVariables property now has values from another browser session.
So something is not thread safe, but we cannot work out what. The problem is always seen after an IISReset and we try to access different tenants from different browsers as concurrently as possible, i.e. clicking refresh in each browser as quickly as possible.
Has anyone seen this problem before? What information would you need to help us?
Regards,
Craig
Comments: Taritsyn, I'm very sorry for not getting back to you sooner and providing enough information. My priority was to get our live site up and running. We are currently working around the apparent thread issue. Our live site installed the following Nuget version packages: Microsoft.AspNet.Web.Optimisation 1.1.3 BundleTransformer.Core 1.9.69 BundleTransformer.Less 1.9.76 (sorry, a typo in my previous message to you) In order to debug through I took the version "Bundle Transformer 1.9.92 Alpha 3" from this site and opened the projects Core and Less within Visual Studio, alongside the last System.Web.Optimisation commit from CodePlex. Obviously our code then referenced this projects. In our code we created a ThemeBundleResult.cs with the following method: ``` public override void ExecuteResult(ControllerContext context) { if (context == null) { throw new ArgumentNullException("context"); } if (BundleTable.Bundles.BundleExists(ThemeVirtualPath)) { BundleTable.Bundles.Remove(BundleTable.Bundles.GetBundleFor(ThemeVirtualPath)); } BundleTable.Bundles.AddThemeBundle(ThemeVirtualPath, ThemeFiles.ToArray(), _themeBuilder); var bundleContext = new BundleContext(context.HttpContext, BundleTable.Bundles, ThemeVirtualPath); var bundle = BundleTable.Bundles.GetBundleFor(ThemeVirtualPath); var response = bundle.GenerateBundleResponse(bundleContext); context.RequestContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache); context.RequestContext.HttpContext.Response.ContentType = response.ContentType; context.RequestContext.HttpContext.Response.Write(response.Content); } ``` We've found the resulting CSS in response.Content has values from a different tenant (browser session). So something is going wrong within bundle.GenerateBundleResponse(bundleContext). Within GenerateBundleResponse there is a Builder.BuildBundleContent(this, context, bundleFiles) line which calls our BaseThemeBuilder.cs which does the following: ``` var lessTranslator = bundle.Transforms.OfType<StyleTransformer>() .Where(x => x != null) .Select(x => x.Translators.OfType<LessTranslator>().FirstOrDefault()) .FirstOrDefault(); ``` and ``` lessTranslator.ModifyVariables = <this now goes to our database to get the theme colour variables> ``` Up to this point the database returns the correct string and the LessTranslator ModifyVariables is set correctly. Moving on we get to an ApplyTransforms method and within this is transform.Process. Up to this point everything still looks OK. Moving through the Process methods we eventually get to a Transform method in TransformerBase. This then leads to Translate in TransformerBase.cs. Eventually we get to a Translate method in the LessTranslator. We can see LessTranslator has been instantiated with an empty ModifyVariables property and then the property is set as I've mentioned above. So now we're in Translate in LessTranslator.cs. This calls a method CreateCompilationOptions(enableNativeMinification). This sets up a new CompilationOptions type using the ModifyVariables property of the LessTranslator instance. The ModifyVariables property value is now incorrect and contains the values from an entirely different browser session. So between us setting the ModifyVariables property with our database values and the creation of CompilationOptions which reads the ModifyVariables property value something has gone wrong. Yet nothing between setting and getting the value references ModifyVariables. I hope this helps. Regards, Craig