Thursday, August 6, 2009

How to move K2 [blackpearl] data from one farm to another

Sometimes it can be useful to recreate the same situation that you have in a K2 farm elsewhere:

  • if you got some troubles in a production environment and you want to investigate on them without corrupting the environment further
  • if you want to test an hotfix or upgrade and you want to do that having exactly the same situation that you will find in your final environment
  • if you want to stress test a clone of your production environment without interrupting the service

K2 [blackpearl] comes with 14 databases. As far as it concerns processes data, however, only two databases have the main impact:

  • K2Server: it is the transactional database used for workflow execution and tasklist retrieval
  • K2ServerLog: it is the logging database used for reporting data, smart object and viewflow

While the server is running some dedicated threads move data that's no more needed for execution from the K2Server to the K2ServerLog database.

In order to move data from one farm to another it is sufficient to replace these two databases in the destination farm with the ones from the source farm (either via detach/attach or backup/restore).

At that point it is enough to run the following T-SQL script in the K2Server database of the destination farm before restarting the K2 server service:

USE [K2Server]


-- Setting K2 Server and removing extra entries

DECLARE @HOSTNAME NVARCHAR(20)

DECLARE @LICENSE NVARCHAR(20)

SELECT @LICENSE = [LicenseKey], @HOSTNAME=[HostName] FROM [HostServer].[dbo].[LicenseKeys] WHERE [HostName] = 'dest farm hostname'

UPDATE [_Server] set [Name]=@HOSTNAME, [LicenseKey]=@LICENSE WHERE [ID] = 1

DELETE FROM [_Server] WHERE ID > 1


-- Update Server Users

DELETE FROM [_ServerUser]

INSERT INTO [_ServerUser] VALUES ('K2:DENALLIX\Administrator', 1,1,1)

INSERT INTO [_ServerUser] VALUES (''K2:DENALLIX\K2Admin', 1,1,1)

INSERT INTO [_ServerUser] VALUES (''K2:DENALLIX\K2App', 1,1,1)


-- Update Settings…

UPDATE [_Setting] SET [Value] = 'Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=' + @HOSTNAME + ';Port=5555' WHERE [Name] = 'UMData'


--Update String Tables

UPDATE [_StringTable] SET [Value] = @HOSTNAME

WHERE [Root] = 'Production' AND [Name] = 'Mail Server'

UPDATE [_StringTable] SET [Value] = 'Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=' + @HOSTNAME + ';Port=5555'

WHERE [Root] = 'Production' AND [Name] = 'SmartObject Server'

UPDATE [_StringTable] SET [Value] = 'Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=' + @HOSTNAME + ';Port=5555'

WHERE [Root] = 'Production' AND [Name] = 'ServiceObject Server'

UPDATE [_StringTable] SET [Value] = 'Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=' + @HOSTNAME + ';Port=5555'

WHERE [Root] = 'Production' AND [Name] = 'Category Server'

UPDATE [_StringTable] SET [Value] = 'http://' + @HOSTNAME + ':8081'

WHERE [Root] = 'Production' AND [Name] = 'Web Service URL'

UPDATE [_StringTable] SET [Value] = 'Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=' + @HOSTNAME + ';Port=5252'

WHERE [Root] = 'Production' AND [Name] = 'Workflow Server'

UPDATE [_StringTable] SET [Value] = 'Integrated=True;IsPrimaryLogin=True;Authenticate=True;EncryptedPassword=False;Host=' + @HOSTNAME + ';Port=5555'

WHERE [Root] = 'Production' AND [Name] = 'Workflow Management Server'


-- ** NOTE - The Actioners might need to be updated with new labels??

--select * from _Actioners

The script updates all the relevant tables… it is only needed to change the hostname and the server users (highlighted) accordingly.

Monday, August 3, 2009

Silverlight 3 & RIA Services based K2 View Flow

The customer I'm currently working for asked me to realize a POC of a custom view flow for K2 [blackpearl]. The reason for that was because they wanted to make data provided natively by K2 together with custom data coming from external sources to be exposed as a whole in a graphical business process view flow. In order to do that they needed to understand how easy and fast would have been to write an application that pulls data down from the process engine and shows that data in a graphical format on the web.
For what is concerning the UI I had no choice, they wanted the view flow to be exposed via web so Silverlight was definitively the right answer.
As far as is concerning the K2 flow data access instead, I knew that I had 2 choices:



  • To use the ViewProcessInstance method of SourceCode.Workflow.Client.Connection to retrieve an xml representation of the process instance flow (it is essentially what the OOB ViewFlow does under the hood)

  • To access directly the K2ServerLog database in order to extract the information I needed

Even if the first choice should have been the right one for many reasons:


  • Coding over data provided from a documented API should be faster than reading the same data from a partially documented database

  • What would happen at my code if K2ServerLog changed due to a K2 hotfix?

  • Why do not follow the same path that SourceCode itself followed?

I finally decided for the second one because:


  • I saw that accessing K2ServerLog directly performs better than calling K2 server to retrieve the same data

  • Accessing K2ServerLog directly instead of calling K2 server doesn't load K2 server

  • The K2ServerLog schema is not changed frequently… the actual one has pretty much the same structure it had in K2.NET 2003

  • K2ServerLog tables and schemas are documented in the K2 [blackpearl] official documentation

  • K2ServerLog is the logging - not transactional – database, thus accessing it should not disrupt K2 Server execution performances

  • I had few time to realize my POC and I knew well the structure of K2ServerLog because I already studied it before for other purposes

  • I wanted to try RIA Services in a real scenario… J

I started defining constraints (keys, foreign keys, relationships…) on a copy of K2ServerLog database (K2ServerLog itself comes with no constraints). This would have helped LINQ to SQL to create entities and associations in the appropriate manner. Obviously constraints were only needed for development and were not meant to be used at runtime. What I obtained finally is the following (I extracted below only the part that is relevant for my View Flow):





After that I created a new Silverlight Application and I enabled .NET RIA Services integration:







I added a new LINQ to SQL Classes item and I followed the wizard to make it pointing to my modified K2ServerLog database and to include the desired tables.

I finally obtained the ERM for the K2ServerLog database:






I added a new Domain Service Class item (including metadata classes). I added a custom method to the domain service in order to get a process instance and all the related entities given its id:


public _ProcInst Get_ProcInstById(Int32 procInstId)
{
DataLoadOptions loadOptions = new DataLoadOptions();

// ProcInst
loadOptions.LoadWith<_ProcInst>(p => p._Proc);
loadOptions.LoadWith<_ProcInst>(p => p._ActInsts);

// Proc
loadOptions.LoadWith<_Proc>(p => p._Acts);
loadOptions.LoadWith<_Proc>(p => p._Lines);

// Act
loadOptions.LoadWith<_Act>(a => a._Events);

// Line
loadOptions.LoadWith<_Line>(l => l._Act);
loadOptions.LoadWith<_Line>(l => l._Act1);
loadOptions.LoadWith<_Line>(l => l._LineInsts);

this.Context.LoadOptions = loadOptions;

_ProcInst procInst = (from o in Context._ProcInsts
where o.ID == procInstId
select o).First();

return procInst;
}

I decorated with the Include attribute the same properties in the appropriate metadata classes:


internal sealed class _LineMetadata
{
// Metadata classes are not meant to be instantiated.
private _LineMetadata()
{
}

[Include]
public _Act _Act;

[Include]
public _Act _Act1;

[Include]
public EntitySet<_LineInst> _LineInsts;

And that's it; I was ready to code in Silverlight!!!

And that's the result… I know it's still not too rich but I haven't spent too much time playing with effects:


K2 [blackpearl] Transactional Model



Being inspired by K2.NET 2003 documentation (see KB000066 for more details) and led by a direct experience I decided to put down this post about the transactional model that lies behind K2 [blackpearl].




Transaction Processing



When you purchase an item from an online store, you exchange money (in the form of credit) for the item. If your credit is good, a series of related operations ensures that you get the item and the store gets your money. However, if a single operation in the series fails during the exchange, the entire exchange fails. You do not get the item and the store does not get your money.



The technology responsible for making the exchange balanced and predictable is called transaction processing. Transactions ensure that data-oriented resources are not permanently updated unless all operations within the transactional unit complete successfully. By combining a set of related operations into a unit that either completely succeeds or completely fails, you can simplify error recovery and make your application more reliable.
A transaction is a set of related tasks that either succeed or fail as a unit. In transaction processing terminology, the transaction either commits or aborts. For a transaction to commit, all participants must guarantee that any change to data will be permanent. Changes must persist despite system crashes or other unforeseen events. If even a single participant fails to make this guarantee, the entire transaction fails. All changes to data within the scope of the transaction are rolled back to a specific set point.

Automatic transaction processing is a service provided by COM+ that enables you to configure a class at design time to participate in a transaction at run time. To use this service, the class must derive directly or indirectly from the System.EnterpriseServices.ServicedComponent class.



Writing Serviced Components




A serviced component is the mechanism that enables COM+ services to be available to .NET Framework classes. You can modify any Common Language Specification (CLS)–compliant class to use COM+ services. The System.EnterpriseServices namespace provides custom attributes and classes for accessing these services from managed code.
If you are new to writing serviced components take a look here.
If you are new to transaction isolation level in COM+ take a look here.




Enable Root Transactions in K2 [blackpearl] Server Events



In order to make a K2 Server Event working in a transactional manner you need to configure this behaviour both at the Activity and Event level. It is as simple as checking two check boxes in the General Properties Wizard for the Activity and one of the contained Server Events:







There is only a rule to bear in mind: a transactional Activity can contain only a single Server Event marked as transactional. This basically means that the transaction context does not flow across different Server Events within the same Activity. Missing this requirement may result in unexpected behaviour (like the process halting in running state indefinitely).
What happens behind is that K2 [blackpearl] registers on the fly a transactional serviced component (K2STXH). This component is used this component as the root of the transaction when executing via reflection the code contained within the transactional Server Event.
The aforementioned K2 transactional server component presents the following characteristics:



  • TransactionOption is RequiredNew: it means that a new transaction is created (the component is the root of the transaction)

  • IsolationLevel is ReadCommitted




Writing Serviced Components for K2 [blackpearl]



You can write transactional serviced components and enlist them in a transaction originated from the K2 Server Event.
In order to allow your serviced components to be enlisted in a transaction started from a K2 [blackpearl] Server Event you have to:



  • Configure the TransactionOption as Required or, at least, Supported. This is because the serviced component that K2 uses as the root of the transaction has its transaction option configured as RequiredNew.Marking your serviced components as RequiredNew will generate a new transaction while marking them as NotSupported will make them not to take part to the transaction

  • You cannot configure them with a transaction isolation level higher than ReadCommitted. This is because the serviced component that K2 uses as the root of the transaction has its isolation level configured as ReadCommitted