Versioning - Ruby SDK
This page shows how to do the following:
- Use the Ruby SDK patching API
- Patch in new code safely
- Understand deprecated patches
- Safely deploy
post_patch_activity
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 inPostPatchActivity
. - Running
PostPatchActivity
code with deprecation markers formy-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:
- Use patched to patch in new code and run it alongside the old code.
- Remove the old code and apply deprecate_patch.
- 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