feat(batch29): implement jetstream batching group-b validation and apply state
This commit is contained in:
@@ -143,4 +143,165 @@ public sealed class JetStreamBatchingCoreTests
|
||||
store.Received(1).Stop();
|
||||
JetStreamBatching.GlobalInflightBatches.ShouldBe(0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Commit_DiffContainsState_UpdatesRuntimeState()
|
||||
{
|
||||
var diff = new BatchStagedDiff
|
||||
{
|
||||
MsgIds = new Dictionary<string, object?> { ["id-1"] = null },
|
||||
Counter = new Dictionary<string, MsgCounterRunningTotal>
|
||||
{
|
||||
["foo"] = new() { Ops = 2, Total = new System.Numerics.BigInteger(5) },
|
||||
},
|
||||
Inflight = new Dictionary<string, InflightSubjectRunningTotal>
|
||||
{
|
||||
["foo"] = new() { Bytes = 10, Ops = 1 },
|
||||
},
|
||||
ExpectedPerSubject = new Dictionary<string, BatchExpectedPerSubject>
|
||||
{
|
||||
["foo"] = new() { SSeq = 3, ClSeq = 9 },
|
||||
},
|
||||
};
|
||||
|
||||
var state = new BatchRuntimeState();
|
||||
|
||||
JetStreamBatching.Commit(diff, state);
|
||||
|
||||
state.MsgIds.ContainsKey("id-1").ShouldBeTrue();
|
||||
state.ClusteredCounterTotal["foo"].Total.ShouldBe(new System.Numerics.BigInteger(5));
|
||||
state.Inflight["foo"].Ops.ShouldBe((ulong)1);
|
||||
state.ExpectedPerSubjectSequence[9].ShouldBe("foo");
|
||||
state.ExpectedPerSubjectInProcess.Contains("foo").ShouldBeTrue();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BatchApplyWrappers_ClearAndRejectPaths_UpdateState()
|
||||
{
|
||||
var entry = new TestCommittedEntry();
|
||||
var apply = new BatchApply
|
||||
{
|
||||
Id = "batch-A",
|
||||
Count = 3,
|
||||
EntryStart = 4,
|
||||
MaxApplied = 8,
|
||||
Entries = [entry],
|
||||
};
|
||||
|
||||
var context = new BatchApplyContext { Clfs = 10 };
|
||||
JetStreamBatching.RejectBatchStateLocked(apply, context);
|
||||
|
||||
context.Clfs.ShouldBe((ulong)13);
|
||||
entry.Returned.ShouldBeTrue();
|
||||
apply.Id.ShouldBe(string.Empty);
|
||||
apply.Count.ShouldBe((ulong)0);
|
||||
|
||||
apply.Id = "batch-B";
|
||||
apply.Count = 2;
|
||||
JetStreamBatching.ClearBatchStateLocked(apply);
|
||||
apply.Id.ShouldBe(string.Empty);
|
||||
apply.Count.ShouldBe((ulong)0);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckMsgHeadersPreClusteredProposal_ExpectedStreamMismatch_ReturnsNotMatchError()
|
||||
{
|
||||
var store = new JetStreamMemStore(new StreamConfig { Name = "ORDERS", Storage = StorageType.MemoryStorage });
|
||||
try
|
||||
{
|
||||
var hdr = NatsMessageHeaders.GenHeader(null, NatsHeaderConstants.JsExpectedStream, "OTHER");
|
||||
var diff = new BatchStagedDiff();
|
||||
var context = new BatchHeaderCheckContext
|
||||
{
|
||||
Store = store,
|
||||
ClSeq = 1,
|
||||
Clfs = 0,
|
||||
MaxPayload = 1024,
|
||||
StreamSubjects = ["ORDERS.>"],
|
||||
};
|
||||
|
||||
var (_, _, _, apiError, error) = JetStreamBatching.CheckMsgHeadersPreClusteredProposal(
|
||||
diff,
|
||||
context,
|
||||
"ORDERS.created",
|
||||
hdr,
|
||||
[],
|
||||
sourced: false,
|
||||
name: "ORDERS",
|
||||
allowRollup: true,
|
||||
denyPurge: false,
|
||||
allowTtl: true,
|
||||
allowMsgCounter: false,
|
||||
allowMsgSchedules: false,
|
||||
discard: DiscardPolicy.DiscardOld,
|
||||
discardNewPer: false,
|
||||
maxMsgSize: -1,
|
||||
maxMsgs: -1,
|
||||
maxMsgsPer: -1,
|
||||
maxBytes: -1);
|
||||
|
||||
apiError.ShouldNotBeNull();
|
||||
apiError!.ErrCode.ShouldBe(JsApiErrors.StreamNotMatch.ErrCode);
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
finally
|
||||
{
|
||||
store.Stop();
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void CheckMsgHeadersPreClusteredProposal_DuplicateMsgIdInBatch_ReturnsDuplicateError()
|
||||
{
|
||||
var store = new JetStreamMemStore(new StreamConfig { Name = "ORDERS", Storage = StorageType.MemoryStorage });
|
||||
try
|
||||
{
|
||||
var hdr = NatsMessageHeaders.GenHeader(null, NatsHeaderConstants.JsMsgId, "msg-1");
|
||||
var diff = new BatchStagedDiff
|
||||
{
|
||||
MsgIds = new Dictionary<string, object?> { ["msg-1"] = null },
|
||||
};
|
||||
var context = new BatchHeaderCheckContext
|
||||
{
|
||||
Store = store,
|
||||
ClSeq = 1,
|
||||
MaxPayload = 1024,
|
||||
};
|
||||
|
||||
var (_, _, _, apiError, error) = JetStreamBatching.CheckMsgHeadersPreClusteredProposal(
|
||||
diff,
|
||||
context,
|
||||
"ORDERS.created",
|
||||
hdr,
|
||||
[],
|
||||
sourced: false,
|
||||
name: "ORDERS",
|
||||
allowRollup: true,
|
||||
denyPurge: false,
|
||||
allowTtl: true,
|
||||
allowMsgCounter: false,
|
||||
allowMsgSchedules: false,
|
||||
discard: DiscardPolicy.DiscardOld,
|
||||
discardNewPer: false,
|
||||
maxMsgSize: -1,
|
||||
maxMsgs: -1,
|
||||
maxMsgsPer: -1,
|
||||
maxBytes: -1);
|
||||
|
||||
apiError.ShouldNotBeNull();
|
||||
apiError!.ErrCode.ShouldBe(JsApiErrors.AtomicPublishContainsDuplicateMessage.ErrCode);
|
||||
error.ShouldNotBeNull();
|
||||
}
|
||||
finally
|
||||
{
|
||||
store.Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class TestCommittedEntry : ICommittedEntry
|
||||
{
|
||||
public bool Returned { get; private set; }
|
||||
|
||||
public void ReturnToPool() => Returned = true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user