RSS

Category Archives: K2 API

Data Fields and Workflow Versions

Here’s our scenario… We have live workflows deployed and a fairly complex ASP.Net app as a front end. In our latest deployment we have added a few process level data fields and now the application expects to be able to read and write to those data fields. However, existing live processes won’t have those data fields so we will be looking at getting exceptions when we try access them. So what to do?

I figured there would be 2 ways of dealing with this sort of versioning. The first option is to have the process version stored in a data field which we could read and then the value of this version would tell us whether or not we can expect the data field to exist. This is fine in theory, but in practice I see there being problems further down the line, plus it means 2 reads instead of 1.

The option I decided on is to create a WorkflowDataField class and return this instead of just a string. This WorkflowDataField would not only contain the value of the data field but it would also have a result type so the calling code could check if there were errors.

Here’s my WorkflowDataField class:

    public enum ResultType
    {
        Success,
        DataFieldDoesNotExist
    }

    public class WorkflowDataField
    {
        private string m_value;

        public ResultType QueryResultType { get; set; }

        public string Value
        {
            get { return m_value; }
            set { m_value = value; }
        }

        public int IntValue
        {
            get { return Convert.ToInt32(m_value); }
            set { m_value = value.ToString(); }
        }

        // etc...

        public override string ToString()
        {
            return Value;
        }
    }

Pretty simple, nothing fancy. Now when I query the data field I do something like this:

var workflowDataField = new WorkflowDataField();
try
{
    workflowDataField.Value = processInstance.DataFields[key].Value.ToString();
    workflowDataField.QueryResultType = ResultType.Success;
}
catch (Exception e)
{
    workflowDataField.QueryResultType = ResultType.DataFieldDoesNotExist;
    workflowDataField.Value = e.Message;
}
return workflowDataField;

Unfortunately the exception thrown is nothing more specific than System.Exception so if you want to be sure that the exception is being thrown because of a missing data field then you have to parse the exception message for ‘does not exist’.

I am a firm believer in the philosophy of not using exceptions to control program flow, but in this case I made an exception (sorry for the pun). The reasons I’m allowing myself to do this are:
1. It’s not a system exception, it’s expected.
2. Exceptions are slow and expensive, but we’d only be catching exceptions while the old instances are alive. As time goes on we’d get fewer and fewer, so in the long run it would be better performance than doing 2 reads each time.

Hope that helps someone…

 
Leave a comment

Posted by on July 5, 2012 in K2 API, K2 Workflows

 

K2 and Entity Framework

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:

  1. Start your processes asynchronously. We may have to go this route.
  2. 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.

 
2 Comments

Posted by on January 25, 2012 in .Net, K2 API

 

Using the API 101

I was at one of the K2 Roadshows recently and the main question on everyone’s mind was, surprisingly, how to best go about using K2’s API. This isn’t going to be an in-depth answer but hopefully it will give people an idea of where to start.

First we need to clear up some concepts. The K2 Host Server is actually made up of a number of components or Hosted Services. Some examples of hosted services are:

  • Environment Library Server – this is the hosted service you query every time you want the environment fields for a particular environment
  • Licensing Management Server – The name speaks for itself
  • Event Bus Server
  • etc.

There are 2 hosted services which are important to get your head around, and these are:

  • Workflow Server – To do all things workflow-related.
  • SmartObject Server – To do all things SmartObject related.

Keep these 2 hosted services in mind because almost everything you do with the K2 API will be essentially communicating with these 2 hosted services. So how do we communicate?

Communication with the hosted services is always via a TCP connection. By default you’re going to be using port 5555, the only exception being the workflow server which uses port 5252. These ports can be configured and changed during the installation, but you wouldn’t want to.

So in a nutshell, here’s what you do to use the API:

  1. Open a TCP connection to the hosted service you need to connect to
  2. Make the call
  3. Close the TCP connection

Here’s an example of using the API to get the worklist of the person who is currently logged in (something you would need to do if you were building a custom task list, for example):

string K2ServerName = "localhost";
SourceCode.Workflow.Client.Connection connection = new SourceCode.Workflow.Client.Connection();
connection.Open(K2ServerName);
SourceCode.Workflow.Client.Worklist workList = connection.OpenWorklist();
connection.Close();

That’s about it, but I want to mention some best practices. First of all, re-use the same connection for all the calls you need to make, as shown below:

string K2ServerName = "localhost";
SourceCode.Workflow.Client.Connection connection = new SourceCode.Workflow.Client.Connection();
connection.Open(K2ServerName);
...Make many calls using the active connection...
connection.Close();

Secondly, if you encounter an exception while making the API calls you will never get to the line which closes the connection, which will leave you with open connections. This is obviously not what you want, so you should create your connection using a ‘Using’ statement so that the connections are closed for you as soon as you’re done with them (all K2 connections implement BaseAPIConnection which in turns implements IDisposable). Here’s how you do that:

string K2ServerName = "localhost";
using (SourceCode.Workflow.Client.Connection connection = new SourceCode.Workflow.Client.Connection())
{
    connection.Open(K2ServerName);
    SourceCode.Workflow.Client.Worklist workList = connection.OpenWorklist();
}

Hope that helps…

 
Leave a comment

Posted by on April 13, 2011 in K2 API