|
Dear all,
I have a working StateMachineWorkflow hosted under asp.net 2.0.
I have written a simple routine (shown below) to interrogate the current state of a workflowinstance:
public string WfCurrentStateDescription(string WorkflowInstanceId) { // Get the workflowRuntime which was started in global.asax WorkflowRuntime wfRuntime = (WorkflowRuntime)Application[WorkflowRuntimeKey];
StateMachineWorkflowInstance wfInst = new StateMachineWorkflowInstance(wfRuntime, new Guid(WorkflowInstanceId)); return wfInst.CurrentState.Description; }
I find that, once I have called this on a workflowinstance, that instance gets "stuck" in the current state and never transitions to the next state, even though code appears to get called as though state transitions were happening.
Has anyone else seen this? I have no idea why it should happen, and no idea what to do about it.
The code looks so simple that I can't see what I could be doing wrong to cause this disastrous error.
Trevor | | Trevor E Hilder | Hi,
it's very strange indeed; this code is used in Tom Lake's Aspnetstatemachineworkflow.exe and that works very well in RC4. I've just have tested it now.
Are you using the ManualWorkflowSchedulerService ?
Serge Luca; Guidance Belgium; blog: www.redwood.be | | Serge Luca | Dear Serge,
I am not using the ManualWorkflowSchedulerService, because it is not appropriate for the workflows which I am creating.
Once created, these have to operate independently of the thread of execution of web pages, since they have Delay activities in them, which fire off emails at specified intervals of time.
If I use the ManualWorkflowSchedulerService, the Delays never fire the events to send the emails.
Could it be that StateMachineWorkflowInstance doesn't work properly in this scenario?
Trevor | | Trevor E Hilder | Hi Trevor,
1?I've tested Tom sample with both Manual and DefaultSchedulerService, but that works (haven't tested the delay activities, however)
2?By the way, you can use manualWorkflowSchedulerService and specify useactivetimers=true (in the constructor) to have the best of both worlds.
Serge
Serge Luca; Guidance Belgium; blog: www.redwood.be | | Serge Luca | Dear Serge,
Thanks for taking the trouble to check this out.
I note in Tom Lake's blog, to which you referred me, he says:
"If you are using a state machine workflow in ASP.NET you need to use the ManualSchedulerService."
But why is this? Is it really true that "you need to" use it?
Is there a clear explanation anywhere as to when the DefaultSchedulerService is appropriate and when the ManualSchedulerService is?
Unless I have misunderstood this, ManualSchedulerService causes the workflow to run in the same execution thread as the host which launches it, whereas DefaultSchedulerService causes it to run under its own thread.
The latter looks most appropriate where the workflow needs to be launched from code in a web page, but then needs to operate autonomously, which is the case in my application.
I am going to build Tom Lake's sample app, then try to reproduce the trouble I am having with StateMachineWorkflowInstance. I still can't see why it is causing me so much trouble.
Trevor | | Trevor E Hilder | <<Unless I have misunderstood this, ManualSchedulerService causes the workflow to run in the same execution thread as the host which launches it, whereas DefaultSchedulerService causes it to run under its own thread>>
it's correct : the DefaultSchedulerService can be used in asp.net ; for scalabilty reasons, the WF team provides the ManualSchedulerService. But we're using beta (or RC) code, so in your case it's worth the while to test with both if you face some unexpected behaviour.
More infos:
http://blogs.msdn.com/pandrew/archive/2006/04/28/586181.aspx http://blogs.msdn.com/advancedworkflow/archive/2006/04/25/583392.aspx
Serge Luca; Guidance Belgium; blog: www.redwood.be | | Serge Luca | Dear Serge,
Thanks for another very prompt response.
When I started work with WinWF, I struggled to decide where to host my workflows, and ended up deciding to do so in asp.net, using SimpleReadWriteActivities with a LocalServiceContract (obtained from one of the Hands-On Labs, I think), to talk between the web pages and the workflows.
I am beginning to suspect that this is not the best solution for what I am trying to do, which might be why I am running into trouble.
........
Further thought:
Having implemented ManualWorkflowSchedulerService, I am now satisfied that the current hosting in asp.net will be fine for the job. There is no need to change this to be hosted in a Windows service.
Trevor | | Trevor E Hilder | Dear Serge,
I have successfully changed my workflow app to use ManualSchedulerService now. I had misunderstood how it works, but have now resolved the issues.
However, when I call StateMachineWorkflowInstance to obtain CurrentState, it still freezes the workflow instance in its current state.
I am now going to try out Tom's sample to see if I get the same trouble with it.
I am completely baffled here - it clearly has nothing to do with which scheduler service I am using.
Trevor | | Trevor E Hilder | Dear Serge and other interested parties,
I have no trouble with Tom Lake's sample app, but this is no surprise, given that it has no persistence or tracking service running.
I am going to try adding these to Tom's sample, to see if it goes wrong in the same way as mine.
Trevor | | Trevor E Hilder | Dear Serge & others,
I have now reproduced the issue in Tim Lake's sample app.
Here is how to do it:
1) Install the app as usual.
2) Edit web.config, replacing the WorkflowRuntime section with:
< WorkflowRuntime Name="WorkflowServiceContainer">
< CommonParameters>
< add name="ConnectionString" value="Data Source=.\SQLEXPRESS;database=ASPNETStateMachineWorkflow;Integrated Security=True;"/>
</ CommonParameters>
< Services>
< add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UseActiveTimers="true"/>
< add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" ownerShipTimeoutSeconds="10" LoadIntervalSeconds="10"/>
< add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
< add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</ Services>
</ WorkflowRuntime>
3) Create a SQLExpress database with the name ASPNETStateMachineWorkflow and populate it with the tracking and persistence tables using the standard scripts.
Now run the app. If you take a workflow instance through its lifecycle rapidly, everything works fine, but if you create an instance, wait 10 seconds for it to persist, then continue, the workflow state freezes and the app crashes.
This is a very nasty bug - a workaround or fix would be much appreciated!
Trevor | | Trevor E Hilder | I have identified a workaround for this bug. If the SqlWorkflowPersistenceService is added without attributes ownerShipTimeoutSeconds="10" LoadIntervalSeconds="10" everything works fine.
Does anyone know what the default values for these are? It does not appear to be documented anywhere.
Also, are there any recommendations for what these values ought to be? The documentation doesn't actually explain what they do.
Any explanation of their precise function would be most welcome.
Trevor | | Trevor E Hilder | Something to do with locking.
Locking duration depends on the thirth and fourth parameter of SqlWorkFlowPersistence constructor; for instance in the following case: new SqlWorkflowPersistenceService (connectionString,true, new TimeSpan(0,1,0),new TimeSpan(0,0,5)); your workflow will remains locked for 1 minute; the system will check the db every 5 seconds. For example: app1 starts and the first activity is an DelayActivity
=>the workflow is persisted and unloaded;
if we take a look at the stateinstance table of our persistence db, will notice that the ownerID is null =>the workflow is unlocked.
After the idle period the workflow is automaticallly reloaded.
Now if we take a look at the stateinstance table , we will notice that the ownerID is not null and that the ownedUntil column as been set to now + 1 minute.SqlPersistence services takes care of setting the ownerid.
By using SQLProfiler while the workflow is in the database, we can also notice that the stored procedure RetrieveExpiredTimerIds checks the StateInstance table every 5 seconds (Indeed : we 've specified TimeSpan(0,0,5)). If another host application (app2) tries to load a locked workflow , an WorkflowOwnerException will be triggered. In summary: 1?workflow unloaded: workflow unlocked in the persistence store. 2?workflow reloaded: workflow locked in the persistence store. 3?locking happens in the StateInstance table of your persistence DB
I haven't tested Tom sample with persistence & tracking service; I will test it tomorrow.
Serge
Serge Luca; Guidance Belgium; blog: www.redwood.be | | Serge Luca | Dear Serge,
Thanks for the explanation, which is really useful.
Do you know what the default values for these two parameters are?
Trevor | | Trevor E Hilder | Dear Serge,
I have been experimenting with Tom Lake's app, modified to include tracking and persistence, as I previously described.
The bad news is that it is unstable if SQLWorkflowPersistenceService is added without the ownerShipTimeoutSeconds and LoadIntervalSeconds attributes.
I am finding that the WorkflowRuntime_WorkflowCompleted handler gets fired at random, when the workflow has not in fact run to completion.
This thing appears to be a bit of a mess!
Trevor | | Trevor E Hilder | Dear Serge,
I just found out why the program fires the WorkflowRuntime_WorkflowCompleted event apparently at random, then crashes.
This is because Tom has put a delay activity in OrderOpenState, so that it times out after 10 seconds. When this fires, there is a bug whereby the program crashes.
I had not noticed this, because the Delay never fires when using the ManualWorkflowSchedulerService, unless you include the attribute UseActiveTimers="true". This is only alllowed if the SqlWorkflowPersistenceService is added.
So, this is a big red herring! I am just sorting this out, so that I can see whether I am still left with any issues in the program.
Trevor | | Trevor E Hilder | Dear Serge,
In Tom Lake's program, having removed the time-out which caused the program to crash, everything works perfectly, as long as the attribute ownerShipTimeoutSeconds="10" is removed.
If that is included, the CurrentState gets "stuck" as I originally described.
There is certainly a bug in here, but removing ownerShipTimeoutSeconds="10" seems to cure it.
Trevor | | Trevor E Hilder | If the ownerShipTimeoutSeconds atttribute is not specified, and LoadIntervalSeconds="10" is specified, Tom's app seems to work ok.
Unfortunately, my app does not work reliably with the same settings. It still looks as though using StateMachineWorkflowInstance to obtain CurrentState causes the persistence mechanism to become unreliable.
The most common symptom seems to be that datapipes for the simple data access mechanism do not get restored correctly when a persisted workflow is rehydrated.
Is this likely to have been fixed in .NET framework 3.0 RC1, which was released on 1st September?
Trevor | | Trevor E Hilder | I have tried my app rebuilt under CR1, and it exhibits the same unreliability which I described above.
There is still a nasty bug here.
Hasn't anybody else run into this, or am I the only person trying to do this?
Serge, did you ever reproduce the problem in Tom's sample app? You said that you were going to try it.
I am going to try submitting a bug report about this, since it appears to have been ignored by Microsoft so far.
Trevor | | Trevor E Hilder | Hi Trevor,
I'll try it tonight (tonight in Belgium ;) I was busy on another project...
Serge
Serge Luca; Guidance Belgium; blog: www.redwood.be | | Serge Luca | Hi Trevor,
I've tested Tom's application this morning (in a hurry).
I've used RC1; I've created the persistence DB from scratch, and I've added the persistence service in global.asax :
.Runtime.Hosting.WorkflowPersistenceService persistenceService = new System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService( "Initial Catalog=SqlPersistenceService;Data Source=localhost\\sqlexpress;Integrated Security=SSPI;", true, TimeSpan.MaxValue, new TimeSpan(0, 0, 10)); workflowRuntime.AddService(persistenceService);
You'll notice that I've specified an unlimited ownership period.
I've run it, and that works ; the app retrieve and display the current state.
If yout set "n" seconds for the ownership period, and if you wait more than "n" sec, you get an error (duration has expired) that's normal.
Still have to try with the tracking service,however.
I've you have a problem, set the trace on the persistence DB with SQL profiler and analyse the queries; that helps.
Serge
Serge Luca; Guidance Belgium; blog: www.redwood.be | | Serge Luca |
|