Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Timeout expired. - Using Db in ServiceStack Service

I'm using the Db property in a ServiceStack service to access my database but every now and then I get the following error from IIS:

Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.

Stack Trace:

[InvalidOperationException: Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.  This may have occurred because all pooled connections were in use and max pool size was reached.]
   System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) +6371713
   System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) +6372046
   System.Data.SqlClient.SqlConnection.Open() +300
   ServiceStack.OrmLite.OrmLiteConnection.Open() +44
   ServiceStack.OrmLite.OrmLiteConnectionFactory.OpenDbConnection() +132
   ServiceStack.ServiceInterface.Service.get_Db() +68

I have set the ReuseScope in the Configure method to ReuseScope.None which should close the connection on a per request basis I believe? What am I doing wrong here?

public override void Configure(Container container)
{
    JsConfig.EmitCamelCaseNames = true;

    //Register all your dependencies
    ConfigureDb(container);

    //Set MVC to use the same Funq IOC as ServiceStack
    ControllerBuilder.Current.SetControllerFactory(new FunqControllerFactory(container));
}

ConfigureDb:

private static void ConfigureDb(Container container)
{
    var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
    container.Register<IDbConnectionFactory>(c =>
        new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider))
        .ReusedWithin(ReuseScope.None);

    using (var db = container.Resolve<IDbConnectionFactory>().Open())
    {
      // Do database seed/initialisation
    }
}

Edit

After more diagnosis, it seems to happen when I refresh the page several times when I call this service method:

public Warranty Get(Warranty request)
    {
        var warranty = new Warranty();
        if (request.Id != default(int))
        {
            warranty = Db.Id<Warranty>(request.Id);
            warranty.WarrantyOrder = ResolveService<WarrantyOrderService>().Get(new WarrantyOrder { WarrantyId = warranty.Id });
            warranty.WarrantyStatus = ResolveService<WarrantyStatusService>().Get(new WarrantyStatus { Id = warranty.StatusId });
            warranty.WarrantyNotes = ResolveService<WarrantyNoteService>().Get(new WarrantyNotes { WarrantyId = warranty.Id });
            warranty.WarrantyDialogues = ResolveService<WarrantyDialogueService>().Get(new WarrantyDialogues { WarrantyId = warranty.Id });
            warranty.WarrantyCredit = ResolveService<WarrantyCreditService>().Get(new WarrantyCredit { WarrantyId = warranty.Id });
            warranty.WarrantyPhotos = ResolveService<WarrantyPhotoService>().Get(new WarrantyPhotos { WarrantyReference = warranty.WarrantyReference });
            warranty.WarrantyReport = ResolveService<WarrantyReportService>().Get(new WarrantyReport { WarrantyId = warranty.Id });
        }

        return warranty;
    }

I have changed ConfigureDb as per @mythz answer below:

    private static void ConfigureDb(Container container)
    {
        var connectionString = ConfigurationManager.ConnectionStrings["AppDb"].ConnectionString;
        container.Register<IDbConnectionFactory>(c => new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));
    }

The service needs to call out other services to populate the other objects on my Warranty object, I'm not sure on how to improve this?

like image 356
CallumVass Avatar asked Nov 12 '13 11:11

CallumVass


1 Answers

The IDbConnectionFactory like all connection managers is a thread-safe factory to create DB Connections, i.e. it's not a DB connection itself. It is supposed to be registered as a singleton.

By default ServiceStack's IOC (Funq) registers as a Singleton by default, so the correct registration is just:

container.Register<IDbConnectionFactory>(c =>
    new OrmLiteConnectionFactory(connectionString, SqlServerDialect.Provider));

Resolving Services

As ServiceStack Services have the potential to utilize managed resources, they should be used within a using statement whenever they're resolved from another service, so that they're appropriately disposed of, e.g:

using (var orders = ResolveService<WarrantyOrderService>())
using (var status = ResolveService<WarrantyStatusService>())
{
    var warranty = new Warranty { 
        WarrantyOrder = orders.Get(new WarrantyOrder { WarrantyId = warranty.Id }),
        WarrantyStatus = status.Get(new WarrantyStatus { 
            WarrantyId = warranty.StatusId }),
       //etc
    }       

    return warranty; 
}
like image 94
mythz Avatar answered Nov 06 '22 21:11

mythz