How to Resume Suspended Workflows in .NET WF 4.0

Wednesday, 28 April 2010 05:57 AM
by Coose

So we’ve been on WF 4.0 for a while now, and we have been anxiously awaiting Dublin (AppFabric).  One of the things we really wanted out of AppFabric was the ability to resume workflows that have suspended due to unhandled exceptions.

Well, I discovered that you don’t need AppFabric.  The sql instance store already gives us that functionality…there’s just no GUI for it (that I can find).

So, start by creating the databases.  The scripts are in the framework folder:

C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en\SqlWorkflowInstanceStoreSchema.sql
C:\Windows\Microsoft.NET\Framework\v4.0.30319\SQL\en\SqlWorkflowInstanceStoreLogic.sql

Now, in the service behavior for the workflow xamlx, add some entries:

          <sqlWorkflowInstanceStore connectionStringName="Workflow"

                                    hostLockRenewalPeriod="00:00:05"

                                    runnableInstancesDetectionPeriod="00:00:02"

                                    instanceCompletionAction="DeleteAll"

                                    instanceLockedExceptionAction="AggressiveRetry"

                                    instanceEncodingOption="None" />

          <workflowIdle timeToPersist="00:00:02"

                        timeToUnload="00:00:05"/>

Make sure your connection string name matches your connection string to the database tables you created from the above sql scripts.

One more configuration item to add.  The workflow control endpoint needs to be added to the xamlx workflow service.

<service name="MyWorkflowService"

         behaviorConfiguration="myServiceBehavior">

  <endpoint address=""

            binding="customBinding"

            bindingConfiguration="myBindingConfiguration"

            contract="IMyContract" />

  <endpoint address="wce"

            binding="basicHttpBinding"

            kind="workflowControlEndpoint"/>

</service>

The endpoint with ‘kind=”workflowControlEndpoint”’ (new for .NET 4.0) is the key here.  That creates the workflow control endpoint on your workflow service.

Now, after I get an exception in a workflow (and on my development machine, there has been many), the InstancesTable in the instance store contains the information we need.  Running the sql:

select Id, SuspensionExceptionName, SuspensionReason, ExecutionStatus
from [System.Activities.DurableInstancing].InstancesTable
where IsSuspended = 1

I get these records:

image

So, here I can see the suspended workflows.  What I need from here is the Id.

Now, let’s call the endpoint we created above.  It’s a piece of cake:

You need a reference to System.ServiceModel.Activities v4.  On my machine it’s found at C:\Program Files\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.0\System.ServiceModel.Activities.dll.  This assembly contains the classes WorkflowControlEndpoint and WorkflowControlClient.

Guid workflowId = SelectWorkflowIdSomehow();

 

WorkflowControlEndpoint ep = new WorkflowControlEndpoint(

    new BasicHttpBinding(),

    new EndpointAddress("http://localhost/myWorkflowService.xamlx/wce")

);

WorkflowControlClient client = new WorkflowControlClient(ep);

client.Unsuspend(workflowId);

That’s it.  The WorkflowControlEndpoint does the work, and the workflow is unsuspended and resumed from the last persisted state before the exception happened.  Essentially, everything that happened between un-persistence and the exception is gone, and the previous state is loaded.

Cool, huh?  And you don’t even need AppFabric.  AppFabric does add a nice layer of UI for persistence and tracking, so I’m still looking forward to it! :)

Enjoy.  Or don’t.  Whatever.

Comment on this
Development
|

Workflow Tracing

Tuesday, 14 July 2009 10:58 AM
by Coose

I was having some problems today with my workflows, and I needed to get some more information.  Of course, tracing is the obvious solution.  I didn’t even know where to begin putting trace points in my code, so I thought I would jack up the integrated workflow tracing and see if that gave me any ideas.  Now how to do that?  I just can’t remember.  Google to the rescue!

Of course, you get the documentation sample:

  <system.diagnostics>
    <switches>
      <add name="System.Workflow LogToFile" value="1" />
      <add name="Host" value="Error" />
      <add name="Runtime" value="Warning" />
      <add name="Tracking" value="Info" />
      <add name="Activity" value="All" />
    </switches>
    <trace autoflush="true" indentsize="4">
    </trace>
  </system.diagnostics>

Uh…Ok.  But where does that go?  It goes to a file (WorkflowTrace.log) in your program exe directory.  And it’s plain text.  How to integrate this into the Service Model tracing I already have established for my project?

  <system.diagnostics>
    <switches>
      <add name="System.Workflow LogToTraceListeners" value="1" />
      <add name="Host" value="Error" />
      <add name="Runtime" value="Warning" />
      <add name="Tracking" value="Info" />
      <add name="Activity" value="All" />
    </switches>
    <trace autoflush="true" indentsize="4">
    </trace>
  </system.diagnostics>

Uh…Ok.  But what listeners is it going to?  For me…none.  It’s not explicitly stated, so sometimes I don’t explicitly do it.  We have to manually add a trace listener in the trace configuration section, not sources.  So our configuration becomes:

  <system.diagnostics>
    <switches>
      <add name="System.Workflow LogToTraceListeners" value="1" />
      <add name="Host" value="Error" />
      <add name="Runtime" value="Warning" />
      <add name="Tracking" value="Info" />
      <add name="Activity" value="All" />
    </switches>
    <trace autoflush="true" indentsize="4"

     <listeners>

       <add

         initializeData="c:\logs\test.svclog"

         type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

         name="ServiceModelTraceListener"

         traceOutputOptions="Timestamp">

       </add>

     </listeners>
    </trace>
  </system.diagnostics>

Ok.  One step closer to the edge. But, you can eliminate the LogToTraceListeners line altogether, and use each switch name as a source.  This keeps a more consistent Service Model e2e tracing configuration:

<system.diagnostics>

  <switches>

    <!--<add name="System.Workflow LogToFile" value="1" />

    <add name="System.Workflow LogToTraceListeners" value="1" />-->

    <add name="System.Workflow.Runtime" value="Warning" />

    <add name="System.Workflow.Runtime.Hosting" value="Warning" />

    <add name="System.Workflow.Runtime.Tracking" value="Warning" />

    <add name="System.Workflow.Activities" value="Warning" />

    <add name="System.Workflow.Activities.Rules" value="Warning" />

  </switches>

  <trace autoflush="true" />

  <sources>

    <source name="System.ServiceModel" switchValue="Warning" propagateActivity="true">

      <listeners>

        <add type="System.Diagnostics.DefaultTraceListener" name="Default" />

        <add name="ServiceModelTraceListener" />

      </listeners>

    </source>

    <source name="System.Workflow">

      <listeners>

        <add name="ServiceModelTraceListener" />

      </listeners>

    </source>

    <source name="System.Workflow.Runtime">

      <listeners>

        <add name="ServiceModelTraceListener" />

      </listeners>

    </source>

    <source name="System.Workflow.Runtime.Hosting">

      <listeners>

        <add name="ServiceModelTraceListener" />

      </listeners>

    </source>

    <source name="System.Workflow.Runtime.Tracking">

      <listeners>

        <add name="ServiceModelTraceListener" />

      </listeners>

    </source>

    <source name="System.Workflow.Activities">

      <listeners>

        <add name="ServiceModelTraceListener" />

      </listeners>

    </source>

  </sources>

  <sharedListeners>

    <add initializeData="c:\logs\test.svclog"

      type="System.Diagnostics.XmlWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"

      name="ServiceModelTraceListener"

      traceOutputOptions="Timestamp" />

  </sharedListeners>

</system.diagnostics>

Now, a single .svclog file contains WCF Service Model tracing, as well as Workflow tracing, including activity tracing.

One word of warning: Again, blindly copying and pasting code from other blogs led me to use “Verbose” as the switch value for the workflow switches.  You know what? Nothing traced at all because Verbose is not a valid switch value.  Yeah…I wish I had that 30 minutes of my life back.

The proper switch value for verbose output is All, not Verbose.

Don’t say I didn’t warn you.  It is documented incorrectly out there.

Comment on this
Development
|