I’m taking a quick break from WF4 to blog about some fun we’re having with Entity Framework and K2. I’ll get back to WF4 straight away…
Our situation is as follows: Our workflow directly references a class library (our application’s business layer) and the class library uses Entity Framework to access the application database. The first event in our workflow is a code event which loads up some config entries from this class library (which in turns gets them from our database) and then assigns a task to the user. We’re starting our workflows through the API and we’re doing it synchronously, which means the method doesn’t return until the K2 process reaches a stable state like a client event.
Here’s the issue – the first call to start a process instance takes 35 seconds. After that they are all quick, but after a bit of inactivity it takes 35 seconds again.
To understand what’s happening you need to understand the inner workings of Entity Framework, K2 and AppDomains. The actual cause of the performance issue is Entity Framework. I won’t go into all the details (because we don’t need them) but when you first query a database using Entity Framework a model needs to be created. This takes a lot of time. There is also some connection pooling, which also takes time. The important point here is that the model and the connection pool are cached and kept alive within the context of an AppDomain.
To make it more complicated, K2 has designed their application such that each process definition has it’s own AppDomain (which is of course the correct design decision). This means that if you have 2 different process definitions referencing the same class library you will have 2 AppDomains, 2 cached models, 2 connection pools. K2 also unloads and reloads these AppDomains after 15 minutes of inactivity, so if nothing happens with ProcessA for 16 minutes and you start a new one, you’re going to see that long delay.
So what can you do? Unfortunately, not much. I tried hosting our class library in a WCF service in the hope that this would keep the model cached outside of our K2 process’ AppDomain but it doesn’t work that way. You can’t use a ‘Keep Alive’ script because the script would need to be run in the same AppDomain as the model, and unless you’re going to build that into each of your K2 processes that’s not going to work. You can, however, try one of the following options:
- Start your processes asynchronously. We may have to go this route.
- Set the AppDomainUnload timeout value to something greater than 15 minutes. To do this you set the “AppDomainUnload” setting in the \Host Server\bin\K2Server.setup file on the K2 server to something like 8 hours: <AppDomainUnload Minutes=”480″ />
Setting AppDomainUnload to a value of “0” turns off AppDomain unloading completely, but don’t ever do this or you will run into memory usage problems.
I hope this helps someone.