fix: AddCache concurrency race (#55) and FluentAssertions dependency leak (#56) - #60
Open
poxet wants to merge 8 commits into
Open
fix: AddCache concurrency race (#55) and FluentAssertions dependency leak (#56)#60poxet wants to merge 8 commits into
poxet wants to merge 8 commits into
Conversation
…g.Platform for xunit.v3 4.0.0
This was referenced Aug 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #55
Fixes #56
Two consumer-reported defects in the published packages, plus the dependency work the feature workflow requires up front.
#55 —
AddCacheregistration raceCacheRegistrationExtensionsmerged everyAddCachecall into a process-widestatic Dictionarywithout synchronization.ToArray()sizes its array fromCountand then copies, so a concurrentTryAddfrom another host build overran it.The reporter suggested three directions; this takes the third, and for a stronger reason than the race alone. The dictionary was process-wide state behind a per-container API — one host's type registrations leaked into an unrelated host's merged options. The tests already worked around that, calling an internal
ResetRegistrations()from their constructor andDisposepurely to stop cross-test bleed.AddCachealready replacesIOptions<CacheOptions>on every call, so the previous call's options are sitting in the collection as anImplementationInstancebefore the removal:No lock, no shared state, no cross-host leak.
_configuredPersistTypesandResetRegistrationsare gone, andAddCacheIdempotencyTestsno longer needsIDisposableor the reset hooks.New
AddCacheConcurrencyTests— 3 tests, 64 concurrent hosts. Against the old code all three failed with the reporter's exactArgumentException: Destination array is not long enoughatDictionary.CopyTo←Enumerable.ToArray, plus a concurrent-mutationInvalidOperationExceptionand the cross-collection leak.One deliberate semantic change: in a three-or-more-call sequence, a call that does not mention a type now inherits the most recent value for it rather than the first-ever one. Two-call behaviour is unchanged.
Consumers can delete
[assembly: CollectionBehavior(DisableTestParallelization = true)]if they added it for this.#56 — FluentAssertions shipped as a public dependency
Tharga.Cache.csprojdeclared FluentAssertions 8.10.0 with noPrivateAssets, propagating the Xceed Community License to every consumer and forcingNU1605on anyone pinning an older version.The reference was unused — no
using FluentAssertions, no.Should()anywhere in the library's sources — so it is removed outright rather than marked private. Verified by packing and reading the nuspec out of the.nupkg: thenet10.0group now lists onlyMicrosoft.Extensions.Hosting.Abstractions.It was a single stray line, not inherited from a shared
Directory.Build.props, but it reached all six published packages because the siblingsProjectReferencethe core.Microsoft.Testing.Platform migration
Not optional —
xunit.v34.0.0 drops the VSTest bridge on the .NET 10 SDK, sodotnet testfails outright until the projects opt in.global.jsonselects the MTP runner.<OutputType>Exe</OutputType>on all four test projects; xunit v3 4.x refuses to build a library test project.Microsoft.NET.Test.Sdk,xunit.runner.visualstudio,coverlet.collector,coverlet.msbuild— all VSTest-only here. AddedMicrosoft.Testing.Extensions.CodeCoverage.--filter "(Category!=Integration)&(Category!=TimeCritical)"→--filter-not-traitpairs;--collect:"XPlat Code Coverage"→--coverage --coverage-output-format cobertura. Four cobertura files still land in./coveragefor Codecov.coverage/added to.gitignore.Also bumped
Tharga.Blazor2.3.0 → 2.3.1.A test that asserted the opposite of its name
AddCache_CalledTwice_WithSameType_FirstRegistrationWinschecked only that the cache resolved non-null. The merged options have always let the later registration win, so the name was wrong and nothing caught it. Renamed to..._LatestRegistrationWinswith a real assertion onDefaultFreshSpan.Verification
CI-gate command green on every run: 467 tests, 465 passed, 2 skipped.
The full unfiltered suite shows failures only in
FetchDataThrottleTests— allTimeCritical-traited and excluded from CI. That class passes in isolation; this is the pre-existing flakiness already tracked in the backlog, and the new 64-host concurrency tests make it easier to hit locally by loading the CPU. Flagged rather than papered over.Version
CI computes
1.0.xfromMAJOR_MINOR: '1.0', so this lands as a patch. Worth considering 1.1.0 instead: removing the transitive FluentAssertions is a compile-time break for anyone who was unknowingly relying on it, and the registration-accumulation semantics changed. Say the word and I'll bumpMAJOR_MINOR.