Friday, August 24, 2012

Ways to prevent connection/resource leaks - Part 2

You may use these techniques to handle any expensive resource leaks, not just for connection leaks. In Part 1, I am explaining how to fix resource leaks in simpler ways. In this article I am explaining how to fix resource leaks with structuremap and how the structuremap object creation machnism works in multithreaded environment. Structuremap scopes explained here. Also I am explaining evils of singleton object in a multi-threaded environment.

  1. Fix for heterogeneous application - 1

  2. a. If your application is partially/not dependent on ORM.
    b. If your application uses DI/IOC tools like structure map.
    c. If your application is single threaded.

    Solution


    The fact is that singleton objects created by structuremap for HttpContext scope are cached. To create singleton object use the following line of code in your structuremap registry.
    Code:
    For<SqlConnection>().HttpContextScoped().Use(new SqlConnection());
    
    Since it is cached, you can easily dispose it in the global.asax file's Application_EndRequest event by calling the following method.
    Code:
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    

  3. Fix for heterogeneous application - 2

  4. a. If your application is partially/not dependent on ORM.
    b. If your application uses DI/IOC tools like structure map.
    c. If your application is multi-threaded, but uses limited number of threads.

    Solution


    The following line in structuremap registry creates object for both thread scope and http context scope.
    Code:
    For<SqlConnection>().HybridHttpOrThreadLocalScoped().Use(new SqlConnection());
    

    It requires two operations. One is thread scope cleanup and the other is HttpContext scope cleanup.

    In structuremap, thread scoped singleton objects are not cached. The above line of code creates one singleton object for each thread. The thread scoped singleton objects must be disposed at your own cost. That means you must explicitly dispose at the end of every thread in your application. Such as

    Code:
    ObjectFactory.GetInstance<SqlConnection>().Dispose();
    

    Now, the HttpContext scoped objects can be disposed by calling the following method in Application_EndRequest event.

    Code:
    ObjectFactory.ReleaseAndDisposeAllHttpScopedObjects();
    

  5. Fix for heterogeneous application - 3

  6. a. If your application is partially/not dependent on ORM.
    b. If your application uses DI/IOC tools like structure map.
    c. If you are restoring HttpContext.Current within the thread. That is, within a thread you cannot access HttpContext variables, it will be null. But in some cases you may need HttpContext data within a thread and you pass HttpContext.Current as parameter to the thread. This way you restore the HttpContext within a thread. Such as
    Code:
    //Thread method, this method will be executed in parallel by more than 1 threads. ThreadParameters is a class created by me.
    private void BindDataThread(ThreadParameters parameter)
    {
     //I need to access data from HttpContext, restoring it.
     HttpContext.Current = parameter.CurrentHttpContext;
     var connection = ObjectFactory.GetInstance<SqlConnection>();
    }
    

    If you use the above line, it will create a singleton object for HttpContextScope and the same object will be served for various threads from structuremap. Since SqlConnection object is not thread safe it will throw random errors. That means, all threads will be using the same SqlConnection object, one thread will be in retrieving data from the database and the other will be in opening the connection, the third one will be in closing the connection by using the same connection object.

    So what happens to the thread local scope? That I mentioned “HybridHttpOrThreadLocalScoped” in the structuremap registry to create object for thread local scope. Well, in structuremap the HttpContext scope takes precedence by checking HttpContext.Current != null. As soon as you assign "HttpContext.Current = parameter.HttpContext", it will only create object for HttpContext scope within a thread; but it supposed to create object for thread local scope!!

    So what can be the solution?

    Code:
    //For().HybridHttpOrThreadLocalScoped().Use(new SqlConnection());
    For<IVehicle>().LifecycleIs(new ThreadLocalStorageLifecycle()).Use<Car>();
    

    You may try by using the second option (the uncommented) instead of the first option (the commented) as I mentioned above.

    How it will work?

    This creates a singleton object for each thread. That is, the structuremap creates objects per thread basis, no matter for which context the object is requested.

    On the other hand, it can be overhead. Objects created in the page handlers/controllers are run by main thread. We have to dispose them too.

    We can use HttpModules or Application_EndRequest to dispose objects created in main thread.

    Otherwise, if we have an option in DI/IOC container to specify to take ThreadLocalScope lifecyle as precedence, it can be bit more easier.

    Note: If you are using nested DataReader this approach will not work. See the code below

    Code:
    
    int departmentCode;
    //The following statement opens an SqlConnection and going to be active throughout the scope of "using" statement.
    using (SqlDataReader departmentReader = GetDepartmentReader()) 
    {
     while (departmentReader.Read())
     {
      departmentCode = Convert.ToInt32(departmentReader.GetValue(0));
      //The following line will try to use an another reader in the same thread, Since we made the SqlConnection as singlton per thread, this code will again open the same connection and will throw error.
      using (SqlDataReader employeeReader = GetEmployeeReader(departmentCode))
      {
       ...
       ...
      }
     }
    }
    

    All this approaches may raise following questions in your mind.

  7. Why can't you use transient objects?
  8. Transient objects are not cached anywhere in structuremap, you can’t reference back and dispose them.

  9. Why can’t you use nested containers?
  10. For the situations that we discussed so far, I feel the nested containers are overhead. I can simply use “using” statement instead. Both provides the similar solution.


Back to Part-1

No comments:

Post a Comment