-
Notifications
You must be signed in to change notification settings - Fork 50
Patching #524
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Patching #524
Conversation
|
How would I use this patching system to fix a workflow? Manually fork from the step before the patch? |
| # Globally set the application version and executor ID. | ||
| # In DBOS Cloud, instead use the values supplied through environment variables. | ||
| if not os.environ.get("DBOS__CLOUD") == "true": | ||
| if self.enable_patching: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we don't need this config. Because users should be able to use patching + versioning together. This code forces the version to be a weird PATCHING_ENABLED string which is not intuitive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This makes behavior much more intuitive. Patching requires a static version string. This check provides clean errors if you don't have one. You can still override it if you really want to. Otherwise you'll be thinking you're using patching, but your workflows don't recover and you don't know why.
| return asyncio.to_thread(cls.patch, patch_name) | ||
|
|
||
| @classmethod | ||
| def deprecate_patch(cls, patch_name: str) -> bool: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The problem with this approach is it's confusing where to run deprecate_patch: does it replace the original if-else statements? What if I have consecutive if-else branches?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a better design is to explicitly register patches and explicitly enable/disable them. It's easier for testing too -- you can test the patched code controlling which patch is effective.
Patching is a mechanism for safely upgrading workflow code. It is an alternative to workflow versioning (though they can be used together).
The problem patching solves is "How do I make a breaking change to a workflow's code but continue execution of long-running workflows that started on the old code version?" A breaking change is any change in what steps run or the order in which they run.
To use patching, first enable it in configuration:
Next, when making a breaking change, use an
if DBOS.patch():conditional.DBOS.patch()returnsTruefor new workflows (those started after the breaking change) andFalsefor old workflows (those started before the breaking change). Therefore, ifDBOS.patch()is true, call the new code, else call the old code.So let's say our workflow is:
We want to replace the call to
foo()with a call tobaz(), which is a breaking change. We can do this safely using a patch:Now, new workflows will run
baz(), while old workflows will safely recover throughfoo().Once all workflows of the pre-patch code version are complete, we can remove patches from our code. First, we deprecate the patch. This will safely run workflows containing the patch marker, but will not insert the patch marker into new workflows:
Then, when all workflows containing the patch marker are complete, we can remove the patch entirely and complete the workflow upgrade!
If any mistakes happen during the process (a breaking change is not patched, or a patch is deprecated or removed prematurely), the workflow will throw a clean
DBOSUnexpectedStepErrorpointing to the step where the problem occurred.Also, one advanced feature is that if you need to make consecutive breaking changes to the same code, you can stack patches: