Repository navigation
explicit_recovery as a handler and service key - #265
Conversation
With explicit cancellation, Restate cancelling an invocation neither fails
the handler's awaits nor cancels the calls it made: the handler finds out
by awaiting cancellation(ctx), and decides what to stop.
@explicit_cancellation
@greeter.handler()
async def greet(ctx, name):
done, _ = await restate.select(work=ctx.service_call(slow, arg=name), cancelled=cancellation(ctx))
if done == "cancelled":
await cancel_previous_calls(ctx) # the calls still in flight
raise TerminalError("cancelled", status_code=409)
In restate._preview, next to explicit_recovery: on a handler, or a whole
service, object or workflow. The VM disables shared-core's implicit
cancellation; the context keeps the attempt's calls that may be in flight
(only with the option on), which cancel_previous_calls cancels.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RePHkTUvGceAzHBUPULRvy
Like every other option: @svc.handler(explicit_cancellation=True), a workflow's main()/handler() too, and Service/VirtualObject/Workflow(..., explicit_cancellation=True) for all their handlers. The restate._preview.explicit_cancellation decorator is gone; cancellation() and cancel_previous_calls() stay in restate._preview. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RePHkTUvGceAzHBUPULRvy
Like explicit_cancellation and every other option: @svc.handler(explicit_recovery=True), a workflow's main()/handler() too, and Service/VirtualObject/Workflow(..., explicit_recovery=True) for all their handlers. The restate._preview.explicit_recovery decorator still works, and warns that it is deprecated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RePHkTUvGceAzHBUPULRvy
There was a problem hiding this comment.
ℹ️ No critical issues — two documentation rough edges inline.
Reviewed changes
This review covers the single commit that turns preview explicit_recovery into a handler and service option and deprecates the decorator.
- New option on all constructors and decorators —
explicit_recovery: bool = Falseadded to theService,VirtualObjectandWorkflowconstructors, theirhandler(),Workflow.main()/_add_handler, andmake_handler. It's passed by keyword, so the positionalmake_handlerargument lists stay intact. The runtime still ORs the handler flag with the service flag inserver.py, so service-level enabling works unchanged. - Decorator deprecated —
restate._preview.explicit_recoverystill sets the flag but now emits aDeprecationWarningwithstacklevel=2. - Tests migrated —
tests/explicit_recovery.pynow uses the keyword option. It also checks that the decorator still works and warns.
claude-opus-5-5 | 𝕏
…d:: directive The handler decorators' explicit_recovery docs say what explicit recovery means for calls and sends (new requests each attempt: give them a stable idempotency key), ctx.run (runs again) and awakeables (not supported), as the decorator's docstring did. The deprecated decorator keeps that text, with a .. deprecated:: 1.4.0 directive, as connection.py's Connection.wait. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RePHkTUvGceAzHBUPULRvy
There was a problem hiding this comment.
✅ No new issues found. Both earlier documentation threads are addressed.
Reviewed changes
Since the last Pullfrog review, one commit changed docstrings only. There are no code or behavior changes.
- Restored the explicit-recovery caveats — the
explicit_recoverydocs onhandler()forService,VirtualObjectandWorkflow, and onWorkflow.main(), now cover all three caveats: calls and sends are new requests on each attempt (so give them a stable idempotency key, e.g. one made fromctx.request().id),ctx.runruns again on every attempt, and awakeables are unsupported. TheHandlerfield doc points readers to those docs. - Added the
.. deprecated:: 1.4.0directive — the deprecatedrestate._preview.explicit_recoverydocstring now follows the repo's deprecation convention, and it keeps the full caveat list.
claude-opus-5-5 | 𝕏
restate._preview.explicit_recovery was never released, so it goes rather than being deprecated: explicit_recovery=True on the handler decorator, or on the service, is the way to enable explicit recovery (still a preview feature, as its docs say). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
There was a problem hiding this comment.
✅ No new issues found. The code changes are clean, with one note below about the PR description.
Reviewed changes
Since the last Pullfrog review, one commit deletes the explicit_recovery decorator. The handler and service keys are now the only way to turn the option on.
- Removed
restate._preview.explicit_recovery._preview.pynow holds only the explicit-cancellation helpers from #264. The imports that only the decorator used (handler_from_callable,Service,VirtualObject,Workflow,warnings) and theTTypeVar are gone. Nothing else in the repo references the decorator. - Updated
tests/explicit_recovery.py. Removed the deprecation-warning test and the misapplied-decoratorValueErrortest. Added assertions that theServiceandWorkflowconstructors accept the key.
ℹ️ The removed decorator was in published releases
The PR description says restate._preview.explicit_recovery "was never released". It was: the restate-sdk 1.3.0 sdist on PyPI contains python/restate/_preview.py with def explicit_recovery, and the decorator was already in the tree at the 1.1.0 version bump. After this change, any 1.3.0 user who has from restate._preview import explicit_recovery gets an ImportError when they upgrade.
The _preview module says its APIs "might change or be removed without notice", so deleting the decorator is allowed. The decision just shouldn't rest on the claim that it never shipped.
Technical details
# Removal of a released `_preview` API
## Affected sites
- python/restate/_preview.py — `explicit_recovery` decorator deleted (shipped in restate-sdk 1.1.0–1.3.0 on PyPI)
- PR #265 description — "was never released, so it's deleted rather than deprecated"
## Required outcome
- The PR description and release notes describe the removal accurately, or the deprecated shim from 7365129 is kept for a release.
## Open questions for the human
- Is a hard removal fine under the `_preview` contract, or should 7365129's `DeprecationWarning` shim stay for one release (1.4.0) and be removed after that?claude-opus-5-5 | 𝕏
# Conflicts: # python/restate/_preview.py # python/restate/handler.py # python/restate/object.py # python/restate/service.py # python/restate/workflow.py

explicit_recoveryas a handler and service keyThis makes the preview
explicit_recoveryoption a key, like every other option and likeexplicit_cancellation(#264), instead of a decorator:handler()and a workflow'smain(), and on theService,VirtualObjectandWorkflowconstructors. Off by default.ctx.runruns again on every attempt, and awakeables are not supported.restate._preview.explicit_recoverywas never released, so it's deleted rather than deprecated.tests/explicit_recovery.pyuses the key.🤖 Generated with Claude Code