Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Nancy slow to start accepting requests

I am struggling to see why Nancy doesn't accept requests for about 1 minute. I have approximately 60 endpoints and all these initialize very quickly so all the modules get processed.

Are there common reasons for this? Or is there a way to track down what is going on?

EDIT

Logging of app start up

App Start 4/15/2014 11:03:48 AM
App Start Complete 4/15/2014 11:03:48 AM
Bootstrap 4/15/2014 11:04:19 AM
Module 3 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:37 AM
Module 2 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:37 AM
Module 1 4/15/2014 11:06:38 AM
Module 1 4/15/2014 11:06:38 AM
Module 1 4/15/2014 11:06:38 AM

As can be seen in the times there is a delay before bootstrap and also before modules are called.

EDIT 2

My configuration is Nancy (v0.22.2 built from source as strong key was needed with no code changes) ASP.NET 4.5 using Web Forms. Using Visual Studio 2013 as IDE

like image 346
Dreamwalker Avatar asked Apr 15 '14 08:04

Dreamwalker


2 Answers

I think I found what the issue was. The problem is with the AutoRegister feature from the TinyIoC container that Nancy uses.

Basically at startup (first request) it scans every assembly of your AppDomain to register dependencies. This process is very slow: https://github.com/NancyFx/Nancy/issues/643

The solution is to register your dependencies manually as indicated here: https://github.com/NancyFx/Nancy/wiki/Bootstrapping-nancy

Basically you just have to create a class in your AspNet project that inherits from DefaultNancyAspNetBootstrapper and override the ConfigureApplicationContainer method:

public class Bootstrapper : DefaultNancyAspNetBootstrapper
{
    protected override void ConfigureApplicationContainer(TinyIoCContainer container)
    {
        // Register our app dependency as a normal singleton           

    }
}

Hope this helps,

like image 135
gsimoes Avatar answered Nov 15 '22 20:11

gsimoes


The root cause of slow performing of AutoRegister feature in Nancy is huge amount of assemblies and types processed. The second issue is not optimal code for such case. The current stable version of Nancy (1.4.3) has the following code https://github.com/NancyFx/Nancy/blob/1.x-WorkingBranch/src/Nancy/TinyIoc/TinyIoC.cs#L3092-L3094 As result this linq expression processed for each type... To change this behavior you can use the following class as base for your Bootstrapper. Also you can speed up this code by exclude your 3d party assemblies in AutoRegisterIgnoredAssemblies (see example in code).

In my system it helps to increase speed of auto registration by 521 times from 50 secs to 95 ms.

    public class AutoRegistredBootstrapper : DefaultNancyBootstrapper
{
    private readonly object AutoRegisterLock = new object();
    private void AutoRegister(TinyIoCContainer container, IEnumerable<Assembly> assemblies)
    {
        var ignoreChecks = new List<Func<Type, bool>>()
        {
            t => t.FullName.StartsWith("System.", StringComparison.Ordinal),
            t => t.FullName.StartsWith("Microsoft.", StringComparison.Ordinal),
            t => t.IsPrimitive(),
            t => (t.GetConstructors(BindingFlags.Instance | BindingFlags.Public).Length == 0) && !(t.IsInterface() || t.IsAbstract()),
            t => t.Assembly == typeof(NancyEngine).Assembly
        };


        lock (AutoRegisterLock)
        {
            var thisType = this.GetType();
            var types = assemblies.SelectMany(a => AssemblyExtensions.SafeGetTypes(a))
                .Where(type => (type.DeclaringType != thisType)
                               && !type.IsGenericTypeDefinition())
                .Where(t => !ignoreChecks.Any(check => check(t)))
                .ToList();

            var assignebleTypes =
                types.Where(
                        type =>
                            type.IsClass() && (type.IsAbstract() == false) && (type != thisType))
                    .Select(t =>
                    {
                        // be careful with side effects in linq
                        container.Register(t);
                        return t;
                    })
                    .Where(implementationType => implementationType.GetTypeInfo().ImplementedInterfaces.Any() || implementationType.BaseType != typeof(Object))
                    .ToList();

            var abstractInterfaceTypes = types.Where(type => ((type.IsInterface() || type.IsAbstract())));

            foreach (var abstractInterfaceType in abstractInterfaceTypes)
            {
                var localType = abstractInterfaceType;
                var implementations =
                    assignebleTypes.Where(implementationType => localType.IsAssignableFrom(implementationType)).ToList();

                if (implementations.Count > 1)
                {
                    if (implementations.Count != implementations.Distinct().Count())
                    {
                        var fullNamesOfDuplicatedTypes = string.Join(",\n",
                            implementations.GroupBy(i => i).Where(j => j.Count() > 1).Select(j => j.Key.FullName));

                        throw new ArgumentException($"types: The same implementation type cannot be specified multiple times for {abstractInterfaceType.FullName}\n\n{fullNamesOfDuplicatedTypes}");
                    }

                    foreach (var implementationType in implementations)
                    {
                        container.Register(abstractInterfaceType, implementationType, implementationType.FullName);
                    }

                }

                var firstImplementation = implementations.FirstOrDefault();
                if (firstImplementation != null)
                {
                    container.Register(abstractInterfaceType, firstImplementation);
                }
            }
        }
    }

    protected override IEnumerable<Func<Assembly, bool>> AutoRegisterIgnoredAssemblies
    {
        get
        {
            return DefaultAutoRegisterIgnoredAssemblies.Concat(new Func<Assembly, bool>[]
            {
                asm => asm.FullName.StartsWith("ICSharpCode.", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("Ionic.", StringComparison.Ordinal),
                asm => asm.FullName.StartsWith("CommandLine,", StringComparison.Ordinal)
                // ADD THE REST OF 3D party libs that you don't use as dependencies 
            });
        }
    }

    protected override void ConfigureApplicationContainer(TinyIoCContainer container)
    {
        var currentDomainAssemblies = AppDomain.CurrentDomain.GetAssemblies();

        var ignoredAssemblies = this.AutoRegisterIgnoredAssemblies.ToList();
        var asmsForProcessing = currentDomainAssemblies.Where(a => !ignoredAssemblies.Any(ia => ia(a))).ToList();

        AutoRegister(container, asmsForProcessing);
    }

}
like image 38
Eduard Kibort Avatar answered Nov 15 '22 21:11

Eduard Kibort