From 2181a7461fcaf7a8b80da20f44f55216fbe19906 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 27 Oct 2026 03:51:01 +0100 Subject: [PATCH] Tests: the racing cache holds its records in a routed store Co-Authored-By: Claude Sonnet 5.5 --- .../CachelessSingleFileTests.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/tests/GameCult.Caching.Tests/CachelessSingleFileTests.cs b/tests/GameCult.Caching.Tests/CachelessSingleFileTests.cs index b09213b5..384d2755 100644 --- a/tests/GameCult.Caching.Tests/CachelessSingleFileTests.cs +++ b/tests/GameCult.Caching.Tests/CachelessSingleFileTests.cs @@ -1441,6 +1441,13 @@ namespace GameCult.Caching.Tests Assert.That(CacheBackingStore.GateTakes, Is.EqualTo(1), "a settled store's call takes only its cache's gate"); } + [CultDocument("tests.gate_filler", "tests.gate_filler.v1")] + [MessagePackObject(AllowPrivate = true)] + internal sealed class GateFiller + { + [Key(0)] [CultName] public string Name = string.Empty; + } + // Under attaches that succeed, a call takes at most three gates, and three is reachable: the guess was null, an attach then // settled before the call took the store's gate, and the call took the settled cache's gate as its third. [Test] @@ -1449,6 +1456,7 @@ namespace GameCult.Caching.Tests var rounds = 0; var registry = CultDocumentRegistry.ForTypes(new[] { typeof(IdDeck) }); new SingleFileMessagePackBackingStore(_path).WriteDocument(new CultRecordKey("a"), new IdDeck { Name = "a" }, registry: registry); + var withFiller = CultDocumentRegistry.ForTypes(new[] { typeof(IdDeck), typeof(GateFiller) }); var mostTakes = 0; var deadline = DateTime.UtcNow + TimeSpan.FromSeconds(60); for (var round = 0; mostTakes < 3 && DateTime.UtcNow < deadline; round++) @@ -1456,10 +1464,15 @@ namespace GameCult.Caching.Tests rounds++; var store = new RoleStore(_path); store.ReleaseAttach.Set(); - // A cache holding many records makes the attach's checks before it publishes Cache take long enough to race. - var cache = new CultCache(registry); - for (var index = 0; index < 20000; index++) - cache.AddAsync(new IdDeck { Name = "held" + index }).GetAwaiter().GetResult(); + // A cache holding many records in a store of its own makes the attach's checks before it publishes Cache take long + // enough to race. + var cache = new CultCache(withFiller); + cache.AddBackingStore(new SingleFileMessagePackBackingStore(_path + ".filler" + round), typeof(GateFiller)); + cache.Commit(batch => + { + for (var index = 0; index < 20000; index++) + batch.Upsert(new GateFiller { Name = "held" + index }, new CultRecordHandle(new CultRecordKey("held" + index))); + }); using var barrier = new Barrier(2); var takes = 0; var spin = (round % 200) * 400; @@ -1481,6 +1494,7 @@ namespace GameCult.Caching.Tests Finishes(caller, "a calling thread"); Finishes(attacher, "an attaching thread"); cache.Dispose(); + File.Delete(_path + ".filler" + round); Assert.That(takes, Is.InRange(1, 3), "no call took more than three gates"); mostTakes = Math.Max(mostTakes, takes); }