Thursday, November 16, 2006

Repair Error, this instance or a whole lot more?

K2.net 2003 have the great feature of fixing errors on the fly. When any code component in K2 (line rule, start or preceding rule, event, etc.) generates an unhandled exception, the process instance goes into an Error state. These process instances in Error state can be viewed form the K2.net Service Manager by creating a Error Profile and then viewing the errors in that profile.




From here the Service Manager allows you to open the details of the error and various details of the error can be viewed including:

  • Process Name
  • Error Type
  • Error Location
  • Error Details


The really nice feature is being able to see the code that generated the error. From this view you can edit the code and repair the error on the fly.




But... what is the effect of doing this? The K2 server stores only ONE copy of this code. Its stored in the _Code table in the transaction DB (normally called K2). By modifying the code in this error details window and hitting the Repair button, the Service Manager updates the code in the _Code table and re-executes the object (in my example an error was created in an Server Event, so the event is re-executed with the new code).

Because there is only one copy of the code, the change made is reflected for ALL process instances and all new process instances. So be careful what you do in your code change as its going to affect way more than just the current process instance! As a matter of fact, even previous version of the process having the same code is updated.

Something else to note is that the original process definition will still contain the un-edited bad code and will also need to be looked at before you re-export the process.

I must apologise to my ex students as I've probably not been explaining the behaviour correctly in the past. We burn and learn. Comments (including possible moans at me) welcome!

Thursday, September 21, 2006

K2.net 2003 development best practices...

I've spent some time with some of the top K2.net UK consultants recently. The topic of K2.net 2003 workflow development best practices came up (again) and I thought I'll share some here. Best practices are subjective, so feel free to share your own opinions.

Datafields: You'll by now have noticed the three check boxes at the bottom of the screen when creating a datafield in the K2.net Studio. Don't ignore them! 'Keep Audit Trail' will result in the K2 server making a copy of the datafield every time its value change. This option should not be checked by default. Only enable this for datafields that needs to be audited (for tracking or compliance reasons). 'Data On Demand' means that the value of the datafield is only loaded when its accessed and not when the process instance is created. A ProcessInstance object is created (and all the non-data on demand datafields are loaded) whenever a worklist item or server item is loaded. Only leave this option unchecked for datafields that you will access often to make sure they are readily available. For datafields that are large or datafields that are not often accessed, ticking this option will create a small delay when accessing them, but will greatly improve the performance of loading a worklist item or server item.

Worklist performance: This is where most clients experience bottlenecks in performance and its very often due to a bad design. Consider the best practise for datafields to improve access to a worklist. Secondly, don't load a users full worklist if its not required. Use the WorklistCriteria object when loading the worklist in order to paginate and optimally filter the result.

Connections: The rule of 'acquire late, release early' for any resources that your application use applies to the K2.net Connection object as well. In plain English this means, open the connection to the K2 server as late as possible and close it as soon as you are done (except if you need to keep holding onto the connection to improve multiple transactions that follow each other). When you are done, close the connection, don't be lazy and just conveniently forget about it. The Connection object itself might be a managed object in .NET but the resource you are consuming, the actual connection to the K2 server, is not. Same goes for SQL connections, files handles, etc. Because the K2 Connection object does not implement IDisposable (and even if it did, (repeat after me) you must still close the connection), I normally use this simple pattern to ensure that my connection is closed:

K2ROM.Connection con;
try
{
con = new K2ROM.Connection();
con.Open();
// use it here
}
catch (Exception ex)
{
// handle exception here
}
finally
{
if (con != null) con.Close();
}


Error Handling: Things go wrong, bottom line. You must build exception handling into your process. Exception handling can be added as low as event and line rule level, up to a general exception handler for the process. Build exception handling that can work around the error in line with the business process. As a last resort, allow the exception to be bubbled up to the K2 server.

Code location: Although there has been massive improvements in the code editor in K2.net Studio, consider a better, re-usable and more maintainable architecture by writing your code in VS.NET and building a referenced assembly. You can pass any event sensitive context object from your process into your assembly by setting a reference to the K2ROM and KO libraries in your assembly.

Log file performance: Once your solution has been tested and rolled out, disable the log file output of the K2.net server in the Service Manager - if its not needed. Depending on the amount of activity on your server, writing a log file can impact on the server's performance.

Process data v.s. external data: K2.net 2003 is a great workflow engine, its a bad data store. You don't store your MP3 files in your email client, so don't store your application data in your process. Store only in the process data that's related to the workflow. If your workflow processes a document, keep the document in its own store (network share, SharePoint, etc.) and work with a reference to the document in your process. If your workflow processes a customer, store the customer record in a database and work with the id in the process. Ask yourself the question, 'who is the owner of this data'. If its not K2, don't store it in K2.

Large destination rules: Few developers realise that the K2 server creates an instance of an Activity for each destination in the destination rule. This means that an Activity with a hundred destinations will result in the K2 server creating a 100 instances of the Activity in the transaction database. A service pack 3 feature allows you to limit this to one instance and should be considered where performance is important and multiple Activity instances (i.e. Activity datafields) are not required.

Debugging: By running the K2 server in interactive mode, you can view the real time log output of the server. Write plenty of trace information in your process - this does not only help tracking development bugs, but will help to assist finding the audit and source of an issue that surface during production.

I'll keep building this list as I stumble across issues.

Monday, January 30, 2006

K2.net client event serial numbers and emails

Long time no blog.

Interesting one I picked up the other day. I was putting a URL in an email notification for a user (destination) to complete a worklist item. The user kept on reporting that under some conditions, he would hit the link, but the page would not open correctly because the serial number that was past of the URL query string, got cut off at a comma.

K2.net builds serial numbers for client events in order to correctly identify a specific client event for a given activity instance:


How does k2 generate the serial number?
http://forum.k2workflow.com/viewtopic.php?t=2&sid=6b1b93ac90bad66f0c700a8bef7d910e

The Serial Number is unique for each event instance within the process instance. This means that if you have an activity that contains 2 events (for example a server and client event), then during the execution of the process, each of these will have their own unique serial number. The serial number is basically the most granular unique identifier within the process. It identifies a single step, within a specific activity instance, within a specific process instance, and if it is a client event, the serial number also uniquely identifies which user it is allocated to. Therefore, if an activity is set to have 3 users in it's destination rule, then for the client event, there will be 3 different serial numbers for each user that the client event is sent to.


The format of the serial number is something like SERVER,xx,yy. When I included the URL of the client page in the email notification to the destination, his email client would sometimes (if the line needs to be line wrapped) cut the URL into two lines in the email. It would do this at one of the commas.

To avoid this, I had to UrlEncode the serial number part in the URL. This will avoid that the URL gets cut in two pieces (even if the line gets line wrapped) when included in an text email.

The code looks like this:
sn = System.Web.HttpUtility.UrlEncode(K2.SerialNumber);

URL before using HttpUtility.UrlEncode:
http://server/sites/page.aspx?sn=SRV,12,13

URL after using HttpUtility.UrlEncode:
http://server/sites/page.aspx?sn=SRV%2c12%2c13