alerting(eval): PER_EXCHANGE composite cursor — monotone across same-ms exchanges
Tests: - cursorMonotonicity_sameMillisecondExchanges_fireExactlyOncePerTick - firstRun_boundedByRuleCreatedAt_notRetentionHistory
This commit is contained in:
@@ -166,7 +166,7 @@ class ExchangeMatchEvaluatorTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
void perExchange_lastFiringCarriesNextCursor() {
|
||||
void perExchange_batchCarriesNextCursorInEvalState() {
|
||||
var condition = new ExchangeMatchCondition(
|
||||
new AlertScope("orders", null, null),
|
||||
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
|
||||
@@ -182,32 +182,32 @@ class ExchangeMatchEvaluatorTest {
|
||||
EvalResult r = eval.evaluate(condition, ruleWith(condition), new EvalContext("default", NOW, new TickCache()));
|
||||
var batch = (EvalResult.Batch) r;
|
||||
|
||||
// last firing carries the _nextCursor key with the latest startTime
|
||||
EvalResult.Firing last = batch.firings().get(batch.firings().size() - 1);
|
||||
assertThat(last.context()).containsKey("_nextCursor");
|
||||
assertThat(last.context().get("_nextCursor")).isEqualTo(t2);
|
||||
// The batch carries the composite next-cursor in nextEvalState under "lastExchangeCursor"
|
||||
assertThat(batch.nextEvalState()).containsKey("lastExchangeCursor");
|
||||
assertThat(batch.nextEvalState().get("lastExchangeCursor"))
|
||||
.isEqualTo(t2.toString() + "|ex-2");
|
||||
}
|
||||
|
||||
@Test
|
||||
void perExchange_usesLastExchangeTsFromEvalState() {
|
||||
void perExchange_usesLastExchangeCursorFromEvalState() {
|
||||
var condition = new ExchangeMatchCondition(
|
||||
new AlertScope("orders", null, null),
|
||||
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
|
||||
FireMode.PER_EXCHANGE, null, null, 60);
|
||||
|
||||
Instant cursor = NOW.minusSeconds(120);
|
||||
var rule = ruleWith(condition, Map.of("lastExchangeTs", cursor.toString()));
|
||||
var rule = ruleWith(condition, Map.of("lastExchangeCursor", cursor.toString() + "|ex-prev"));
|
||||
|
||||
when(searchIndex.search(any())).thenReturn(SearchResult.empty(0, 50));
|
||||
|
||||
eval.evaluate(condition, rule, new EvalContext("default", NOW, new TickCache()));
|
||||
|
||||
// Verify the search request used the cursor as the lower-bound
|
||||
// Verify the search request used the cursor tuple: timeFrom + afterExecutionId
|
||||
ArgumentCaptor<com.cameleer.server.core.search.SearchRequest> captor =
|
||||
ArgumentCaptor.forClass(com.cameleer.server.core.search.SearchRequest.class);
|
||||
verify(searchIndex).search(captor.capture());
|
||||
// timeFrom should be the cursor value
|
||||
assertThat(captor.getValue().timeFrom()).isEqualTo(cursor);
|
||||
assertThat(captor.getValue().afterExecutionId()).isEqualTo("ex-prev");
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -241,6 +241,7 @@ class ExchangeMatchEvaluatorTest {
|
||||
EvalResult r3 = eval.evaluate(condition, advanced,
|
||||
new EvalContext("default", t.plusSeconds(3), new TickCache()));
|
||||
assertThat(((EvalResult.Batch) r3).firings()).hasSize(1);
|
||||
assertThat(((EvalResult.Batch) r3).nextEvalState()).containsKey("lastExchangeCursor");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user