Nancy icon indicating copy to clipboard operation
Nancy copied to clipboard

2.0-barney RequestExecutionException on initial simultaneous requests

Open dk-ok opened this issue 8 years ago • 3 comments

Prerequisites

  • [x] I have written a descriptive issue title
  • [x] I have verified that I am running the latest version of Nancy
  • [x] I have verified if the problem exist in both DEBUG and RELEASE mode
  • [x] I have searched open and closed issues to ensure it has not already been reported

Description

The following error:

Nancy.RequestExecutionException: Oh noes! ---< System.ArgumentNullException: Value cannot be null.
Parameter name: type
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at System.Activator.CreateInstance(Type type)
   at Nancy.ViewEngines.Razor.RazorViewEngine.><c__DisplayClass14_0.>GenerateRazorViewFactory<b__1()
   at Nancy.ViewEngines.Razor.RazorViewEngine.GetOrCompileView(ViewLocationResult viewLocationResult, IRenderContext renderContext, Type passedModelType)
   at Nancy.ViewEngines.Razor.RazorViewEngine.GetViewInstance(ViewLocationResult viewLocationResult, IRenderContext renderContext, Object model)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute4[T0,T1,T2,T3,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3)
   at Nancy.ViewEngines.Razor.RazorViewEngine.GetViewStartLayout(Object model, IRenderContext renderContext, Assembly referencingAssembly, Boolean isPartial)
   at System.Dynamic.UpdateDelegates.UpdateAndExecute5[T0,T1,T2,T3,T4,TRet](CallSite site, T0 arg0, T1 arg1, T2 arg2, T3 arg3, T4 arg4)
   at Nancy.ViewEngines.Razor.RazorViewEngine.><c__DisplayClass9_0.>RenderView<b__0(Stream stream)
   at Nancy.Responses.MaterialisingResponse.PreExecute(NancyContext context)
   at Nancy.NancyEngine.>InvokeRequestLifeCycle<d__22.MoveNext()
   --- End of inner exception stack trace ---
   at Nancy.NancyEngine.InvokeOnErrorHook(NancyContext context, ErrorPipeline pipeline, Exception ex)

Steps to Reproduce

After startup, when requesting 2 distinct views (and perhaps more) simultaneously (via Chrome), both requests return the error above. The 2 views are embedded in two other assemblies and resolved via ResourceViewLocationProvider.

If I request only one, let it complete, then request the other, no problem. Also works if I request both simultaneously after this kind of initial success.

System Configuration

  • Nancy version: 2.0
  • Nancy host
    • [ ] ASP.NET
    • [x] OWIN
    • [ ] Self-Hosted
    • [ ] Other:
  • Other Nancy packages and versions:
  • Environment (Operating system, version and so on): VS2015, Win 7
  • .NET Framework version: 4.5.2
  • Additional information:

dk-ok avatar Sep 14 '16 19:09 dk-ok

@dk-ok Ever got this working?

mylemans avatar Jun 18 '17 15:06 mylemans

We're still having this issue, any update? Or any workaround available (like a trick to delay the next requests until the first is finished?)

mylemans avatar Sep 10 '17 09:09 mylemans

Seems that the problem is occurred when razor views are generated concurrently. The workaround i came up with is to replace DefaultViewCache with "ThreadSafe" view cache, to prevent concurrent views generation. A view generator delegate is passed into view cache here: https://github.com/NancyFx/Nancy/blob/master/src/Nancy.ViewEngines.Razor/RazorViewEngine.cs#L355 So, the code above not prevent concurrent views generation (both for different and for the same views). ThreadSafeViewCache:

    public class ThreadSafeViewCache: IViewCache
    {
        private readonly ConcurrentDictionary<ViewLocationResult, object> _cache;
        private readonly object _cacheLock = new object();
        private readonly ViewConfiguration _configuration;

        public ThreadSafeViewCache(INancyEnvironment environment)
        {
            _cache = new ConcurrentDictionary<ViewLocationResult, object>();
            _configuration = environment.GetValue<ViewConfiguration>();
        }

        public TCompiledView GetOrAdd<TCompiledView>(ViewLocationResult viewLocationResult, Func<ViewLocationResult, TCompiledView> valueFactory)
        {
            if (_configuration.RuntimeViewUpdates)
            {
                if (viewLocationResult.IsStale())
                {
                    _cache.TryRemove(viewLocationResult, out var old);
                }
            }

            if (_cache.TryGetValue(viewLocationResult, out var value))
                return (TCompiledView)value;

            lock (_cacheLock)
            {
                if (_cache.TryGetValue(viewLocationResult, out value))
                    return (TCompiledView)value;

                value = valueFactory(viewLocationResult);
                _cache.TryAdd(viewLocationResult, value);
            }

            return (TCompiledView)value;
        }
    }

At first i thought the problem will be resolved by creating lock per view file (to ensure a view is compiled only once), but unfortunately not.

IliaBrahinets avatar Dec 24 '19 17:12 IliaBrahinets