Skip to main content

Versioning - Ruby SDK

This page shows how to do the following:

Introduction to Versioning

Because we design for potentially long running Workflows at scale, versioning with Temporal works differently. We explain more in this optional 30 minute introduction:

Use the Ruby SDK Patching API

In principle, the Ruby SDK's patching mechanism operates similarly to other SDKs in a "feature-flag" fashion.

To understand this, you can break it down into three steps, which reflect three stages of migration:

  • Running PrePatchActivity code while concurrently patching in PostPatchActivity.
  • Running PostPatchActivity code with deprecation markers for my-patch patches.
  • Running only the PostPatchActivity code.

Let's walk through this process in sequence.

Suppose you have an initial Workflow version using PrePatchActivity:

class MyWorkflow < Temporalio::Workflow::Definition
def execute
result = Temporalio::Workflow.execute_activity(
PrePatchActivity,
start_to_close_timeout: 100
)

# ...
end
end

Now, you want to update your code to run PostPatchActivity instead. This represents your desired end state.

class MyWorkflow < Temporalio::Workflow::Definition
def execute
result = Temporalio::Workflow.execute_activity(
PostPatchActivity,
start_to_close_timeout: 100
)

# ...
end
end

Problem: You cannot deploy PostPatchActivity directly until you're certain there are no more running Workflows created using the PrePatchActivity code, otherwise you are likely to cause a nondeterminism error.

Instead, you'll need to deploy PostPatchActivity and use the patched method to determine which version of the code to execute.

Implementing patching involves three steps:

  1. Use patched to patch in new code and run it alongside the old code.
  2. Remove the old code and apply deprecate_patch.
  3. Once you're confident that all old Workflows have finished executing, remove deprecate_patch.

Patching in new code

Using patched inserts a marker into the Workflow History.

During replay, if a Worker encounters a history with that marker, it will fail the Workflow task when the Workflow code doesn't produce the same patch marker (in this case, my-patch). This ensures you can safely deploy code from PostPatchActivity as a "feature flag" alongside the original version (PrePatchActivity).

class MyWorkflow < Temporalio::Workflow::Definition
def execute
if Temporalio::Workflow.patched('my-patch')
result = Temporalio::Workflow.execute_activity(
PostPatchActivity,
start_to_close_timeout: 100
)
else
result = Temporalio::Workflow.execute_activity(
PrePatchActivity,
start_to_close_timeout: 100
)
end

# ...
end
end

Understanding deprecated patches in the Ruby SDK

After ensuring that all Workflows started with PrePatchActivity code have finished, you can deprecate the patch.

Deprecated patches serve as a bridge between PrePatchActivity and PostPatchActivity. They function similarly to regular patches by adding a marker to the Workflow History. However, this marker won't cause a replay failure when the Workflow code doesn't produce it.

If, during the deployment of PostPatchActivity, there are still live Workers running PrePatchActivity code and these Workers pick up Workflow histories generated by PostPatchActivity, they will safely use the patched branch.

class MyWorkflow < Temporalio::Workflow::Definition
def execute
Temporalio::Workflow.deprecate_patch('my-patch')
result = Temporalio::Workflow.execute_activity(
PostPatchActivity,
start_to_close_timeout: 100
)

# ...
end
end

Safe Deployment of post_patch_activity

You can safely deploy PostPatchActivity once all Workflows labeled my-patch or earlier are finished, based on the previously mentioned assertion.

class MyWorkflow < Temporalio::Workflow::Definition
def execute
result = Temporalio::Workflow.execute_activity(
PostPatchActivity,
start_to_close_timeout: 100
)

# ...
end
end