镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

Isolate inbound payload hydration failures to their deliveries - #829

Open
wangbill (YunchuWang) wants to merge 9 commits into
mainfrom
yunchuwang-payload-hydration-isolation
Open

wangbill (YunchuWang) wants to merge 9 commits into
mainfrom
yunchuwang-payload-hydration-isolation

Conversation

@YunchuWang

@YunchuWang wangbill (YunchuWang) commented Oct 30, 2026 •

Copy link
Copy Markdown
Member

Summary

What changed?

  • Isolate inbound payload hydration failures to the failed delivery. Abandon only its token, continue the same GetWorkItems stream, and never dispatch a partially hydrated item.
  • Give each transport read a fresh watchdog scope. Successful receipt stops that read's watchdog and cannot leave later reads classified as timed out. Payload resolution and abandonment use worker/call cancellation separately.
  • Preserve the independent 120-second budget per active worker download and one 30-second budget/deadline across receive-loop Abandon RPC attempts/backoff, with bounded concurrency and drainage.
  • Move the two actual worker/extension bridge contracts to Worker.Grpc.Internal; remove the added protobuf friend access. Remove the split-reader helpers and newly introduced production test callbacks.
  • Preserve supported public/protected interceptor signatures and async override behavior. Add external-subclass, shared-lifetime, and distinct-cancellation tests; reuse the call-lifetime token in the common path.

Why is this change needed?

  • One unreadable payload must not disconnect unrelated healthy deliveries. Healthy hydration must not inherit a fired read timeout, while stalled downloads and abandonment must remain bounded.

Issues / work items


Project checklist

  • Release notes are not required for the next release
    • Otherwise: Notes added to release_notes.md
  • Backport is not required
    • Otherwise: Backport tracked by issue/PR #issue_or_pr
  • All required tests have been added/updated (unit tests, E2E tests)
  • Breaking change?
    • If yes:
      • Impact: Existing supported signatures, constructors, wire fields, and persisted formats are unchanged. Two additive public infrastructure contracts in Worker.Grpc.Internal are explicitly not application APIs; this is not a claim of zero public metadata additions.
      • Migration guidance: Custom stores must honor cancellation; active worker downloads have the documented 120-second budget.

AI-assisted code disclosure (required)

Was an AI tool used? (select one)

  • No
  • Yes, AI helped write parts of this PR (e.g., GitHub Copilot)
  • Yes, an AI agent generated most of this PR

If AI was used:

  • Tool(s): GitHub Copilot (GPT-6.1 Sol).
  • AI-assisted areas/files: Worker/interceptor bridge, timeout/cancellation handling, tests, documentation, and PR description.
  • What you changed after AI output: The agent kept abandon-only scope, addressed each of the 12 review threads in 325f6bbd, and corrected the test package's approved-feed restore in 5c1c38eb. Human approval is still required.

AI verification (required if AI was used):

  • I understand the code and can explain it
  • I verified referenced APIs/types exist and are correct
  • I reviewed edge cases/failure paths (timeouts, retries, cancellation, exceptions)
  • I reviewed concurrency/async behavior
  • I checked for unintended breaking or behavior changes

Testing

Automated tests

  • Result: Complete local suites passed: 283 worker + 397 payload-extension + 346 integration = 1,026 passed, with 10 integration tests skipped because a DTS emulator was not enabled locally. The final-head GitHub restore, build, test, pack, and DTS emulator checks all passed in Validate Build; emulator job.
dotnet test test\Worker\Grpc.Tests\Worker.Grpc.Tests.csproj --configuration Release --no-restore --verbosity minimal --nologo

Passed: 283 tests, including exact 119/120-second successful-read boundary cases, download drain/recovery, total abandonment/retry/backoff budgets, shutdown, external subclass dispatch, shared lifetime, and distinct cancellation.

dotnet test test\Extensions\AzureBlobPayloads.Tests\AzureBlobPayloads.Tests.csproj --configuration Release --no-restore --verbosity minimal --nologo

Passed: 397 tests. Scope-denial assertions require the exact delivery wrapper and expected inner storage exception.

dotnet test test\Grpc.IntegrationTests\Grpc.IntegrationTests.csproj --configuration Release --no-restore --verbosity minimal --nologo

Passed: 346; skipped: 10 emulator-only tests. Includes public management/unary/history behavior and call-disposal cancellation.

$env:DOTNET_PROCESSOR_COUNT = '1' # also repeated with '2'
dotnet test test\Worker\Grpc.Tests\Worker.Grpc.Tests.csproj --configuration Release --no-build --no-restore --filter 'FullyQualifiedName~ConsumeAsync_LateMessage|FullyQualifiedName~ProcessorExecuteAsync_PendingPayloadRecovery|FullyQualifiedName~ProcessorExecuteAsync_DownloadTimeout|FullyQualifiedName~ProcessorExecuteAsync_AbandonTimeout|FullyQualifiedName~AsyncServerStreamingCall_ExternalSubclass|FullyQualifiedName~AsyncServerStreamingCall_DistinctReadToken|FullyQualifiedName~AsyncServerStreamingCall_CommonCallToken' --verbosity quiet --nologo

Passed: 13 cases on each of 10 runs at 1 logical CPU and 10 runs at 2 logical CPUs (260 repeated executions), with the final package version.

dotnet build src\Extensions\AzureBlobPayloads\AzureBlobPayloads.csproj --configuration Release --no-restore --verbosity minimal --nologo

Passed: netstandard2.0, net6.0, net8.0, net10.0. An unsigned net10.0 Grpc build with -p:SignAssembly=false -p:StrongNamePublicKey= also passed and has an empty public-key token.

Empty-cache dependency verification, using only the checked-in feed configuration:

dotnet restore test\Worker\Grpc.Tests\Worker.Grpc.Tests.csproj --configfile nuget.config --packages <new-empty-package-directory> --no-http-cache --force --verbosity minimal --nologo
dotnet restore test\Grpc.IntegrationTests\Grpc.IntegrationTests.csproj --configfile nuget.config --packages <same-package-directory> --no-http-cache --force --verbosity minimal --nologo

Both passed. Test-only Microsoft.Extensions.TimeProvider.Testing 10.0.0 has a native net10 dependency group and does not restore Microsoft.Bcl.TimeProvider. No global feeds, credentials, or product dependencies were added. Complete worker/integration suites and the four-target build passed again from this clean package set.

git --no-pager diff --check and git --no-pager diff --cached --check passed before committing.

Manual validation (only if runtime/behavior changed)

  • Environment (OS, .NET version, components): Windows; .NET SDK 10.0.404; existing loopback HTTP/2 fixture and mocked payload stores. Timing tests use standard TimeProvider/FakeTimeProvider, not production test callbacks or long real delays.
  • Steps + observed results:
    1. The previous CI failure in ConsumeAsync_LateMessage_DoesNotTimeOutHealthyHydrationAsync is covered by deterministic success/timeout ownership cases and repeated low-CPU runs. Final-head CI tests pass.
    2. External subclass tests compile in an assembly that is not a payload-extension friend and verify that the original protected resolver runs once for work-item/worker-history responses.
    3. No live-Azure fault injection was performed. An exploratory whole-integration run at 2 CPUs was stopped after repeated timeouts in unchanged OrchestrationPatterns shared-fixture tests; its isolated IsReplaying case and the complete normal-CPU suite pass. Changed-path tests pass the low-CPU stress runs above.
  • Evidence (optional): Logs and TRX results are retained in the coding session. Head 325f6bbd initially failed restore on uncached Microsoft.Bcl.TimeProvider 8.0.0; 5c1c38eb passed clean-cache restore and the final GitHub validation workflow. Other analysis checks/human review may still be pending; no merge was performed.

Notes for reviewers

  • Abandon-only: missing blobs, authorization/I/O failures, and download timeouts do not become terminal task/orchestration failures. NotFound on abandon remains item-local; other failures/expiry enter connection recovery. Normal completion and background-task failure policy are unchanged.
  • Worker-processing history uses per-active-download budgets. Public unary/management history has no new timeout. Started operations are drained; a custom store that ignores cancellation cannot safely be forcibly detached.
  • The protobuf project is unchanged from main. Worker/extension integration uses two documented unsupported Worker.Grpc.Internal contracts, not friend access or public types added just for tests.
  • No newly introduced ForTest callbacks remain. Optional production TimeProvider scheduling uses the system clock by default on net8+; older targets use native timers. The fake-clock package is test-only.
  • Lower-case abandonment log placeholders match existing Logs.cs events 58–65. Each of the 12 review threads has a substantive inline reply and is resolved.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings October 30, 2026 19:15

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.

🟢 Approval recommended

No active review issues block the change to isolate payload hydration failures to individual deliveries.

No open issues 🎉

What changed in this PR

This PR addresses #825 by isolating inbound payload hydration failures to individual deliveries, preserving the Durable Task worker’s shared stream and unrelated in-flight work.

Changes:

  • Introduces an internal exception carrying the failed delivery and its completion token.
  • Reads work items directly and reuses abandonment logic, tolerating ownership loss while preserving connection-level recovery for other failures.
  • Adds consumer and HTTP/2 loopback regression coverage plus an Unreleased changelog entry.
File Description
test/​Worker/​Grpc.Tests/​WorkItemStreamConsumerTests.cs Tests delivery recovery, callback ordering, and failure propagation.
test/​Grpc.IntegrationTests/​PayloadHydrationFailureTests.cs Adds loopback coverage for isolation, redelivery, reconnection, and shutdown.
src/​Worker/​Grpc/​WorkItemStreamConsumer.cs Uses direct stream reads with delivery-local recovery.
src/​Worker/​Grpc/​Worker.Grpc.csproj Grants integration tests access to worker internals.
src/​Worker/​Grpc/​Logs.cs Adds ownership-loss logging.
src/​Worker/​Grpc/​GrpcDurableTaskWorker.Processor.cs Handles hydration failures using shared abandonment logic.
src/​Grpc/​WorkItemPayloadException.cs Defines the internal failed-delivery exception.
src/​Grpc/​Grpc.csproj Exposes the internal contract to consuming assemblies and tests.
src/​Extensions/​AzureBlobPayloads/​Interceptors/​PayloadInterceptor.cs Wraps work-item hydration failures with delivery context.
CHANGELOG.md Documents delivery-local hydration failure handling.

Review effort: Balanced

Remove redundant consumer tests and exercise the shared failure-isolation matrix with both gRPC and Azure Managed worker configuration. Preserve retryable inbound delivery semantics and document the recovery boundary.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 22:17

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.

🟢 Approval recommended

No active review issues block the change to isolate payload hydration failures to individual deliveries.

No open issues 🎉

Review effort: Balanced

…hy work

Replace the standalone test server with focused coverage in existing harnesses. Preserve ordinary background error handling, validate execution metadata before terminal settlement, and release entity locks for inline and streamed history. Recoverable download failures remain delivery-local and retryable.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 23:33
@YunchuWang wangbill (YunchuWang) changed the title Isolate inbound payload hydration failures to their deliveries Isolate inbound payload failures and fail missing-payload deliveries Nov 10, 2026

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.

🟡 Changes recommended

Unresolved recovery and replay defects can leave deliveries owned until expiry, disconnect healthy streams, and terminally fail callers before they can handle missing-input failures.

3 open issues

Review effort: Balanced

…ng healthy work"

This reverts commit a0f5320 at the user request. Restore retryable delivery-local abandonment for all incoming hydration failures.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 23:45
@YunchuWang wangbill (YunchuWang) changed the title Isolate inbound payload failures and fail missing-payload deliveries Isolate inbound payload hydration failures to their deliveries Nov 10, 2026

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.

🟢 Approval recommended

No active review issues were identified for the change isolating payload hydration failures to individual deliveries.

2 open issues
1 resolved since last review

Review effort: Balanced

Remove the standalone 536-line scripted sidecar and reuse existing worker and integration test harnesses. Leave ordinary background failure handling unchanged, remove unnecessary test friend access and duplicate documentation, and retain delivery-local abandonment for every hydration failure.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 10, 2026 23:57

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.

🟡 Changes recommended

Missing regression tests for cancellation during hydration and pending abandonment leave the required unchanged shutdown behavior unverified.

3 open issues

Review effort: Balanced

Use worker shutdown cancellation during delivery recovery and rearm the read watchdog before receiving again. Add gated shutdown and slow-abandon regressions; fix the scoped-payload assertion and prevent test-sidecar concurrency starvation on small CI hosts. Keep all hydration failures abandon-only.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 11, 2026 15:38

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.

🟢 Approval recommended

No active review issues remain for the changes isolating inbound hydration failures to individual deliveries.

No open issues 🎉

3 resolved since last review

Review effort: Balanced

Watch only transport receipt for silent disconnects. Give each active worker payload download its own 120-second cancellation budget while preserving bounded concurrency and drainage. Bound receive-loop abandonment and its existing retries/backoff together to 30 seconds. Keep downloads abandon-only and preserve public unary/history behavior.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 11, 2026 16:19
Comment thread src/Grpc/WorkItemPayloadException.cs Resolved
Comment thread src/Grpc/Grpc.csproj Resolved

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.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

5 open issues

Review effort: Lite

Comment thread src/Grpc/Grpc.csproj Resolved
Comment thread src/Grpc/WorkItemPayloadException.cs Resolved
Comment thread src/Worker/Grpc/Logs.cs Resolved
Move worker-extension infrastructure out of the protobuf assembly and remove its friend access. Preserve protected interceptor overrides and async behavior, simplify call disposal and common-token allocation, and remove timeout test callbacks. Use standard clock scheduling for deterministic deadline, cancellation, and external-subclass regressions; successful transport reads no longer poison later reads with a fired watchdog.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 11, 2026 17:27

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.

🔵 Needs a closer look

Human assessment is needed because interceptor wrappers can expose healthy payload downloads to watchdog cancellation, while abandon deadlines can be misreported as handshake failures and incorrectly mark channels as poisoned.

No open issues 🎉

5 resolved since last review

Review effort: Balanced

Use Microsoft.Extensions.TimeProvider.Testing 10.0.0 for the net10 test projects. Its native net10 dependency group does not request the uncached Microsoft.Bcl.TimeProvider 8.0.0 package. Verified both test projects restore from an empty package cache using only the checked-in nuget.config.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot AI balanced review requested due to automatic review settings November 11, 2026 17:36

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.

🟢 Approval recommended

No active review issues remain for the changes isolating inbound payload hydration failures to individual deliveries.

No open issues 🎉

Review effort: Balanced

Comment on lines +115 to +117
bool hasNext = payloadReader is null
? await stream.MoveNext(tokenSource.Token).ConfigureAwait(false)
: await payloadReader.MoveNextAsync(OnMessageReceived, tokenSource.Token, cancellation).ConfigureAwait(false);

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.

If a logging interceptor wraps this reader, what happens when receiving a message takes 119 seconds and downloading its payload takes another 2 seconds? Could the 120-second read timeout cancel the download and disconnect the whole stream?

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.

Payload hydration failure for one work item tears down the shared GetWorkItems stream

3 participants