Skip to content

Improve handling of object change notifications during local run - #19079

Open
Karol Zadora-Przylecki (karolz-ms) wants to merge 6 commits into
mainfrom
dev/karolz/address-dcp-update-comments
Open

Improve handling of object change notifications during local run#19079
Karol Zadora-Przylecki (karolz-ms) wants to merge 6 commits into
mainfrom
dev/karolz/address-dcp-update-comments

Conversation

@karolz-ms

Copy link
Copy Markdown
Contributor

Description

This is a follow-up to the post-merge review discussion on #18952.

During a local run, a DCP watch can reconnect after an object was deleted and recreated without observing the Deleted event. The previous replay deduplication keyed observations by object kind, name, and resource version, so a replacement object could be suppressed if its opaque resource version happened to equal the previous object's version. Per-resource log state could also remain associated with the old object incarnation.

This change tracks each observation's Kubernetes UID with its resource version, treats a changed UID as a replacement, and clears stale log-stream and terminal-flush state before processing the replacement. Unchanged watch replays remain suppressed. The terminal-log comments now describe the exact retry behavior, and regression tests verify both replacement handling and late-subscriber log delivery.

User-facing behavior

Local runs now recognize recreated DCP objects even when deletion happened while the watch was disconnected. Late console-log subscribers continue to receive terminal-resource logs through the normal subscriber-driven follow stream, without relying on periodic watch replays.

Validation:

dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj --no-launch-profile -- --filter-class "*.DcpExecutorTests" --filter-not-trait "quarantined=true" --filter-not-trait "outerloop=true"
209 passed, 1 platform-specific test skipped

Fixes # (issue)

Checklist

  • Is this feature complete?
    • Yes. Ready to ship.
    • No. Follow-up changes expected.
  • Are you including unit tests for the changes and scenario tests if relevant?
    • Yes
    • No
  • Did you add public API?
    • Yes
      • If yes, did you have an API Review for it?
        • Yes
        • No
      • Did you add <remarks /> and <code /> elements on your triple slash comments?
        • Yes
        • No
    • No
  • Does the change make any security assumptions or guarantees?
    • Yes
      • If yes, have you done a threat model and had a security review?
        • Yes
        • No
    • No

Copilot AI balanced review requested due to automatic review settings August 6, 2026 20:44
@karolz-ms Karol Zadora-Przylecki (karolz-ms) added the area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication label Aug 6, 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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Note

This error may be related to your runner configuration. You can now configure runners for Copilot code review separately from Copilot cloud agent by creating a copilot-code-review.yml file with your setup steps. Read the docs for details.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Code review findings: 1 test-coverage gap (validated empirically), 1 fragile-invariant issue, 1 pre-existing concurrency hazard, 1 test-realism issue, and 1 pre-existing resource leak. No bugs found in the core ProcessResourceChange identity logic — the isSameResource / isReplacement computation handles the no-previous-observation and empty-version edge cases correctly, and the ReplacedResetResourceLogState ordering correctly precedes the terminal flush.

Comment thread tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs Outdated
Comment thread src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs Outdated
Comment thread src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs Outdated
Comment thread tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs Outdated
Comment thread src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs Outdated

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

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

Comment thread src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs Outdated
@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.sh | bash -s -- 19079

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/microsoft/aspire/main/eng/scripts/get-aspire-cli-pr.ps1) } 19079"

@github-actions

This comment has been minimized.

Copilot AI review requested due to automatic review settings August 7, 2026 17:23
@github-actions

This comment has been minimized.

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

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

Comment thread src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs Outdated
@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Copilot AI review requested due to automatic review settings August 7, 2026 18:23
@github-actions

This comment has been minimized.

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs:355

  • terminalLogsHandledForActiveSubscribers means a flush was attempted, not that it completed. For FailedToStart the flush is deliberately point-in-time, and an Exited flush can time out; both leave _allLogsFlushed unset. Suppressing the subscriber-driven follow stream here then leaves an already-active subscriber with no stream to deliver logs that arrive after that incomplete flush, and an unchanged terminal object may never produce another notification. Start the normal stream whenever the flush did not complete.
                        var shouldStartStream =
                            resourceChange == ResourceChangeResult.Replaced ||
                            !terminalLogsHandledForActiveSubscribers;

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Copilot AI review requested due to automatic review settings August 7, 2026 19:25
@github-actions

This comment has been minimized.

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

Copilot AI review requested due to automatic review settings August 7, 2026 22:15
@karolz-ms
Karol Zadora-Przylecki (karolz-ms) force-pushed the dev/karolz/address-dcp-update-comments branch from d5a6767 to f3ecc26 Compare August 7, 2026 22:15
@github-actions

This comment has been minimized.

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

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

Comment thread src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs Outdated
@karolz-ms

Copy link
Copy Markdown
Contributor Author

PR Testing Report

PR Information

Artifact Version Verification

  • Expected commit: f3ecc2611d3b34f641dc0f1cef53ff6e942ff029
  • Installed CLI: 13.5.0-pr.19079.gf3ecc261
  • Status: PASS - the installed dogfood CLI reports the PR head's short SHA.

Changes Analyzed

Files Changed

  • src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs
  • tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs
  • tests/Aspire.Hosting.Tests/Dcp/GatedLogger.cs
  • tests/Aspire.Hosting.Tests/Dcp/GatedReadStream.cs
  • tests/Aspire.Hosting.Tests/Dcp/TestKubernetesService.cs

Change Categories

  • Hosting core
  • Tests
  • CLI implementation
  • Dashboard
  • Templates
  • Client components
  • VS Code extension
  • CI infrastructure

The production change records Kubernetes UID together with resource version, recognizes a changed UID as a replacement even when the resource version is reused, and resets log-stream, terminal-flush, and deduplication state before processing the replacement. It also prevents an old overlapping stream from removing state owned by a newer stream.

Test Scenarios Executed

Scenario 1: PR Artifact Provenance

Objective: Confirm that runtime testing uses the CLI and packages built for the latest PR commit.

Coverage type: Artifact integrity

Status: PASS

Steps and result:

  1. Installed PR Improve handling of object change notifications during local run #19079 with the repository dogfood installer into an isolated temporary directory.
  2. Used the installed binary directly rather than a PATH lookup.
  3. Verified aspire --version returned 13.5.0-pr.19079.gf3ecc261, matching head f3ecc261....

Evidence:

  • install.log
  • version.txt

Scenario 2: DCP Executor Regression Suite

Objective: Exercise the complete unit-test surface around the changed DCP watcher and logging lifecycle.

Coverage type: Happy path, regression, concurrency, and boundary

Status: PASS

Command:

dotnet test --project tests/Aspire.Hosting.Tests/Aspire.Hosting.Tests.csproj \
  --no-launch-profile -- \
  --filter-class '*.DcpExecutorTests' \
  --filter-not-trait 'quarantined=true' \
  --filter-not-trait 'outerloop=true'

Result: 212 total; 211 passed; 0 failed; 1 expected macOS platform skip.

The run included the new and adjacent regression cases for:

  • changed UID with a reused resource version after a missed delete;
  • stale stream and terminal-log state reset on replacement;
  • unchanged same-UID/version watch replay suppression;
  • conservative processing when no resource version is available;
  • late terminal-resource subscribers receiving logs;
  • completed follow-stream restart with an existing subscriber;
  • old overlapping streams not clearing newer deduplication state.

Evidence:

  • dcp-executor-tests.log

Scenario 3: Real DCP Watch and Log Lifecycle

Objective: Validate the dogfood CLI and PR hosting packages in a real AppHost process, including a terminal resource, late log retrieval, and resource restart.

Coverage type: Happy path and unhappy path

Status: PASS

Steps and result:

  1. Created a fresh C# aspire-empty AppHost from the PR hive.
  2. Added a long-running ticker executable and a failing executable that writes expected-terminal-log to stderr and exits with code 7.
  3. Started the AppHost in isolated background mode.
  4. Confirmed ticker reached Running and failing reached Finished.
  5. Queried failing logs only after it was down; the late subscriber received expected-terminal-log.
  6. Captured live ticker logs.
  7. Restarted ticker, confirmed its process changed from PID 45392 to 46232, waited for it to return to Running, and received fresh tick-1 through tick-4 logs.
  8. Stopped the AppHost and confirmed its process exited.

Expected unhappy-path outcome: The intentionally failing executable remains safely terminal with exit code 7, while its stderr remains available to a subscriber that attaches after termination.

Evidence:

  • minimal/apphost.cs
  • minimal/start.log
  • minimal/describe-before-restart.json
  • minimal/failing-terminal-logs.json
  • minimal/ticker-before-restart.json
  • minimal/restart-ticker.log
  • minimal/restart-pids.txt
  • minimal/describe-after-restart.json
  • minimal/ticker-after-restart.json
  • minimal/stop.log
  • minimal/stop-verification.txt

Non-Blocking Observation

Two fresh aspire-starter generations, using names Pr19079Smoke and AspireSmoke, failed before AppHost startup with CS0234 errors resolving the generated web project's Components namespace. PR #19079 changes no CLI or template files, and the PR's Ubuntu template and built-in starter CI checks passed, so this was isolated from the changed DCP behavior. The runtime scenario was repeated successfully with a fresh minimal AppHost instead of modifying the failed starter output.

Evidence:

  • starter-observation/pr19079smoke-start.log
  • starter-observation/aspiresmoke-start.log

Summary

Scenario Status Notes
Artifact provenance PASS Dogfood CLI matched PR head
DCP executor regression suite PASS 211 passed, 1 expected platform skip
Real DCP watch and log lifecycle PASS Terminal logs, restart, fresh logs, and shutdown verified

Overall Result

PASS - PR #19079's changed behavior is verified.

No blocking issue was found in the UID-aware watcher replacement handling or log-stream lifecycle. The separate starter-template compile anomaly is outside this PR's diff and did not reproduce in the minimal AppHost path used to exercise the changed hosting code.

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c1ef331b-ff6c-4c40-935f-8ae4e6ee8872

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

Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs:604

  • A restart notification can still be lost when it races natural EOF of the previous follow stream. Until the worker reaches finally, the completed/finishing state remains in _logStreams, so this TryAdd fails and consumes the only LogsAvailable trigger; the old worker can then remove itself, leaving an existing subscriber with no stream for the restarted resource. Coordinate the completing transition with registration (for example, atomically replace/retry a state marked as completing, or enqueue a retry after the existing state finishes) so this race cannot drop the restart.
        // Register before starting the worker so an immediately completing stream can remove itself.
        if (!_logStreams.TryAdd(resourceName, logStream))
        {
            logStream.Dispose();
            return;

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Tests selector (audit mode)

The full test matrix and all jobs still run in audit mode. The tests and jobs below are what selective CI would run under enforcement.

48 / 100 test projects · 4 jobs, from 5 changed files.

Selected test projects (48 / 100)

Aspire.EndToEnd.Tests, Aspire.Hosting.Analyzers.Tests, Aspire.Hosting.Azure.Kubernetes.Tests, Aspire.Hosting.Azure.Kusto.Tests, Aspire.Hosting.Azure.Tests, Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Browsers.Tests, Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests, Aspire.Hosting.DevTunnels.Tests, Aspire.Hosting.Docker.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.DotnetTool.Tests, Aspire.Hosting.EntityFrameworkCore.Tests, Aspire.Hosting.Foundry.Tests, Aspire.Hosting.Garnet.Tests, Aspire.Hosting.GitHub.Models.Tests, Aspire.Hosting.Go.Tests, Aspire.Hosting.JavaScript.Tests, Aspire.Hosting.Kafka.Tests, Aspire.Hosting.Keycloak.Tests, Aspire.Hosting.Kubernetes.Tests, Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests, Aspire.Hosting.MongoDB.Tests, Aspire.Hosting.MySql.Tests, Aspire.Hosting.Nats.Tests, Aspire.Hosting.OpenAI.Tests, Aspire.Hosting.Oracle.Tests, Aspire.Hosting.Orleans.Tests, Aspire.Hosting.PostgreSQL.Tests, Aspire.Hosting.Python.Tests, Aspire.Hosting.Qdrant.Tests, Aspire.Hosting.RabbitMQ.Tests, Aspire.Hosting.Radius.Tests, Aspire.Hosting.Redis.Tests, Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests, Aspire.Hosting.SqlServer.Tests, Aspire.Hosting.Testing.Tests, Aspire.Hosting.Tests, Aspire.Hosting.Valkey.Tests, Aspire.Hosting.Yarp.Tests, Aspire.Playground.Tests

Selected jobs (4)

deployment-e2e, extension-e2e, polyglot, typescript-api-compat


How these were chosen — grouped by what changed

⚠️ 43 of the 48 selected test projects come from a single change — src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs.

🔧 src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs (changed source)
43 via the project graph

show 43

Aspire.Hosting.Analyzers.Tests (2 hops), Aspire.Hosting.Azure.Kubernetes.Tests (2 hops), Aspire.Hosting.Azure.Kusto.Tests (2 hops), Aspire.Hosting.Azure.Tests, Aspire.Hosting.Browsers.Tests (2 hops), Aspire.Hosting.CodeGeneration.Go.Tests, Aspire.Hosting.CodeGeneration.Java.Tests, Aspire.Hosting.CodeGeneration.Python.Tests, Aspire.Hosting.CodeGeneration.Rust.Tests, Aspire.Hosting.CodeGeneration.TypeScript.Tests, Aspire.Hosting.Containers.Tests (2 hops), Aspire.Hosting.DevTunnels.Tests (2 hops), Aspire.Hosting.Docker.Tests (2 hops), Aspire.Hosting.DotnetTool.Tests (2 hops), Aspire.Hosting.EntityFrameworkCore.Tests (2 hops), Aspire.Hosting.Foundry.Tests (2 hops), Aspire.Hosting.Garnet.Tests (2 hops), Aspire.Hosting.GitHub.Models.Tests (2 hops), Aspire.Hosting.Go.Tests (2 hops), Aspire.Hosting.JavaScript.Tests (2 hops), Aspire.Hosting.Kafka.Tests (2 hops), Aspire.Hosting.Keycloak.Tests (2 hops), Aspire.Hosting.Kubernetes.Tests (2 hops), Aspire.Hosting.Maui.Tests, Aspire.Hosting.Milvus.Tests (2 hops), Aspire.Hosting.MongoDB.Tests (2 hops), Aspire.Hosting.MySql.Tests (2 hops), Aspire.Hosting.Nats.Tests (2 hops), Aspire.Hosting.OpenAI.Tests (2 hops), Aspire.Hosting.Oracle.Tests (2 hops), Aspire.Hosting.Orleans.Tests (2 hops), Aspire.Hosting.PostgreSQL.Tests (2 hops), Aspire.Hosting.Python.Tests (2 hops), Aspire.Hosting.Qdrant.Tests (2 hops), Aspire.Hosting.RabbitMQ.Tests (2 hops), Aspire.Hosting.Redis.Tests (2 hops), Aspire.Hosting.RemoteHost.Tests, Aspire.Hosting.Seq.Tests (2 hops), Aspire.Hosting.SqlServer.Tests (2 hops), Aspire.Hosting.Testing.Tests (2 hops), Aspire.Hosting.Valkey.Tests (2 hops), Aspire.Hosting.Yarp.Tests (2 hops), Aspire.Playground.Tests

🧪 tests/Aspire.Hosting.Tests/Dcp/DcpExecutorTests.cs (changed test)
1 directly: Aspire.Hosting.Tests
3 via the project graph: Aspire.Hosting.Blazor.Tests, Aspire.Hosting.Dotnet.Tests, Aspire.Hosting.Radius.Tests

📦 affected project Aspire.Hosting
1 test: Aspire.EndToEnd.Tests

🧪 tests/Aspire.Hosting.Tests/Dcp/GatedLogger.cs (changed test)
1 directly: Aspire.Hosting.Tests

🧪 tests/Aspire.Hosting.Tests/Dcp/GatedReadStream.cs (changed test)
1 directly: Aspire.Hosting.Tests

🧪 tests/Aspire.Hosting.Tests/Dcp/TestKubernetesService.cs (changed test)
1 directly: Aspire.Hosting.Tests

Job reasons

Job Triggered by
deployment-e2e affected project Aspire.Hosting.Azure
extension-e2e src/Aspire.Hosting/Dcp/DcpResourceWatcher.cs
• affected project Aspire.Hosting
polyglot affected project Aspire.Hosting.JavaScript
typescript-api-compat affected project Aspire.Hosting

Selection computed for commit b602229.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@github-actions

github-actions Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Retrying the failed CI jobs for this pull request from the CI run attempt. The rerun is being tracked in the rerun attempt.

@adamint Adam Ratzman (adamint) left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I did not find any new issues at the current head. The DCP executor suite passed, and the new UID/recreation tests caught targeted mutations in the old-resource and watch-restart paths.

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

Labels

area-app-model Issues pertaining to the APIs in Aspire.Hosting, e.g. DistributedApplication

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants