Skip to content

workflow: report whether the sidecar staged an operation - #1354

Open
famarting wants to merge 2 commits into
dapr:mainfrom
famarting:workflow-staged-operations
Open

famarting wants to merge 2 commits into
dapr:mainfrom
famarting:workflow-staged-operations

Conversation

@famarting

Copy link
Copy Markdown
Contributor

Why

dapr/dapr#10909 and dapr/dapr#10910 add the WorkflowsStagedOperations preview. With it on, a sidecar with no workflow worker connected still accepts start, raise event, terminate, suspend, resume and rerun. It saves them and runs them once a worker connects. Today the caller gets the same OK as for an operation that ran, so it can't tell the two apart.

dapr/durabletask-protobuf#60 (merged) adds bool staged to those six responses. dapr/durabletask-go#147 (merged) shows it to Go callers. This PR does the same for Python. The sidecar side that sets the field is dapr/dapr#11061.

What

Protos. The vendored durabletask stubs are regenerated from durabletask-protobuf caf5cc1d (the #60 merge commit) with tools/regen_durabletask_protos.sh and the pinned grpcio-tools==1.81.1 / mypy-protobuf==5.0.0. Regenerating at the old commit first gave byte-identical files, so the diff is only the proto change. The new recorded commit also carries dapr/durabletask-protobuf#58, which adds parentExecutionId to backend_service.proto. That shows up in backend_service_pb2*. The SDK does not use that service. I kept it so the stubs match PROTO_SOURCE_COMMIT_HASH.

Client. No existing method changes its signature or return value. Each of the six operations gets a *_with_result variant on both the sync and the async DaprWorkflowClient:

Method Returns
schedule_new_workflow_with_result WorkflowScheduleResult(instance_id, staged)
raise_workflow_event_with_result WorkflowOperationResult(staged)
terminate_workflow_with_result WorkflowOperationResult(staged)
pause_workflow_with_result WorkflowOperationResult(staged)
resume_workflow_with_result WorkflowOperationResult(staged)
rerun_workflow_from_event_with_result WorkflowRerunResult(new_instance_id, staged)

The three result types are frozen dataclasses, exported from dapr.ext.workflow. staged is false when the operation ran at once, and on sidecars that never stage. Purge is never staged, so it has no variant. schedule_new_workflow_with_result leaves out the deprecated reuse_id_policy.

result = client.schedule_new_workflow_with_result(my_workflow, input=data)
if result.staged:
    print(f'{result.instance_id} will start once a worker connects')

The vendored engine clients (TaskHubGrpcClient, AsyncTaskHubGrpcClient) get matching *_with_result methods that return the response proto. The plain engine methods now call them and drop the flag, as in durabletask-go#147.

Why separate methods. Four of the existing methods return None and two return a str. Returning a result object from them would change their return types. A keyword flag that switches the return type needs @overload on every method and is easy to misread. Separate methods keep old callers as they are and give new callers a typed result. The names follow Go's ...WithResult methods so the SDKs read alike.

Docs: dapr/ext/workflow/AGENTS.md lists the new methods and types. No example is added, because none can show staged=True until a released runtime sets it.

Tests

tests/ext/workflow/test_workflow_staged.py starts a fake sidecar on a real local gRPC server. It answers each of the six calls with staged true, then false, and checks the result of every *_with_result method, for the sync and async clients. It also checks that the plain methods still return what they did, and that both kinds of method send the same request.

Commands run, all on Python 3.12, after uv sync --frozen --all-packages --group dev:

  • uv run ruff check --fix && uv run ruff format: all checks passed, no changes left.
  • uv run mypy: no issues found in 231 source files.
  • uv run pytest tests -m "not e2e" --ignore=tests/integration --ignore=tests/examples --import-mode=importlib -o faulthandler_timeout=300 (the CI test command, without coverage): 2247 passed, 41 deselected.
  • uv run python -m unittest discover ./tests/ext/workflow: 252 tests, OK.

I did not run tests/examples or tests/integration. They need a Dapr runtime, and no released runtime sets staged yet.

🤖 Generated with Claude Code

famarting and others added 2 commits November 6, 2026 09:12
…5cc1d

Picks up dapr/durabletask-protobuf#60, which adds staged to the
responses of start, raise event, terminate, suspend, resume and rerun.

Generated with tools/regen_durabletask_protos.sh and the pinned
grpcio-tools 1.81.1 and mypy-protobuf 5.0.0. Regenerating at the
previous commit reproduces the committed files exactly. The recorded
commit also includes dapr/durabletask-protobuf#58, which adds
parentExecutionId to backend_service.proto; the SDK does not use that
service.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
With the WorkflowsStagedOperations preview feature on, a sidecar with
no workflow worker connected accepts start, raise event, terminate,
suspend, resume and rerun, saves them, and runs them once a worker
connects. The response says so in staged, but the client dropped it.

Add a *_with_result variant of each of those six methods to the sync
and async DaprWorkflowClient. They return WorkflowScheduleResult,
WorkflowOperationResult or WorkflowRerunResult, each with staged. The
existing methods keep their signatures and return values.

The vendored engine clients get matching *_with_result methods that
return the response proto, and the plain methods now call them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Fabian Martinez <46371672+famarting@users.noreply.github.com>
@famarting
famarting requested review from a team as code owners November 6, 2026 08:19
@codecov

codecov Bot commented Nov 6, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.45161% with 52 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.69%. Comparing base (6e26a49) to head (9445557).

Files with missing lines Patch % Lines
.../_durabletask/internal/orchestrator_service_pb2.py 1.92% 51 Missing ⚠️
dapr/ext/workflow/dapr_workflow_client.py 96.29% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1354      +/-   ##
==========================================
+ Coverage   88.61%   88.69%   +0.07%     
==========================================
  Files         165      166       +1     
  Lines       13540    13617      +77     
==========================================
+ Hits        11998    12077      +79     
+ Misses       1542     1540       -2     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant