Skip to content

fix(ext-workflow): honor DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES on workflow channels#1085

Open
tezizzm wants to merge 1 commit into
dapr:mainfrom
tezizzm:fix/workflow-grpc-max-message-size
Open

fix(ext-workflow): honor DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES on workflow channels#1085
tezizzm wants to merge 1 commit into
dapr:mainfrom
tezizzm:fix/workflow-grpc-max-message-size

Conversation

@tezizzm

@tezizzm tezizzm commented Jun 10, 2026

Copy link
Copy Markdown

Description

Long-running workflows whose activity results exceed gRPC's 4 MiB default fail with
RESOURCE_EXHAUSTED: Sent message larger than max (X vs. 4194304). #1024 added
DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES and threaded it through the Dapr API
channel only — it did not cover the workflow (durabletask) channel, so even with
the env var exported, workflows still hit the 4 MiB ceiling once the orchestration
history (input + accumulated activity results) crossed the limit.

This PR mirrors the #1024 pattern in dapr-ext-workflow:

  • Adds a get_grpc_channel_options() helper in dapr/ext/workflow/util.py. Resolution
    order: explicit max_grpc_message_length kwarg → settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES
    None (gRPC default).
  • Threads a new optional max_grpc_message_length: Optional[int] = None kwarg through
    the three workflow gRPC constructors and passes the resolved channel_options:
    • WorkflowRuntime.__init__TaskHubGrpcWorker
    • DaprWorkflowClient.__init__ (sync) → TaskHubGrpcClient
    • aio.DaprWorkflowClient.__init__ (async) → AsyncTaskHubGrpcClient
  • Updates the public docstrings of all three constructors to document the kwarg and its
    precedence.

Unlike #1024, the limit is applied symmetrically to both send and receive, because
workflow activity payloads cross the channel in both directions (worker → daprd carries
activity output too); #1024 only set the receive limit in its env-var branch.

No new environment variable is introduced (reuses #1024's), and no vendored
_durabletask/ internals or dapr/proto/ files are touched.

Issue reference

We strive to have all PR being opened based on an issue, where the problem or feature have been discussed prior to implementation.

Please reference the issue this PR will close: #1084

Checklist

Please make sure you've completed the relevant tasks for this PR, out of the following list:

  • Code compiles correctly
  • Created/updated tests
  • Extended the documentation

…workflow channels

Workflow activity payloads over gRPC's 4 MiB default raised
RESOURCE_EXHAUSTED because the durabletask channel ignored the message
size limit. dapr#1024 plumbed DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES
through the Dapr API channel only; the workflow worker and clients still
fell back to the gRPC default.

Add a get_grpc_channel_options helper that resolves the limit from an
explicit max_grpc_message_length kwarg, then the env var, then None, and
thread it through WorkflowRuntime, DaprWorkflowClient, and the async
DaprWorkflowClient. Unlike dapr#1024, set both send and receive limits
symmetrically since workflow payloads cross the channel in both
directions. Reuses the existing setting; no new env var.

Signed-off-by: Martez Killens <5050479+tezizzm@users.noreply.github.com>
@tezizzm tezizzm requested review from a team as code owners June 10, 2026 01:09
@codecov

codecov Bot commented Jun 10, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.08257% with 1 line in your changes missing coverage. Please review.
✅ Project coverage is 82.79%. Comparing base (bffb749) to head (9f40992).
⚠️ Report is 142 commits behind head on main.

Files with missing lines Patch % Lines
...t/dapr-ext-workflow/tests/test_workflow_runtime.py 96.42% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1085      +/-   ##
==========================================
- Coverage   86.63%   82.79%   -3.85%     
==========================================
  Files          84      146      +62     
  Lines        4473    14945   +10472     
==========================================
+ Hits         3875    12373    +8498     
- Misses        598     2572    +1974     

☔ 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.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes workflow gRPC message-size handling in dapr-ext-workflow so long-running workflows with large activity payloads (or large accumulated orchestration history) can honor DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES (and an explicit max_grpc_message_length kwarg) instead of failing at gRPC’s default 4 MiB limit.

Changes:

  • Added get_grpc_channel_options() utility to resolve workflow gRPC channel options (kwarg → env var → gRPC default), applying limits symmetrically for send/receive.
  • Threaded max_grpc_message_length: Optional[int] = None through workflow runtime and both sync/async workflow clients, passing resolved channel_options into durabletask constructors.
  • Added unit tests covering precedence and symmetric option application for runtime and clients.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
ext/dapr-ext-workflow/dapr/ext/workflow/util.py Adds get_grpc_channel_options() to compute workflow-channel gRPC max message size options.
ext/dapr-ext-workflow/dapr/ext/workflow/workflow_runtime.py Threads max_grpc_message_length into WorkflowRuntime and passes computed channel_options to TaskHubGrpcWorker.
ext/dapr-ext-workflow/dapr/ext/workflow/dapr_workflow_client.py Threads max_grpc_message_length into sync DaprWorkflowClient and passes computed channel_options to TaskHubGrpcClient.
ext/dapr-ext-workflow/dapr/ext/workflow/aio/dapr_workflow_client.py Threads max_grpc_message_length into async DaprWorkflowClient and passes computed channel_options to AsyncTaskHubGrpcClient.
ext/dapr-ext-workflow/tests/test_workflow_util.py Adds tests for get_grpc_channel_options() precedence and symmetric send/receive options.
ext/dapr-ext-workflow/tests/test_workflow_runtime.py Adds tests asserting WorkflowRuntime passes symmetric channel_options derived from kwarg/env var.
ext/dapr-ext-workflow/tests/test_workflow_client.py Adds tests asserting sync DaprWorkflowClient passes symmetric channel_options derived from kwarg/env var.
ext/dapr-ext-workflow/tests/test_workflow_client_aio.py Adds tests asserting async DaprWorkflowClient passes symmetric channel_options derived from kwarg/env var.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +33 to +43
Args:
max_grpc_message_length: Explicit max gRPC message size in bytes. Takes
precedence over the env-var setting when truthy.

Returns:
A sequence of ``(option, value)`` tuples setting both send and receive
limits, or ``None`` when no limit is configured.
"""
size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES or None
if size is None:
return None

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree with this suggestion.

Comment on lines +41 to +47
size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES or None
if size is None:
return None
return [
('grpc.max_send_message_length', size),
('grpc.max_receive_message_length', size),
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES or None
if size is None:
return None
return [
('grpc.max_send_message_length', size),
('grpc.max_receive_message_length', size),
]
size = max_grpc_message_length or settings.DAPR_GRPC_MAX_INBOUND_MESSAGE_SIZE_BYTES
if not size:
return None
return [
('grpc.max_send_message_length', size),
('grpc.max_receive_message_length', size),
]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows None & 0 to fall through and return None for both

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.

[WORKFLOW SDK BUG] Activity payloads >4 MiB raise RESOURCE_EXHAUSTED on the workflow gRPC channel

3 participants