Caching tests: pin the variant refusal message and ReadDurable leaving the store view alone; start TwoWriters rounds together
Some checks failed
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 20m27s
Some checks failed
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 20m27s
The directory race test no longer collided once each attempt reads fresh, so each round now starts both writers at a barrier. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
a020247fcc
commit
d3edf80ed3
1 changed files with 26 additions and 1 deletions
|
|
@ -78,16 +78,22 @@ namespace GameCult.Caching.Tests
|
|||
using var a = Open(path, directory);
|
||||
using var b = Open(path, directory);
|
||||
var applied = 0;
|
||||
// Each round starts both writers together: an attempt reads fresh from the store, so only overlapping read-commit windows lose.
|
||||
var round = new Barrier(2);
|
||||
|
||||
var racers = new[] { a, b }.Select(cache => Task.Run(() =>
|
||||
{
|
||||
var results = new List<CultUpdateOutcome>();
|
||||
for (var i = 0; i < n; i++)
|
||||
{
|
||||
round.SignalAndWait();
|
||||
results.Add(cache.Update<UpdateCounter>(Key, counter =>
|
||||
{
|
||||
Interlocked.Increment(ref applied);
|
||||
return Plus(counter, 1);
|
||||
}, maxAttempts: 200).Outcome);
|
||||
}
|
||||
|
||||
return results;
|
||||
})).ToArray();
|
||||
Assert.That(Task.WaitAll(racers, TimeSpan.FromMinutes(2)), Is.True, "the racers did not finish");
|
||||
|
|
@ -252,7 +258,7 @@ namespace GameCult.Caching.Tests
|
|||
before = File.ReadAllBytes(path);
|
||||
var gearCalls = 0;
|
||||
using (var cache = Open(path, false))
|
||||
Assert.That(() => cache.Update<UpdateGear>(new CultRecordKey("gear-big"), gear => { gearCalls++; return gear; }), Throws.InvalidOperationException);
|
||||
Assert.That(() => cache.Update<UpdateGear>(new CultRecordKey("gear-big"), gear => { gearCalls++; return gear; }), Throws.InvalidOperationException.With.Message.Contains("Update cannot copy it"));
|
||||
Assert.That(gearCalls, Is.EqualTo(0), "apply ran for a variant");
|
||||
Assert.That(calls, Is.EqualTo(0), "apply ran for a refused call");
|
||||
Assert.That(File.ReadAllBytes(path), Is.EqualTo(before));
|
||||
|
|
@ -509,6 +515,25 @@ namespace GameCult.Caching.Tests
|
|||
Assert.That(StoredValue(path, directory), Is.EqualTo(8));
|
||||
}
|
||||
|
||||
// The durable read is not a load: it leaves the store's view of what the cache holds alone, so a later pull still delivers the rival's write.
|
||||
[TestCase(false)]
|
||||
[TestCase(true)]
|
||||
public void ADeclinedUpdateDoesNotHideTheRivalsWriteFromALaterPull(bool directory)
|
||||
{
|
||||
var path = PathOf("hidden.cc");
|
||||
Seed(path, directory, 1);
|
||||
using var a = Open(path, directory);
|
||||
using var b = Open(path, directory);
|
||||
Assert.That(b.Get<UpdateCounter>(Key)!.Value, Is.EqualTo(1));
|
||||
Rival(a, 7);
|
||||
|
||||
Assert.That(b.Update<UpdateCounter>(Key, _ => null).Outcome, Is.EqualTo(CultUpdateOutcome.Declined));
|
||||
Assert.That(b.Get<UpdateCounter>(Key)!.Value, Is.EqualTo(1), "a declined Update changed what the cache holds");
|
||||
b.PullAllBackingStoresAsync().GetAwaiter().GetResult();
|
||||
|
||||
Assert.That(b.Get<UpdateCounter>(Key)!.Value, Is.EqualTo(7));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CodecLessStoreBackedCacheUpdates()
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue