Summary
A failure in an asynchronous publish or materialization task causes CacheClient.ShutdownAsync to throw before MSBuildCachePluginBase.EndBuildInnerAsync calls DisposeAsync.
Impact
Cleanup is skipped on a realistic network or placement failure. The static single-plugin semaphore and cache directory lock remain held, while cache sessions, hashers, and log handles remain undisposed. In a reused MSBuild node, later builds can be permanently rejected as though another cache instance were still running.
Evidence
src/Common/Caching/CacheClient.cs drains background tasks and throws an AggregateException when any task fails. src/Common/MSBuildCachePluginBase.cs then executes shutdown and disposal sequentially without a finally block:
await _cacheClient.ShutdownAsync(cancellationToken);
await DisposeAsync();
Async publishing and materialization are enabled by default.
Suggested fix
Guarantee disposal with exception-safe shutdown, while preserving the original shutdown failure. Add coverage with a cache client whose background operation faults and assert that all locks and resources are released.
Acceptance criteria
DisposeAsync runs even when ShutdownAsync fails.
- The shutdown exception remains observable.
- A subsequent plugin instance in the same process can acquire the single-instance lock.
- Cache/session disposal is verified by a focused test.
Summary
A failure in an asynchronous publish or materialization task causes
CacheClient.ShutdownAsyncto throw beforeMSBuildCachePluginBase.EndBuildInnerAsynccallsDisposeAsync.Impact
Cleanup is skipped on a realistic network or placement failure. The static single-plugin semaphore and cache directory lock remain held, while cache sessions, hashers, and log handles remain undisposed. In a reused MSBuild node, later builds can be permanently rejected as though another cache instance were still running.
Evidence
src/Common/Caching/CacheClient.csdrains background tasks and throws anAggregateExceptionwhen any task fails.src/Common/MSBuildCachePluginBase.csthen executes shutdown and disposal sequentially without afinallyblock:Async publishing and materialization are enabled by default.
Suggested fix
Guarantee disposal with exception-safe shutdown, while preserving the original shutdown failure. Add coverage with a cache client whose background operation faults and assert that all locks and resources are released.
Acceptance criteria
DisposeAsyncruns even whenShutdownAsyncfails.