fix(localdb): oplog store review fixes — ack clamping, age-based pruning, monotonicity tests
Claude-Session: https://claude.ai/code/session_01BL2Vu1ESDQ9SCN4gVKkdts
This commit is contained in:
@@ -90,6 +90,38 @@ public sealed class OplogStoreTests : IDisposable
|
||||
Assert.Equal(third.Seq, peer.LastAckedSeq);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RecordPeerAck_OutOfOrderAck_DoesNotRegressWatermark()
|
||||
{
|
||||
using var db = await NewOrdersDb();
|
||||
await WriteRows(db, 5);
|
||||
var store = new OplogStore(db, new ReplicationOptions());
|
||||
var batch = await store.ReadBatchAboveAsync(0, 100, default);
|
||||
|
||||
await store.RecordPeerAckAsync(batch[4].Seq, default);
|
||||
await store.RecordPeerAckAsync(batch[1].Seq, default);
|
||||
|
||||
var peer = await store.GetPeerStateAsync(default);
|
||||
Assert.Equal(batch[4].Seq, peer.LastAckedSeq);
|
||||
// A late duplicate/out-of-order ack of an already-acked seq is benign, not a violation.
|
||||
Assert.False(peer.NeedsSnapshot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task RecordPeerAck_AckBeyondMaxSeq_ClampsAndFlagsSnapshot()
|
||||
{
|
||||
using var db = await NewOrdersDb();
|
||||
await WriteRows(db, 3);
|
||||
var store = new OplogStore(db, new ReplicationOptions());
|
||||
var maxSeq = await store.GetMaxSeqAsync(default);
|
||||
|
||||
await store.RecordPeerAckAsync(maxSeq + 100, default);
|
||||
|
||||
var peer = await store.GetPeerStateAsync(default);
|
||||
Assert.Equal(maxSeq, peer.LastAckedSeq);
|
||||
Assert.True(peer.NeedsSnapshot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Prune_DropsExpiredTombstoneRowVersions_KeepsLive()
|
||||
{
|
||||
@@ -137,12 +169,12 @@ public sealed class OplogStoreTests : IDisposable
|
||||
|
||||
Assert.True(flagged);
|
||||
var count = await db.QueryAsync("SELECT COUNT(*) FROM __localdb_oplog", r => r.GetInt64(0));
|
||||
Assert.True(count[0] <= 3, $"oplog should be pruned to <= 3, was {count[0]}");
|
||||
Assert.Equal(3, count[0]);
|
||||
Assert.True((await store.GetPeerStateAsync(default)).NeedsSnapshot);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CapExceeded_ByAge_SetsNeedsSnapshot()
|
||||
public async Task CapExceeded_ByAge_SetsNeedsSnapshot_AndPrunesExpired()
|
||||
{
|
||||
using var db = await NewOrdersDb();
|
||||
await db.ExecuteAsync("INSERT INTO orders (id, sku, qty) VALUES (1,'A',1),(2,'B',2)");
|
||||
@@ -154,6 +186,9 @@ public sealed class OplogStoreTests : IDisposable
|
||||
|
||||
Assert.True(flagged);
|
||||
Assert.True((await store.GetPeerStateAsync(default)).NeedsSnapshot);
|
||||
// Age-only trigger must actually prune: both entries are past the age cutoff.
|
||||
var count = await db.QueryAsync("SELECT COUNT(*) FROM __localdb_oplog", r => r.GetInt64(0));
|
||||
Assert.Equal(0, count[0]);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
Reference in New Issue
Block a user