Workflow Ownership Probleme
Eines der mächtigsten Features der WF, der Persistenz Service (SqlWorkflowPersistenceService), erlaubt es einem, den aktuellen Status von Workflow Instanzen in der Workflow DB zu speichern. Microsoft stellt dabei alles, was benötigt wird, zur Verfügung, mindestens für den hauseigenen SQL Server.
Eine Applikation, die beispielsweise auf mehreren Servern gleichzeitig läuft, kann auf dieselbe WF Persistenz Datenbank zugreifen. Dazu wurde das relativ einfache Konzept der Workflow Ownership geschaffen. Dies ist eigentlich nichts anderes als ein einfacher Lock mit einem Timeout.
In der WF Persistenz Datenbank dienen dazu die beiden Felder ownerId und ownedUntil der Tabelle InstanceState:

Aufbau der Tabelle InstanceState
Im Feld ownerId wird die GUID des Persistenz Services der jeweiligen WF Runtime eingetragen, und in ownedUntil steht (im UTC Format!), wie lange die Instanz gesperrt ist.
Wenn diese beiden Felder nicht null sind, wird die entsprechende WF Instanz also gerade von einem Host bearbeitet.
So wird sichergestellt, dass eine Workflow Instanz immer nur von einem Host gleichzeitig ausgeführt wird.
Dies funktioniert grundsätzlich auch prima, der Persistenz Service hat allerdings einige Unschönheiten.
Bei jedem Neustart der WorkflowRuntime wird der Persistenz Service neu initialisiert. Dabei erhält er auch immer eine neu GUID, mit welcher dann die WF Instanzen in der WF DB gelockt werden.
Wenn nun zum Beispiel die Ausführung einer Workflow Instanz abgebrochen wird, bleibt der Lock bestehen.
Nun würde man erwarten, dass die Instanz nach dem Ablaufen des Lock Timeouts von einem anderen Host geladen und weiterbearbeitet werden kann. Leider ist dies nicht der Fall. Der Persistenz Service lädt beim Pollen nach freien Workflow Instanzen nur solche mit abgelaufenem Timer. Diejenigen mit abgelaufenem Lock werden nur beim Start des Services geladen. Solange also der Service nicht neu gestartet wird, kann die Workflow Instanz nicht mehr geladen werden.
Es wäre doch schön, wenn man dem Persistenz Service eine ID zuweisen könnte, damit wäre nämlich das Problem gelöst. In einer Umgebung mit mehreren Hosts, könnte jeder Service seine eigene ID erhalten.
Leider ist dies nicht vorgesehen.
Dem kann man aber abhelfen. Nach einem kurzen Blick auf die Implementation des SqlWorkflowPersistenceService im Reflector kann man dank der in C# 3.0 eingeführten Extension Methods den Service wie folgt erweitern:
static class WorkflowExtensions
{
public static void SetServiceInstanceId(this WorkflowPersistenceService workflowPersistenceService, Guid serviceId)
{
Type persistenceServiceType = workflowPersistenceService.GetType();
FieldInfo instanceIdField = persistenceServiceType.GetField("_serviceInstanceId",
BindingFlags.NonPublic |
BindingFlags.Instance);
instanceIdField.SetValue(workflowPersistenceService, serviceId);
}
}
Die Service ID wird einfach via Reflection gesetzt. Verwendung auf eigene Gefahr, bei mir hat’s jedenfalls hervorragend funktioniert.

29. September 2009 um 20:53
[...] habe ich einen kleinen, aber nützlichen Hack gezeigt, mit welchem man die Service ID des Persistenzdienstes setzen kann. Dies funktioniert auch ganz prima, solange man nicht mit Workflow Services [...]