Skip to content

Raise test coverage to 98% and drop dead SaveChangesFailed handling - #2

Merged
GabrielMarquezMatte merged 3 commits into
masterfrom
develop
Sep 28, 2026
Merged

GabrielMarquezMatte merged 3 commits into
masterfrom
develop

Conversation

@GabrielMarquezMatte

@GabrielMarquezMatte GabrielMarquezMatte commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

What does this change?

Raises line coverage of the three packages from 87.6% to 98.5% (unit + integration; unit alone 86.4% → 97.0%), mostly in the EF Core invalidation interceptors, which were at 61%. It also removes the SaveChangesFailed handling from QueryCacheInvalidation: it reset state that the next SavingChanges always overwrites, so it had no effect. No public API or behavior change. The PR also carries the v0.1.0 public API promotion (PublicAPI.Unshipped.txt → PublicAPI.Shipped.txt) merged into develop in #1.

Package Unit Unit + integration
QueryCache.Core 97.0% 98.8%
QueryCache.Dapper 100% 100%
QueryCache.EFCore 96.0% 97.6%

New tests:

  • EF Core: synchronous SaveChanges, synchronous and asynchronous commit and rollback, a failed SaveChanges keeps cached entries, ToDictionaryCachedAsync with a key comparer, FirstCachedAsync with a row, a string filter in the query.
  • PostgreSQL: completing a TransactionScope invalidates what another connection cached while the scope was open.
  • HybridCache: a synchronous SaveChanges returns only after the invalidation reached the distributed cache, and a value invalidated during its fill is never written there. The fake distributed cache in the tests now writes asynchronously and records its writes, so both can be checked.
  • Core and Dapper: lazy (iterator) results, empty and non-empty; QueryKey object equality and ToString; DapperCacheQuery equality, hash code and ToString; DynamicParameters filled with Add.

CA1849 is suppressed in the unit test project only: those tests call SaveChanges, Commit and Rollback synchronously on purpose, to cover the synchronous interceptors.

The 5 lines still uncovered are defensive or depend on a race: null context or missing state in the interceptors, other EF expression node types in TableTags, the LRU update callback, and an invalidation landing exactly during the HybridCache write.

Checklist

  • dotnet build QueryCache.slnx --configuration Release builds clean (warnings are errors)
  • dotnet test --project tests/QueryCache.Tests/QueryCache.Tests.csproj --configuration Release passes (79 tests)
  • Integration tests pass (31 tests: SQL Server, PostgreSQL, Redis)
  • Public API unchanged
  • Tests added/updated for the behavior change
  • One focused change

Test plan

  • Ran both suites with coverage and compared line coverage per package against master.
  • Broke 7 code paths one at a time: synchronous SavedChanges, synchronous commit, the synchronous wait on HybridCache invalidation, the HybridCache check that skips storing an invalidated value, the TransactionScope handler, and two more. The new tests caught all 7.
  • Ran the unit suite 3 times to rule out flakiness.

@codecov-commenter

Copy link
Copy Markdown

Welcome to Codecov 🎉

Once you merge this PR into your default branch, you're all set! Codecov will compare coverage reports and display results in all future pull requests.

Thanks for integrating Codecov - We've got you covered ☂️

@github-actions

Copy link
Copy Markdown

Benchmark Results

No published baseline was found, so these are absolute numbers only. They come from a shared ubuntu-latest runner and cannot be compared against another machine's.

Full results per group

QueryCache.Benchmarks.CacheBenchmarks


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
INTEL XEON PLATINUM 8573C 2.30GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Categories Rows Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
Dapper_Direct Dapper 10 14,744.5 ns 259.69 ns 67.44 ns 1.00 0.01 0.0305 3312 B 1.00
Dapper_CacheHit Dapper 10 398.5 ns 10.66 ns 1.65 ns 0.03 0.00 0.0081 688 B 0.21
Dapper_CacheMiss Dapper 10 16,687.0 ns 315.50 ns 81.93 ns 1.13 0.01 0.0305 5008 B 1.51
Dapper_Direct Dapper 1000 853,711.9 ns 5,976.73 ns 924.91 ns 1.000 0.00 0.9766 138384 B 1.000
Dapper_CacheHit Dapper 1000 400.0 ns 4.53 ns 0.70 ns 0.000 0.00 0.0081 688 B 0.005
Dapper_CacheMiss Dapper 1000 866,642.5 ns 12,114.07 ns 3,145.98 ns 1.015 0.00 0.9766 140080 B 1.012
Ef_Direct EF 10 25,399.3 ns 15,304.11 ns 2,368.33 ns 1.01 0.11 - 9536 B 1.00
Ef_CacheHit EF 10 6,307.1 ns 111.79 ns 17.30 ns 0.25 0.02 0.0534 4872 B 0.51
Ef_CacheMiss EF 10 52,799.9 ns 46,830.11 ns 12,161.63 ns 2.09 0.47 0.2441 26867 B 2.82
Ef_Direct EF 1000 853,436.3 ns 29,632.65 ns 7,695.50 ns 1.000 0.01 1.9531 287168 B 1.00
Ef_CacheHit EF 1000 6,312.4 ns 84.35 ns 21.91 ns 0.007 0.00 0.0534 4872 B 0.02
Ef_CacheMiss EF 1000 882,220.1 ns 26,817.98 ns 4,150.11 ns 1.034 0.01 1.9531 304523 B 1.06

QueryCache.Benchmarks.EfMissBreakdownBenchmarks


BenchmarkDotNet v0.15.8, Linux Ubuntu 24.04.5 LTS (Noble Numbat)
Intel Xeon 6973P-C 2.60GHz, 1 CPU, 4 logical and 2 physical cores
.NET SDK 10.0.401
  [Host]     : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4
  Job-MEHJPP : .NET 10.0.12 (10.0.12, 10.0.1226.42308), X64 RyuJIT x86-64-v4

IterationCount=5  WarmupCount=1  

Method Mean Error StdDev Ratio RatioSD Gen0 Allocated Alloc Ratio
Direct 15.917 μs 0.1772 μs 0.0274 μs 1.00 0.00 0.0610 9.31 KB 1.00
CommandAndKey 4.169 μs 0.0172 μs 0.0045 μs 0.26 0.00 0.0534 4.6 KB 0.49
TableTagsOnly 1.989 μs 0.0329 μs 0.0085 μs 0.12 0.00 0.0420 3.48 KB 0.37
InvalidateOnly 5.646 μs 0.0218 μs 0.0057 μs 0.35 0.00 0.0916 7.8 KB 0.84
StoreMissAroundDirect 17.040 μs 0.3276 μs 0.0507 μs 1.07 0.00 0.0610 9.93 KB 1.07
DirectAfterCommand 23.002 μs 15.2394 μs 3.9576 μs 1.45 0.23 0.1221 13.41 KB 1.44
DirectVarying 28.295 μs 33.2187 μs 5.1406 μs 1.78 0.29 0.1221 13.22 KB 1.42
MissVarying 43.160 μs 91.4899 μs 14.1582 μs 2.71 0.80 0.2441 23.29 KB 2.50
FullMiss 42.547 μs 51.7588 μs 13.4416 μs 2.67 0.77 0.2441 26.32 KB 2.83

@GabrielMarquezMatte
GabrielMarquezMatte merged commit 936613c into master Sep 28, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants