Skip to content

Add Nitro seed update monitoring to Fusion Aspire - #10175

Merged
michaelstaib merged 3 commits into
mainfrom
mst/nitro-seed-refresh
Jul 31, 2026
Merged

michaelstaib merged 3 commits into
mainfrom
mst/nitro-seed-refresh

Conversation

@michaelstaib

Copy link
Copy Markdown
Member

No description provided.

Copilot AI review requested due to automatic review settings July 31, 2026 13:09

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

Adds Nitro stage “seed update” monitoring to Fusion Aspire so AppHost runs can subscribe/query stage changes, download refreshed Fusion archives, and either auto-adopt them (triggering recomposition) or stage them for later. This extends the existing Nitro integration beyond startup-time seed acquisition and schema validation.

Changes:

  • Introduces stage update subscription/query plumbing (GraphQL operations + client) and a background monitor/service that downloads and adopts or stages refreshed seeds.
  • Adds Aspire resource commands to enable/disable Nitro auto-update, and wires seed adoption into recomposition flows (including manual recomposition fetching a fresh seed).
  • Expands test coverage across monitoring behavior, race handling, and command registration/state.

Reviewed changes

Copilot reviewed 27 out of 27 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/HotChocolate/Fusion/test/Fusion.Aspire.Tests/NitroExtensionsTests.cs Adds tests for seed update options and auto-update command registration/state.
src/HotChocolate/Fusion/test/Fusion.Aspire.Tests/Nitro/NitroStageUpdateClientTests.cs Adds tests for stage update subscription/query ordering and header propagation.
src/HotChocolate/Fusion/test/Fusion.Aspire.Tests/Nitro/NitroSeedUpdateMonitorTests.cs Adds comprehensive tests for the stage-change monitor behavior (adoption, staging, retries, backoff).
src/HotChocolate/Fusion/test/Fusion.Aspire.Tests/Nitro/NitroSchemaCompositionTests.cs Verifies recomposition paths adopt fresh/staged seeds correctly and handles download failure fallback.
src/HotChocolate/Fusion/test/Fusion.Aspire.Tests/Nitro/NitroOperationDocumentsTests.cs Validates new stage-update operation documents/IDs match embedded resources.
src/HotChocolate/Fusion/test/Fusion.Aspire.Tests/CompositionHarness.cs Updates test harness wiring to include the new seed update service while disabling monitoring by default for tests.
src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaCompositionRegistration.cs Registers seed update notifier/service and TimeProvider in DI.
src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs Starts seed monitoring on gateway start; integrates staged/manual adoption into recomposition flows with rollback/reporting.
src/HotChocolate/Fusion/src/Fusion.Aspire/NitroExtensions.cs Adds AddNitro overload for seed update configuration and adds enable/disable auto-update commands to gateways.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/Operations/WatchNitroStage.graphql.sha256 Adds persisted-operation hash sidecar for the stage watch subscription.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/Operations/WatchNitroStage.graphql Adds subscription document for stage-change events (fusion config + client events).
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/Operations/GetNitroStageVersion.graphql.sha256 Adds persisted-operation hash sidecar for querying the latest stage version.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/Operations/GetNitroStageVersion.graphql Adds query document for current fusion configuration/client version state.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroStageUpdateClient.cs Implements subscription + snapshot querying for stage changes via GraphQL over SSE.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroStageSnapshot.cs Adds snapshot/change model + deterministic identity hashing for version deduplication.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateState.cs Adds candidate/snapshot/adoption state records used by coordinator/monitor.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateService.cs Adds per-gateway monitor lifecycle, command handling, and notification deduping/reporting.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateOptions.cs Adds public options controlling detection and auto-update behavior.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateNotifier.cs Adds Aspire interaction-based notifications for staged/adopted updates.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateMonitor.cs Implements background subscription/query loop, reconnect/backoff, download+adopt/stage logic.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedCoordinator.cs Extends coordinator to track staged/adopted state, schema hashing, and stage update client integration.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroOperationDocuments.cs Adds stage update operation documents/IDs to embedded resource resolver.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroGatewaySeed.cs Adds schema hash to the seed record for dedupe/adoption decisions.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroCompositionOptions.cs Adds SeedUpdates options bag to composition options.
src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/INitroStageUpdateClient.cs Introduces stage update client abstraction + subscription reader interface.
src/HotChocolate/Fusion/src/Fusion.Aspire/HotChocolate.Fusion.Aspire.csproj Adds Polly dependency and embeds new operation documents/sidecars.
src/HotChocolate/Fusion/src/Fusion.Aspire/GraphQLResourceBuilderExtensions.cs Ensures auto-update commands are attempted when enabling schema composition on a resource.

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

Comment on lines +89 to +93
public static IDistributedApplicationBuilder AddNitro(
this IDistributedApplicationBuilder builder,
string stage,
Action<NitroSeedUpdateOptions> configureSeedUpdates,
Uri? portalUrl = null)
Comment on lines +323 to +327
UpdateState = context =>
{
var service = context.ServiceProvider.GetService<NitroSeedUpdateService>();
if (service is null || !service.IsEnabled)
{
Comment on lines +495 to 499
var filePath = IOPath.Combine(
_runSeedDirectory,
$"{gatewayName}.{Interlocked.Increment(ref _nextRunSeedId):D8}.far");

File.Copy(seedFilePath, filePath, overwrite: true);
Comment on lines 126 to 130
var options = SchemaCompositionRegistration.Ensure(builder);
configureSeedUpdates?.Invoke(options.SeedUpdates);

if (options.Coordinator is { } coordinator)
{

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 27 out of 27 changed files in this pull request and generated no new comments.

Suppressed comments (1)

src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedCoordinator.cs:590

  • ComputeSchemaHashAsync only catches IOException, but FusionArchive.TryGetGatewayConfigurationAsync(...) can throw InvalidOperationException when the archive metadata has no supported gateway formats (e.g., older/partial archives). That would cause seed acquisition/refresh to fail instead of falling back to hashing the whole archive as the comment intends.
    private static async Task<string> ComputeSchemaHashAsync(
        string archivePath,
        CancellationToken cancellationToken)
    {
        try
        {
            using var archive = FusionArchive.Open(archivePath);
            using var configuration = await archive.TryGetGatewayConfigurationAsync(
                WellKnownVersions.LatestGatewayFormatVersion,
                cancellationToken);

            if (configuration is not null)
            {
                await using var schema = await configuration.OpenReadSchemaAsync(cancellationToken);
                return Convert.ToHexString(await SHA256.HashDataAsync(schema, cancellationToken));
            }
        }
        catch (IOException)
        {
            // A seed produced by an older Nitro version can contain only source configurations.
            // Hashing the complete immutable archive still gives the refresh path a stable guard.
        }

@michaelstaib
michaelstaib merged commit c29c711 into main Jul 31, 2026
4 of 5 checks passed
@michaelstaib
michaelstaib deleted the mst/nitro-seed-refresh branch July 31, 2026 13:39
@github-actions

Copy link
Copy Markdown
Contributor

Patch coverage

75.6% of changed lines covered (977/1293)

File Covered Changed Patch %
…/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateNotifier.cs 0 28 0.0% 🔴
…/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs 84 146 57.5% 🔴
…/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateMonitor.cs 255 360 70.8% 🔴
…/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateService.cs 91 128 71.1% 🔴
…/Fusion/src/Fusion.Aspire/Nitro/NitroStageSnapshot.cs 51 71 71.8% 🔴
…/Fusion/src/Fusion.Aspire/Nitro/NitroStageUpdateClient.cs 150 182 82.4% 🟡
src/HotChocolate/Fusion/src/Fusion.Aspire/NitroExtensions.cs 72 82 87.8% 🟡
…/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateState.cs 20 22 90.9% 🟡
…/Fusion/src/Fusion.Aspire/Nitro/NitroSeedCoordinator.cs 243 263 92.4% 🟡
…/Fusion/src/Fusion.Aspire/GraphQLResourceBuilderExtensions.cs 1 1 100.0% 🟢
…/Fusion/src/Fusion.Aspire/Nitro/NitroCompositionOptions.cs 1 1 100.0% 🟢
…/Fusion/src/Fusion.Aspire/Nitro/NitroGatewaySeed.cs 2 2 100.0% 🟢
…/Fusion/src/Fusion.Aspire/Nitro/NitroOperationDocuments.cs 2 2 100.0% 🟢
…/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateOptions.cs 2 2 100.0% 🟢
…/Fusion/src/Fusion.Aspire/SchemaCompositionRegistration.cs 3 3 100.0% 🟢
Uncovered changed lines (JSON)
{
  "sha": "2a02fa2373a1151ccb1ac9ad43dec588360b203d",
  "files": [
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateNotifier.cs", "ranges": [[15, 18], [21, 21], [23, 23], [26, 29], [32, 33], [36, 36], [38, 52]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/SchemaComposition.cs", "ranges": [[680, 688], [690, 690], [706, 711], [766, 768], [781, 783], [785, 792], [811, 815], [823, 827], [829, 833], [835, 841], [844, 847], [849, 852], [854, 855]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateMonitor.cs", "ranges": [[121, 122], [130, 137], [139, 151], [155, 161], [180, 180], [184, 190], [242, 244], [277, 279], [281, 284], [309, 315], [335, 336], [344, 346], [348, 351], [357, 357], [372, 376], [380, 382], [392, 394], [407, 408], [416, 422], [440, 440], [442, 445], [447, 447], [449, 454], [457, 457], [460, 463], [466, 468]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateService.cs", "ranges": [[88, 89], [99, 100], [116, 116], [119, 121], [123, 125], [128, 128], [130, 132], [150, 158], [183, 184], [186, 188], [190, 196], [210, 210]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroStageSnapshot.cs", "ranges": [[55, 58], [65, 75], [78, 82]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroStageUpdateClient.cs", "ranges": [[47, 47], [49, 52], [55, 57], [95, 96], [100, 101], [116, 117], [178, 179], [226, 227], [232, 236], [240, 246], [256, 257]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/NitroExtensions.cs", "ranges": [[302, 303], [311, 318]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedUpdateState.cs", "ranges": [[15, 15], [19, 19]] },
    { "path": "src/HotChocolate/Fusion/src/Fusion.Aspire/Nitro/NitroSeedCoordinator.cs", "ranges": [[346, 348], [396, 397], [455, 456], [516, 517], [540, 540], [580, 585], [602, 605]] }
  ]
}

Project coverage: 54.3% (237219/437108 lines)

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants