Adding a New Version of a Workflow or Custom Activity in Workflow Manager 1.0

 

Updated: October 5, 2012

As you develop your workflows, you might often find a need to change an activity definition either because of a bug that needs to be fixed or because of changing business requirements. In either case, you might want to create a new revision of your activity definition or workflow definition that you had published earlier. This topic provides an overview of the versioning capabilities provided by Workflow Manager 1.0 and how those capabilities apply to your scenarios.

Workflow Versioning Overview

Workflow Manager 1.0 lets you update your activity and workflow definitions. While you may be just updating an activity or workflow definition, this simple update could internally trigger a revisioning mechanism. For example, if you had LoanProcessing workflow that used an EligibilityCheck activity, and you wanted to fix a bug in the EligibilityCheck activity, you would simply update this activity with the new definition. But, what should happen to existing workflows that reference this activity? Should they use the old definition or the new definition? Which implementation would existing instances use? Which implementation would new instances use?

Similarly, you might want to update the LoanProcessing workflow to include an additional approver step for loans above $500K. Even here, you would have questions such as: what would happen to existing instances? Would they use the new definition or the old definition? What version would new instances use?

The sections in this topic describe how to implement these scenarios and provide an overview of the versioning features in Workflow Manager 1.0.

Workflow Revisions

Let's take a look at a workflow revision scenario. You have a workflow named LoanProcessing. LoanProcessing contains an EligibilityCheck activity. You have already published the EligibilityCheck activity and the LoanProcessing workflow to Workflow Manager 1.0. Now, you have to make a revision to the LoanProcessing workflow to fix a bug in the workflow logic. After making the required changes, you can call the PublishWorkflow method on the WorkflowResourceManager class (or use HTTP PUT on the workflow resource if you are calling the REST API directly). As part of this operation, Workflow Manager 1.0 internally creates a new version of this workflow with the definition passed in as part of this call. Depending upon the nature of your bug fix, you have the option to either terminate existing instances (which use the older definition) or let them continue to run using the older definition with which they were started. To publish a workflow, you can use the PublishWorkflow method. The following example publishes the LoanProcessing workflow which uses the EligibilityCheck as its implementation.

WorkflowDescription wfDesc = new WorkflowDescription();  
wfDesc.Name = "LoanProcessing";  
wfDesc.ActivityPath = "EligibilityCheck";  
Uri wfUri = client.Workflows.Publish(wfDesc);  
  

If you decide to terminate existing instances, you could call the PublishWorkflow method with terminateActiveInstances set to true. If you set it to false or call the overload of PublishWorkflow method that does not have a terminateActiveInstances parameter, which internally specifies a value of false, existing instances would continue to run using the definition that they were instantiated with. This would be appropriate for scenarios in which the bug fix is incremental in nature. For example, if there is a change in the LoanProcessingWorkflow, but this process change needs to take effect for new loan application only, and if it is ok for existing loan applications to use the old version of the process (as that is what you had communicated to existing loan customers), in such a situation, you could set terminateActiveInstances to false. In the previous example, the overload of PublishWorkflow that did not take a terminateActiveInstances parameter was called, and so existing instances would not be terminated.

If the bug fix is critical and you wish existing instances to be terminated – for example, if your loan processing workflow accidentally approved all applications, you could update the definition with terminateActiveInstances set to true. The process of terminating the active instances is asynchronous and hence is not guaranteed to be instantaneous, so there is a possibility of one or more instances continuing to run until they are terminated eventually. Note that even though you have multiple revisions of your workflow, only the latest version is used to create new instances. Similarly, when you make a GetWorkflow call for this workflow, you only get to see the latest workflow’s definition.

Activity Revisions

Extending the same loan approval process example mentioned above, what if you have to make a revision to the EligibilityCheck activity to take into account a change in a business requirement. You have made the necessary changes to this activity and now you want to publish this new definition for EligibilityCheck. To do this you can call the PublishActivity method on the client (or HTTP PUT with the Activity’s Resource URI). As a result of this call, all activities and all workflows that use this activity (directly or indirectly) would be updated internally to use a new version, and new instances of any workflows that use this activity (directly or indirectly) would start using the new definition.

Just like how you have a choice of terminating existing active instances with updating a workflow, you have a similar choice while updating an activity as well. However, this choice has a potentially far greater impact on the system. To understand this, we have to recollect that workflows can reference activities (at least one activity) and activities can in turn reference zero or more activities. So, a particular activity could be referenced by zero or more activities and those activities could be referenced by zero or more workflows. This means that a particular activity could be referenced by many workflow definitions directly or indirectly.

When updating an activity, you have the same decision as when updating a workflow. Should all the instances of all the workflows (that are dependent on the activity) be terminated as part of this update operation, or should they continue to run with the previous activity definition. This behavior is determined by the terminateDependentInstances parameter. If you call the overload that does not take a terminateDependentInstances parameter, the dependent instances are not terminated and will use the old version of the activity. In the following example, a TestActivity is published, and dependent instances are not terminated.

Activity newActivity = new Sequence  
{  
     ...  
};  
  
XElement activityXaml = XElement.Parse(XamlServices.Save(newActivity));  
  
ActivityDescription activityDesc = new ActivityDescription();  
activityDesc.SetXaml(activityXaml);  
  
activityDesc.Name = "TestActivity";  
client.Activities.Publish(activityDesc);  

When an existing activity definition is updated, Workflow Manager 1.0 does the following:

  1. Creates a new version for this activity using the provided definition.

  2. Updates all workflow definitions referencing this activity to use the new version of this activity.

  3. If the call was made with terminateDependentInstances set to true, then all the instances of all workflows referencing this activity would be terminated.

Note

In the previous list, the first step is done synchronously as part of the call to update the activity definition, and then the following steps are done asynchronously. This means, even though you had updated a particular activity successfully, it could take a while (of the order of a few minutes) for this change to be applied to all the workflows referencing this activity. So, in the meantime, there is a possibility of those workflows using the older definition. However, once this asynchronous process completes all workflows referencing this activity will use the latest definition of this activity.

Note

Even though you have multiple revisions of your activity, only one of them is active at any point in time. The other revisions are maintained internally for supporting existing instances of workflows referring to this activity. Hence, when you make a Get call for this activity, you only get to see the latest activity definition.

Deleting Workflows

There might be situations where you might want to delete a particular workflow, if it is no longer needed. In the case of the LoanProcessing workflow, suppose your organization stopped processing that particular type of loan. You may want to delete this workflow to ensure that no instances are being triggered accidentally. To delete a workflow, you can call the DeleteWorkflow method using the client API or call DELETE on the REST endpoint with the Workflow resource’s URI. When deleting a workflow there are two choices with respect to the existing instances. If your business decision has been to let existing loans complete processing but not allow new instances, you can call the following Delete method on WorkflowManager.

public void Delete(string workflowName)  

This overload does not terminate existing instances. Existing instances would continue to be processed until they are completed. However, new instances would not be created as the workflow itself does not exist. So, any message you send to activate new instances of this workflow would not result in an instance of this workflow being activated. But, if your business decision is to terminate existing loan applications as well, then you can use the Delete overload that takes a terminateActiveInstances parameter and specify that parameter to true.

public void Delete(string workflowName, bool terminateActiveInstances)  

This prevents any new instances from starting, and terminates running instances.

Deleting Activities

Under very rare circumstances, it might be required to delete activities as well. Similar to the case of updating activities, many workflows may directly or indirectly reference the activity and be affected by the deletion. You can delete an activity either using the Delete method in the Client API or by calling DELETE on the REST endpoint with the activity resource’s URI. Here, again, you have two choices with respect to the existing instances of workflows that refer to this activity. If you want to let existing instances that directly or indirectly reference the activity to continue to run, you can call the Delete method overload on ActivityManager that does not take a terminateDependentInstances parameter, or you can call the overload that does take this parameter and set it to false.

public void Delete(string activityName)  

This overload does not terminate existing instances of workflows referring to this activity. However, new instances of workflows referring to this activity would not be created as this activity does not exist anymore for new instances. Hence, the runtime would throw exceptions in the log stating that the workflow’s XAML could not be fully computed as one of the activities is missing.

Note the difference in behavior between activity deletion and workflow deletion. When a workflow definition is deleted, any message to activate an instance of the workflow would simply be ignored. However, with activity deletion, all the workflow definitions that are referring to this activity would not be deleted. They would continue to exist and hence they would attempt to process any message that belongs to them. However, they would not be able to locate this activity to execute and hence would throw an exception and terminate the instance. What this means to you is that, even though you have deleted a particular activity, your workflow definition is still available. If you modify your workflow definition logic to not use this deleted activity, new instances would start executing properly. If you want to terminate existing instances when you are deleting an activity, call Delete and set terminateDependentInstances to true. This would ensure that existing instances of workflows referencing this activity would also be terminated.

public void Delete(string activityName, bool terminateDependentInstances)