locked
Resume workflow and WaitForEvents timed out RRS feed

  • Question

  • I have a winform that I use to kick off a workflow - in the workflow is a delay that put it to sleep for 1 minute.  I am trying to load up the unloaded workflow in another program (service) that will resume any workflows that are ready to wake up for action.  It is not working - I keep getting the error "A WaitForEvents or BeginWaitForEvents operation timed out after 00:00:00. The time allotted to this operation may have been a portion of a longer timeout." 

    My code to kick off the workflow:

     public partial class Bookmark : Form
     {
      WorkflowApplication wfApp;
      InstanceStore store;
    
      public Bookmark()
      {
       InitializeComponent();
      }
    
      private void btnStart_Click(object sender, EventArgs e)
      {
       AutoResetEvent syncEvent = new AutoResetEvent(false);
    
       wfApp = new WorkflowApplication(new Approval());
       wfApp.PersistableIdle = WorkflowPersistableIdle;
       
       wfApp.InstanceStore = new System.Activities.DurableInstancing.SqlWorkflowInstanceStore(System.Configuration.ConfigurationManager.ConnectionStrings["wf"].ConnectionString);
       
       wfApp.Run();
       syncEvent.Set();
      }
    
      private PersistableIdleAction WorkflowPersistableIdle(WorkflowApplicationIdleEventArgs e)
      {
       return PersistableIdleAction.Unload;
      }
    //etc.

     

    And the code that will check if any unloaded workflows need to wakeup:

     

    public void ResumePendingFlows()
      {
       InstanceStore store = new SqlWorkflowInstanceStore(ConfigurationManager.ConnectionStrings["wf"].ConnectionString);
       InstanceHandle handle = store.CreateInstanceHandle();
       InstanceOwner owner = store.Execute(handle, new CreateWorkflowOwnerCommand(), TimeSpan.MaxValue).InstanceOwner;
       
       store.DefaultInstanceOwner = owner;
    
       bool hasRunnableWorkflows = false;
    
    // here is where it blows up:
    //A WaitForEvents or BeginWaitForEvents operation timed out after 00:00:00. The time allotted to this operation may have been a portion of a longer timeout.
    
       foreach (InstancePersistenceEvent currentEvent in store.WaitForEvents(handle, TimeSpan.Zero))
       {
        if (currentEvent == HasRunnableWorkflowEvent.Value)
        {
         hasRunnableWorkflows = true;
         break;
        }
       }
    
       if (hasRunnableWorkflows)
       {
        Console.WriteLine("Found runnable workflows");
        //WorkflowApplication app = new WorkflowApplication( .... ); 
        //app.InstanceStore = store; 
        //app.LoadRunnableInstance(); 
        //app.Run(); 
       }
       else
       {
        Console.WriteLine("Did not find runnable workflows");
       }
      }
    
    Any help is appreciated - I'm sure I'm missing a huge concept. :(

     

     

    Thursday, February 24, 2011 9:46 PM

Answers

  • Thank you to everyone who helped me with this.  Although I wasn't able to get it working via the aforementioned code [my environment is a touch bit different than a vanilla setup] I was able to very easily roll my own windows service to poll the database and resume workflows.  Thanks for everyones insight on this and other posts, and thanks for making Wf4 easy to work with!

    John

    • Marked as answer by John Hennesey Thursday, May 5, 2011 1:25 PM
    Thursday, May 5, 2011 1:25 PM

All replies

  • I'm guessing by the silence either I missed a big concept or I am doing something completely wrong.  Does anyone know of an example that I can play with where one project starts a workflow, unloads it at persistableidle time, and another comes along and wakes it up?  I see lots of examples of it happening within the same project, but none separate.

    Thanks,

    John

    Friday, February 25, 2011 6:22 PM
  • Hi John,
    Sounds like maybe it is an issue with the timeout either the one configured for unload after idle, or wherever the lock acquisition timeout is configured, check out this thread for some more background info:

    "Using delay activity with WorkflowApplication"

    Tim

    Friday, February 25, 2011 11:38 PM
  • Hang on, why are you passing TimeSpan.Zero in here?

    As far as I can see from docs, WaitForEvents wouldn't give you any events except those ones that occur during the timeout period that you are waiting?

    >After raising the HasRunnableWorkflowEvent, the instance store should stop monitoring the persistence store for runnable instances until it receives and executes the TryLoadRunnableWorkflowCommand. The monitoring continues only if the TryLoadRunnableWorkflowCommand execution does not return any runnable instances.

    foreach (InstancePersistenceEvent currentEvent in store.WaitForEvents(handle, TimeSpan.Zero))

    Saturday, February 26, 2011 12:23 AM
  • I am passing in the TimeSpan.Zero as part of the service that will resume workflows.  My design is to start the workflows in my application then unload them once they hit the delay.  Running in parallel is a service that will check for any wf instances that are unloaded and need to take action (the delay expires);  it will load them up and continue their processing.  It is split this way to save on resources, web farm starting the wf's, etc.

    The reason it is a Zero delay is upon starting of the service it should check for any wf's that are ready for action - then continue monitoring.  Should something crash the box can be rebooted and continue along.  Now why my code is wrong is I am new to WF4; I see lots of examples on how to persist workflows, unload them, reload them, but all within the same application.  I want this to be 2 completely separate app's.  I got the piece of code from Adry's site (http://adrianot75.wordpress.com/2010/11/04/wf4-persisnce-delay-and-execution-resume/) - very good stuff, but I am trying to piece things together.  I haven't found tons of examples / documentation on this concept, hence here I am on the forums. :)

    Any help is much appreciated.

    Tuesday, March 1, 2011 9:13 PM
  • Hi John, yes Tim is right, the Timeout itself is related to the Zero argument. 

    Altough passing 0 means give me the events that are expired, and if no one is expired dumpa TimeoutException... you may want to increase this value to avoid having too many exceptions that in turn lead to poor performance. 

    From my point of view this Timeout exception is not so good at all because it's not an error but simply a "no events" result... but only my 2 cents.

    Actually the code I am using in VB.NET is the following where I set 5 seconds. This is working fine on a service that runs 24x7 for 3 months till now.

     

     Private Sub ResumeRunnableWFThread()
     mResumeRunnableWFThreadExit.Reset()
     mResumeRunnableWFThreadExited.Reset()
    
     Dim apmInvoker As New APMInvoker()
    
     Dim store As SqlWorkflowInstanceStore = apmInvoker.GetInstanceStore()
    
     Dim instanceOwner As InstanceOwner = Nothing
     Dim temporaryhandle As InstanceHandle = Nothing
    
     Do
      Try
      If mResumeRunnableWFThreadExit.WaitOne(TimeSpan.Zero) Then Exit Do
    
      ' Need an handle
      If temporaryhandle Is Nothing OrElse Not temporaryhandle.IsValid Then
       If Not temporaryhandle Is Nothing Then
       FreeHandleAndDeleteOwner(store, temporaryhandle)
       End If
    
       LogWrapper.Current.Warn("HANDLE for persistence is found INVALID. Create a new one...")
    
       ' build the handle first time/again (when invalid)
       temporaryhandle = store.CreateInstanceHandle()
       instanceOwner = store.Execute(temporaryhandle, GetCreateOwnerCommand(), TimeSpan.MaxValue).InstanceOwner
       store.DefaultInstanceOwner = instanceOwner
      End If
    
      Dim eventList As List(Of InstancePersistenceEvent) = store.WaitForEvents(temporaryhandle, TimeSpan.FromSeconds(5))
      Dim foundEvent As InstancePersistenceEvent = Nothing
    
      For Each currentEvent As InstancePersistenceEvent In eventList
       If currentEvent = HasRunnableWorkflowEvent.Value Then
       foundEvent = currentEvent
       Exit For
       End If
      Next
    
      If Not foundEvent Is Nothing Then
       ' found one... try to resume instance.      
       Dim loadHandle As InstanceHandle = store.CreateInstanceHandle(instanceOwner)
       Dim instanceView As InstanceView = Nothing
    
       Try
       instanceView = store.Execute(loadHandle, New TryLoadRunnableWorkflowCommand(), TimeSpan.FromSeconds(5))   
       Catch ex As InstanceNotReadyException
       ' check next time...
       LogWrapper.Current.Warn("Instance is NOT ready... try again")
       Finally
       loadHandle.Free()
       End Try
    
       If Not instanceView Is Nothing Then
       If instanceView.InstanceId <> Guid.Empty AndAlso instanceView.InstanceDataConsistency = InstanceValueConsistency.None Then
        LogWrapper.Current.InfoFormat("Event={0} for InstanceID={1}", foundEvent.Name, eventList.Count, instanceView.InstanceId)
    
        ' ask to resume.
        apmInvoker.ResumeIdleWorkFlow(instanceView.InstanceId, store)
       Else
        ' no instance.
        LogWrapper.Current.WarnFormat("The Event ""{0}"" returned an unusable instance ""{1}"" with state ""{2}""!", foundEvent.Name, instanceView.InstanceId, instanceView.InstanceDataConsistency)
       End If
       Else
       LogWrapper.Current.WarnFormat("The Event ""{0}"" returned a NULL InstanceView.", foundEvent.Name)
       End If
      Else
       LogWrapper.Current.Info("No events found to resume workflows.")
      End If
    
      Catch ex As System.TimeoutException
      ' not pending workflow needed to resume... try next time
    
      Catch ex As ThreadAbortException
      LogWrapper.Current.Error("Thread aborted request.", ex)
    
      ' quit.
      Exit Do
    
      Catch ex As Exception
      LogWrapper.Current.ErrorFormat("Resume Runnable Thread ERROR: {0}", ex)
    
      ' try again.
      Threading.Thread.Sleep(1000)
      End Try
     Loop
    
     Try
      FreeHandleAndDeleteOwner(store, temporaryhandle)
     Catch ex As Exception
      LogWrapper.Current.Error("Can't delete instance owner!", ex)
     End Try
    
     mResumeRunnableWFThreadExited.Set()
     End Sub
    

    PS: Beware the code above includes some external invoke. I had a more complex issue that is associating the correct workflow definition before resuming. In normal cases this is managed by the LoadRunnableINstance method but this is too simplified in my case: I have many definitions, many verisons of definitions and so on... I need to dynamically discover and resume the correct one.

    HTH,

    Cheers


    Adriano
    Tuesday, March 1, 2011 9:37 PM
  • Hi,

    If you are using WorkflowApplication and host workflow in WinForm app. Since WorkflowApplication instance won't resume a delayed and unloaded workflow instance automaticlly, we'd better use a bookmark activity to resume unloaded workflow.

    If you really have to use Delay activity. the following sample "Long running WF4 host“ could be helpful to you.
    http://xhinker.com/post/WF4.aspx

    Regards


    MSDN Community Support
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    This posting is provided "AS IS" with no warranties, and confers no rights. My Blog: http://xhinker.com
    Microsoft Windows Workflow Foundation 4.0 Cookbook
    Thursday, March 3, 2011 3:22 AM
  •  apmInvoker.ResumeIdleWorkFlow(instanceView.InstanceId, store)

    PS: Beware the code above includes some external invoke. I had a more complex issue that is associating the correct workflow definition before resuming. In normal cases this is managed by the LoadRunnableINstance method but this is too simplified in my case: I have many definitions, many verisons of definitions and so on... I need to dynamically discover and resume the correct one.


    Adriano


    Hi Adriano,

    Are you able to post the ResumeIdleWorkFlow() method? I've just posted a question on resuming multiple different workflows and I think this may be the answer. I'd also be happy to hear and discuss the issues you had about dynamically discovering and resuming the correct workflows.

    Thanks

    • Edited by ShaneSt Monday, March 7, 2011 11:02 PM Reduced quote size
    Monday, March 7, 2011 10:58 PM
  • I am finally back to this - here is what I discovered - please set me straight as I am a bit confused.

    I created a workflow using WorkflowApplication outside of a WCF service [I mention this as WCF does not come into play at all for me].  I also have a 2nd program [the "monitoring" program] that runs the above code to check for any workflows that have an expired delay.  I tried these scenarios:

    1. have the monitoring program running and in the store.WaitForEvents(handle, 30 seconds) method while I kick off a workflow that goes into its delay and the delay expires.  Upon the end of the 30 seconds tt didn't detect any events thus erroring.
    2. have the monitoring program look for events that have already expired (wait for events 0 seconds)
    3. and all sorts of other combinations i won't go into detail.

    If I query the database for any delays that have expired I can manually load up the instance and resume it via the guid.  I understand that the workflowApplication object run outside of a WCF service will not automatically resume expired delays - but I was hoping to use the .net objects to at least do the querying for me.  From what I see either I am doing something wrong or a manual query is required.  ??

    Thanks,

    John

    Friday, April 22, 2011 2:41 PM
  • *bump*

    If anyone has any suggestions I'm all ears... :)  Please see above.

    Tuesday, April 26, 2011 2:48 PM
  • Thank you to everyone who helped me with this.  Although I wasn't able to get it working via the aforementioned code [my environment is a touch bit different than a vanilla setup] I was able to very easily roll my own windows service to poll the database and resume workflows.  Thanks for everyones insight on this and other posts, and thanks for making Wf4 easy to work with!

    John

    • Marked as answer by John Hennesey Thursday, May 5, 2011 1:25 PM
    Thursday, May 5, 2011 1:25 PM
  • Hi,

    What query did you use to fetch the instances from the tables ?


    it's all very nice when it works

    Thursday, April 21, 2016 9:59 AM
  • Good evening, Phillippe_Devaux -

    The query I am using is : 

    select *
    from [System.Activities.DurableInstancing].InstancesTable [it]
    where [it].PendingTimer <= @yourDateTime

    Good luck and let me know if there is anything else I can share.

    John

    Friday, April 22, 2016 2:28 AM