Skip to content

Cherry-pick for release/2.0: pipeline & hot-reload test fixes#3518

Merged
RubenCerna2079 merged 5 commits intorelease/2.0from
dev/aaronburtle/Cherry-Pick-Pipeline-Fix-2.0
May 6, 2026
Merged

Cherry-pick for release/2.0: pipeline & hot-reload test fixes#3518
RubenCerna2079 merged 5 commits intorelease/2.0from
dev/aaronburtle/Cherry-Pick-Pipeline-Fix-2.0

Conversation

@aaronburtle
Copy link
Copy Markdown
Contributor

@aaronburtle aaronburtle commented May 6, 2026

Why make this change?

Closes #3517

Ports into release/2.0

  • Reliable cleanup of MSSQL integration tests by properly disposing the config file watcher and FileSystemRuntimeConfigLoader, eliminating intermittent IOException failures on File.Delete.
  • Faster, more balanced MSSQL integration test pipeline: tests are split into two parallel Windows jobs (combined and configuration), with a shared mssql-test-steps.yml template, and DB-independent unit tests are removed from the MSSQL category so they run in the unit-test pipeline instead.
  • Hot-reload test stability: replaced naive timeout waits with polling helpers (WaitForRestEndpointAsync and retry logic to cover the race between hot-reload validation completing and metadata providers finishing re-initialization.
  • Autoentity hot-reload test now uses the same polling pattern, removing the last known race condition in that area.Reliable cleanup of MSSQL integration tests by properly disposing the config file watcher and FileSystemRuntimeConfigLoader, eliminating intermittent IOException failures on File.Delete

What is this change?

Cherry-picked PRs (chronological order on main):

How was this tested?

Existing and newly added unit/integration tests from each source PR.

aaronburtle and others added 4 commits May 5, 2026 23:52
…3243)

## Why make this change?

Closes #2992

Several configuration tests create a TestServer that sets up a
`ConfigFileWatcher` to support hot-reload. When the file watcher detects
a change, its callback reads the file, holding a read handle. Neither
`ConfigFileWatcher` nor `FileSystemRuntimeConfigLoader` implemented
`IDisposable`, so when the TestServer is disposed, the DI container
could not clean up the file watcher. The watcher remained active, and if
its callback was mid-read when `CleanupAfterEachTest` called
`File.Delete`, the delete failed with an `IOException`

## What is this change?

`ConfigFileWatcher` now implements `IDisposable`. It stops raising
events, unsubscribes the Changed handler, and disposes the underlying
`IFileSystemWatcher`.

`FileSystemRuntimeConfigLoader` now implements `IDisposable`. It
disposes the owned `ConfigFileWatcher`. As a DI singleton, it is now
automatically disposed when the TestServer shuts down.

`CleanupAfterEachTest` retries with exponential backoff. As a safety
net, `File.Delete` is retried up to 3 times on `IOException` (2s, 4s, 8s
delays), matching the existing retry pattern used elsewhere.

## How was this tested?

Run against the pipeline. Several pipeline runs were initiated to add
certainty that the fix is working, since this issue is intermittent.

https://dev.azure.com/sqldab/Data%20API%20builder%20Dependency%20Packages/_build/results?buildId=18957&view=results

https://dev.azure.com/sqldab/Data%20API%20builder%20Dependency%20Packages/_build/results?buildId=18958&view=results

https://dev.azure.com/sqldab/Data%20API%20builder%20Dependency%20Packages/_build/results?buildId=18952&view=results

https://dev.azure.com/sqldab/Data%20API%20builder%20Dependency%20Packages/_build/results?buildId=18959&view=results

https://dev.azure.com/sqldab/Data%20API%20builder%20Dependency%20Packages/_build/results?buildId=18960&view=results

---------

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Souvik Ghosh <souvikofficial04@gmail.com>
(cherry picked from commit eb5408e)
…MSSQL category from generic unit tests (#3245)

## Why make this change?

Closes #3244

## What is this change?

This PR makes two changes to improve MSSQL integration test pipeline
performance:

1. Removed TestCategory(TestCategory.MSSQL) from 5 unit test files that
are not MSSQL-specific. These tests do not require a database and should
run in the unit test pipeline instead:

      -  RequestValidatorUnitTests.cs
      -  EntitySourceNamesParserUnitTests.cs
      -  RequestContextUnitTests.cs
      -  RestServiceUnitTests.cs
      -  RuntimeConfigLoaderJsonDeserializerTests.cs

3. Split the single Windows MSSQL integration test job into 2 parallel
jobs to reduce execution time:

- Windows - Combined Integration Tests: GraphQL, REST, Unit, HotReload,
OpenApi, Authorization, Telemetry, and Caching tests (1,246 tests).
SqlTestBase-inheriting tests (GraphQL, REST) naturally create the DB
schema for the co-located tests.

- Window - Configuration Tests: Pure ConfigurationTests (207 tests).
Since this job has no SqlTestBase tests, a System.Data.SqlClient-based
schema initialization step creates the DB schema before test execution.

A new reusable step template `mssql-test-steps.yml` avoids duplicating
the ~130 lines of shared setup (NuGet auth, .NET SDK, LocalDB
install/start, build, test, coverage publish) across the 2 jobs. It
includes an optional `initDbSchema` parameter for jobs that need
explicit schema creation.

## How was this tested?

Run against the pipeline.

---------

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
(cherry picked from commit f146ac2)
…zation race condition (#3302)

## Why make this change?

Related to #2992

Some flakey tests were being masked by faster initialization when the
task they were in was isolated. When we refactored the tests to lower
the overall runtime in this PR
#3245 these flakey tests
were eventually revealed.

The root cause was a race condition: after a successful hot-reload,
`WaitForConditionAsync` detects the "Validated hot-reloaded
configuration file" console message and returns, but the engine's
metadata providers have not fully re-initialized yet. An immediate HTTP
request can arrive before the metadata is ready, causing a 500 error.

## What is this change?

Added retry logic to the three non-ignored tests that make HTTP calls
expecting success responses immediately after hot-reload:

* `HotReloadConfigRuntimePathsEndToEndTest`: REST and GraphQL calls now
retry up to 5 times with 1 second delays
* `HotReloadConfigConnectionString`: REST call now uses
`WaitForRestEndpointAsync` helper
* `HotReloadConfigDatabaseType`: REST call now uses
`WaitForRestEndpointAsync` helper

Added a shared `WaitForRestEndpointAsync` helper method that polls a
REST endpoint until it returns the expected status code (5 retries, 1
second delay).

This follows the same retry pattern already established in
`HotReloadConfigDataSource`, which had this exact fix applied
previously.

## How was this tested?

Ran a batch of 10 pipeline runs which all succeeded.

<img width="1597" height="790" alt="image"
src="https://github.com/user-attachments/assets/d7665b53-ba53-45b2-9c8c-333e4e296551"
/>

(cherry picked from commit 16fdda3)
## Why make this change?

Closes #3319

## What is this change?

Apply the same pattern for making the rest request in this test as in
the rest of the test file.

## How was this tested?

Ran against pipeline.

(cherry picked from commit de235fb)
Copilot AI review requested due to automatic review settings May 6, 2026 08:15
@aaronburtle aaronburtle self-assigned this May 6, 2026
@aaronburtle aaronburtle added the 2.0 label May 6, 2026
@aaronburtle aaronburtle moved this from Todo to Review In Progress in Data API builder May 6, 2026
@aaronburtle aaronburtle added this to the May 2026 milestone May 6, 2026
@aaronburtle aaronburtle changed the base branch from main to release/2.0 May 6, 2026 08:17
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This cherry-pick ports several reliability and CI pipeline improvements into release/2.0, focusing on stabilizing MSSQL test execution (file watcher disposal + hot-reload race fixes) and reducing overall pipeline time by splitting MSSQL tests into parallel Windows jobs.

Changes:

  • Ensure config hot-reload file watchers are disposable and can be cleaned up reliably to prevent intermittent IOException on config file deletion.
  • Stabilize hot-reload tests by replacing fixed waits with REST/GraphQL polling helpers to tolerate metadata re-initialization races.
  • Speed up MSSQL CI by splitting Windows MSSQL tests into two parallel jobs and moving DB-independent unit tests out of the MSSQL category; introduce a shared MSSQL pipeline steps template.

Reviewed changes

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

Show a summary per file
File Description
src/Service/Startup.cs Registers FileSystemRuntimeConfigLoader via factory so DI can dispose it at shutdown.
src/Config/FileSystemRuntimeConfigLoader.cs Implements IDisposable to tear down the config file watcher and release file handles.
src/Config/ConfigFileWatcher.cs Implements IDisposable to stop events/unsubscribe and dispose the underlying watcher.
src/Core/Services/MetadataProviders/SqlMetadataProvider.cs Removes generated autoentities during validate-only initialization to avoid config mutation side effects.
src/Core/Services/MetadataProviders/MsSqlMetadataProvider.cs Enhances autoentity generation with definition-aware querying/logging and marks generated entities as autoentities.
src/Core/Configurations/RuntimeConfigProvider.cs Adds removal of generated autoentities from runtime config (used in validate-only flow).
src/Config/ObjectModel/RuntimeConfig.cs Adds removal helper for entity→datasource mapping and clarifies autoentity-not-found messaging.
src/Config/ObjectModel/Entity.cs Introduces IsAutoentity (JsonIgnore) to tag auto-generated entities.
src/Service.Tests/ModuleInitializer.cs Updates snapshot verifier config to ignore Entity.IsAutoentity.
src/Cli.Tests/ModuleInitializer.cs Updates snapshot verifier config to ignore Entity.IsAutoentity.
src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs Adds polling helpers and a new autoentity hot-reload test to reduce flakiness.
src/Service.Tests/Configuration/ConfigurationTests.cs Adds retry/backoff for config file deletion and improves TestServer disposal patterns; updates expected autoentity conflict message.
src/Service.Tests/UnitTests/SqlMetadataProviderUnitTests.cs Updates unit test callsite for new QueryAutoentitiesAsync signature.
src/Service.Tests/UnitTests/RuntimeConfigLoaderJsonDeserializerTests.cs Removes MSSQL test category so it runs with unit tests instead of MSSQL pipeline.
src/Service.Tests/UnitTests/RestServiceUnitTests.cs Removes MSSQL test category so it runs with unit tests instead of MSSQL pipeline.
src/Service.Tests/UnitTests/RequestValidatorUnitTests.cs Removes MSSQL test category so it runs with unit tests instead of MSSQL pipeline.
src/Service.Tests/UnitTests/RequestContextUnitTests.cs Removes MSSQL test category so it runs with unit tests instead of MSSQL pipeline.
src/Service.Tests/UnitTests/EntitySourceNamesParserUnitTests.cs Removes MSSQL test category so it runs with unit tests instead of MSSQL pipeline.
src/Cli/ConfigGenerator.cs Adjusts dab start behavior so --LogLevel is only passed when explicitly provided.
src/Cli/Commands/AutoConfigOptions.cs Renames auto-config option to template.mcp.dml-tools for consistency.
src/Cli.Tests/AutoConfigTests.cs Updates tests for renamed auto-config option parameter.
src/Cli.Tests/EndToEndTests.cs Adds a test asserting Startup.IsLogLevelOverriddenByCli behavior (but currently has a lifecycle issue; see PR comments).
schemas/dab.draft.schema.json Updates autoentities template mcp schema to allow boolean shorthand or object form.
.pipelines/templates/mssql-test-steps.yml New shared Azure Pipelines template for MSSQL job setup/test execution.
.pipelines/mssql-pipelines.yml Splits Windows MSSQL tests into parallel “combined” and “configuration” jobs using the shared template.
Comments suppressed due to low confidence (1)

src/Service.Tests/Configuration/HotReload/ConfigurationHotReloadTests.cs:832

  • HotReloadAutoentities creates multiple HttpResponseMessage instances (restResult, failRestResult) that are not disposed. Because this test performs retries/polling and additional requests, leaving responses undisposed can unnecessarily hold connections/resources and may contribute to flakiness. Prefer using (or explicit Dispose) for all HttpResponseMessage instances created in the test.
        // Act
        HttpResponseMessage restResult = await _testClient.GetAsync($"rest/autoentity_books");

        GenerateConfigFile(
            connectionString: $"{ConfigurationTests.GetConnectionStringFromEnvironmentConfig(TestCategory.MSSQL).Replace("\\", "\\\\")}",
            autoentityName: "HotReload_{object}");
        await WaitForConditionAsync(
          () => WriterContains(HOT_RELOAD_SUCCESS_MESSAGE),
          TimeSpan.FromSeconds(HOT_RELOAD_TIMEOUT_SECONDS),
          TimeSpan.FromMilliseconds(500));

        // After hot-reload, the engine may still be re-initializing metadata providers.
        // Poll the REST endpoint to allow time for the engine to become fully ready.
        using HttpResponseMessage hotReloadRestResult = await WaitForRestEndpointAsync("rest/HotReload_books", HttpStatusCode.OK);

        // Once the engine is fully ready, verify the old autoentity name is no longer recognized.
        HttpResponseMessage failRestResult = await _testClient.GetAsync($"rest/autoentity_books");

        // Assert
        Assert.AreEqual(HttpStatusCode.OK, restResult.StatusCode,
                    $"REST request before hot-reload failed when it was expected to succeed. Response: {await restResult.Content.ReadAsStringAsync()}");
        Assert.AreEqual(HttpStatusCode.NotFound, failRestResult.StatusCode,
                    $"REST request after hot-reload succeeded when it was expected to fail. Response: {await failRestResult.Content.ReadAsStringAsync()}");
        Assert.AreEqual(HttpStatusCode.OK, hotReloadRestResult.StatusCode,

@RubenCerna2079 RubenCerna2079 enabled auto-merge (squash) May 6, 2026 18:26
@RubenCerna2079 RubenCerna2079 merged commit b3a41a0 into release/2.0 May 6, 2026
12 checks passed
@RubenCerna2079 RubenCerna2079 deleted the dev/aaronburtle/Cherry-Pick-Pipeline-Fix-2.0 branch May 6, 2026 18:52
@github-project-automation github-project-automation Bot moved this from Review In Progress to Done in Data API builder May 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Cherry pick pipeline fixes for release/2.0

5 participants