alerting(core): drop unused perExchangeLingerSeconds from ExchangeMatchCondition

Dead field — was enforced by compact ctor as required for PER_EXCHANGE,
but never read anywhere in the codebase. Removal tightens the API surface
and is precondition for the Task 3.3 cross-field validator.

Pre-prod; no shim / migration.
This commit is contained in:
hsiegeln
2026-04-22 17:10:53 +02:00
parent ba4e2bb68f
commit e483e52eee
6 changed files with 17 additions and 21 deletions

View File

@@ -145,7 +145,7 @@ class AlertRuleControllerIT extends AbstractPostgresIT {
{"name":"sqli-test","severity":"WARNING","conditionKind":"EXCHANGE_MATCH", {"name":"sqli-test","severity":"WARNING","conditionKind":"EXCHANGE_MATCH",
"condition":{"kind":"EXCHANGE_MATCH","scope":{}, "condition":{"kind":"EXCHANGE_MATCH","scope":{},
"filter":{"status":"FAILED","attributes":{"foo'; DROP TABLE executions; --":"x"}}, "filter":{"status":"FAILED","attributes":{"foo'; DROP TABLE executions; --":"x"}},
"fireMode":"PER_EXCHANGE","perExchangeLingerSeconds":60}} "fireMode":"PER_EXCHANGE"}}
"""; """;
ResponseEntity<String> resp = restTemplate.exchange( ResponseEntity<String> resp = restTemplate.exchange(
@@ -164,7 +164,7 @@ class AlertRuleControllerIT extends AbstractPostgresIT {
{"name":"valid-attr","severity":"WARNING","conditionKind":"EXCHANGE_MATCH", {"name":"valid-attr","severity":"WARNING","conditionKind":"EXCHANGE_MATCH",
"condition":{"kind":"EXCHANGE_MATCH","scope":{}, "condition":{"kind":"EXCHANGE_MATCH","scope":{},
"filter":{"status":"FAILED","attributes":{"order.type":"x"}}, "filter":{"status":"FAILED","attributes":{"order.type":"x"}},
"fireMode":"PER_EXCHANGE","perExchangeLingerSeconds":60}} "fireMode":"PER_EXCHANGE"}}
"""; """;
ResponseEntity<String> resp = restTemplate.exchange( ResponseEntity<String> resp = restTemplate.exchange(

View File

@@ -158,7 +158,7 @@ class AlertEvaluatorJobIT extends AbstractPostgresIT {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope(APP_SLUG, null, null), new AlertScope(APP_SLUG, null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 60); FireMode.PER_EXCHANGE, null, null);
var webhook = new WebhookBinding(UUID.randomUUID(), null, null, Map.of()); var webhook = new WebhookBinding(UUID.randomUUID(), null, null, Map.of());
var rule = new AlertRule( var rule = new AlertRule(
ruleId2, envId, "per-exchange-rule-" + ruleId2, null, ruleId2, envId, "per-exchange-rule-" + ruleId2, null,

View File

@@ -61,7 +61,7 @@ class ExchangeMatchEvaluatorTest {
return new ExchangeMatchCondition( return new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 60); FireMode.PER_EXCHANGE, null, null);
} }
private ExecutionSummary summary(String id, Instant startTime, String status) { private ExecutionSummary summary(String id, Instant startTime, String status) {
@@ -77,7 +77,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.COUNT_IN_WINDOW, 5, 300, null); FireMode.COUNT_IN_WINDOW, 5, 300);
when(searchIndex.countExecutionsForAlerting(any())).thenReturn(7L); when(searchIndex.countExecutionsForAlerting(any())).thenReturn(7L);
@@ -92,7 +92,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.COUNT_IN_WINDOW, 5, 300, null); FireMode.COUNT_IN_WINDOW, 5, 300);
when(searchIndex.countExecutionsForAlerting(any())).thenReturn(3L); when(searchIndex.countExecutionsForAlerting(any())).thenReturn(3L);
@@ -105,7 +105,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", "direct:pay", null), new AlertScope("orders", "direct:pay", null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of("orderId", "123")), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of("orderId", "123")),
FireMode.COUNT_IN_WINDOW, 1, 120, null); FireMode.COUNT_IN_WINDOW, 1, 120);
when(searchIndex.countExecutionsForAlerting(any())).thenReturn(2L); when(searchIndex.countExecutionsForAlerting(any())).thenReturn(2L);
@@ -132,7 +132,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 60); FireMode.PER_EXCHANGE, null, null);
when(searchIndex.search(any())).thenReturn(SearchResult.empty(0, 50)); when(searchIndex.search(any())).thenReturn(SearchResult.empty(0, 50));
@@ -146,7 +146,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 60); FireMode.PER_EXCHANGE, null, null);
Instant t1 = NOW.minusSeconds(50); Instant t1 = NOW.minusSeconds(50);
Instant t2 = NOW.minusSeconds(30); Instant t2 = NOW.minusSeconds(30);
@@ -170,7 +170,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 60); FireMode.PER_EXCHANGE, null, null);
Instant t1 = NOW.minusSeconds(50); Instant t1 = NOW.minusSeconds(50);
Instant t2 = NOW.minusSeconds(10); // latest Instant t2 = NOW.minusSeconds(10); // latest
@@ -193,7 +193,7 @@ class ExchangeMatchEvaluatorTest {
var condition = new ExchangeMatchCondition( var condition = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 60); FireMode.PER_EXCHANGE, null, null);
Instant cursor = NOW.minusSeconds(120); Instant cursor = NOW.minusSeconds(120);
var rule = ruleWith(condition, Map.of("lastExchangeCursor", cursor.toString() + "|ex-prev")); var rule = ruleWith(condition, Map.of("lastExchangeCursor", cursor.toString() + "|ex-prev"));

View File

@@ -39,7 +39,7 @@ class NotificationContextBuilderTest {
case EXCHANGE_MATCH -> new ExchangeMatchCondition( case EXCHANGE_MATCH -> new ExchangeMatchCondition(
new AlertScope("my-app", "route-1", null), new AlertScope("my-app", "route-1", null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.PER_EXCHANGE, null, null, 30); FireMode.PER_EXCHANGE, null, null);
case AGENT_STATE -> new AgentStateCondition( case AGENT_STATE -> new AgentStateCondition(
new AlertScope(null, null, null), new AlertScope(null, null, null),
"DEAD", 0); "DEAD", 0);

View File

@@ -9,8 +9,7 @@ public record ExchangeMatchCondition(
ExchangeFilter filter, ExchangeFilter filter,
FireMode fireMode, FireMode fireMode,
Integer threshold, // required when COUNT_IN_WINDOW; null for PER_EXCHANGE Integer threshold, // required when COUNT_IN_WINDOW; null for PER_EXCHANGE
Integer windowSeconds, // required when COUNT_IN_WINDOW Integer windowSeconds // required when COUNT_IN_WINDOW
Integer perExchangeLingerSeconds // required when PER_EXCHANGE
) implements AlertCondition { ) implements AlertCondition {
public ExchangeMatchCondition { public ExchangeMatchCondition {
@@ -18,8 +17,6 @@ public record ExchangeMatchCondition(
throw new IllegalArgumentException("fireMode is required (PER_EXCHANGE or COUNT_IN_WINDOW)"); throw new IllegalArgumentException("fireMode is required (PER_EXCHANGE or COUNT_IN_WINDOW)");
if (fireMode == FireMode.COUNT_IN_WINDOW && (threshold == null || windowSeconds == null)) if (fireMode == FireMode.COUNT_IN_WINDOW && (threshold == null || windowSeconds == null))
throw new IllegalArgumentException("COUNT_IN_WINDOW requires threshold + windowSeconds"); throw new IllegalArgumentException("COUNT_IN_WINDOW requires threshold + windowSeconds");
if (fireMode == FireMode.PER_EXCHANGE && perExchangeLingerSeconds == null)
throw new IllegalArgumentException("PER_EXCHANGE requires perExchangeLingerSeconds");
} }
@Override @Override

View File

@@ -28,7 +28,7 @@ class AlertConditionJsonTest {
var c = new ExchangeMatchCondition( var c = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of("type","payment")), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of("type","payment")),
FireMode.PER_EXCHANGE, null, null, 300); FireMode.PER_EXCHANGE, null, null);
String json = om.writeValueAsString((AlertCondition) c); String json = om.writeValueAsString((AlertCondition) c);
AlertCondition parsed = om.readValue(json, AlertCondition.class); AlertCondition parsed = om.readValue(json, AlertCondition.class);
assertThat(parsed).isInstanceOf(ExchangeMatchCondition.class); assertThat(parsed).isInstanceOf(ExchangeMatchCondition.class);
@@ -39,7 +39,7 @@ class AlertConditionJsonTest {
var c = new ExchangeMatchCondition( var c = new ExchangeMatchCondition(
new AlertScope("orders", null, null), new AlertScope("orders", null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
FireMode.COUNT_IN_WINDOW, 5, 900, null); FireMode.COUNT_IN_WINDOW, 5, 900);
AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class); AlertCondition parsed = om.readValue(om.writeValueAsString((AlertCondition) c), AlertCondition.class);
assertThat(((ExchangeMatchCondition) parsed).threshold()).isEqualTo(5); assertThat(((ExchangeMatchCondition) parsed).threshold()).isEqualTo(5);
} }
@@ -49,7 +49,7 @@ class AlertConditionJsonTest {
assertThatThrownBy(() -> new ExchangeMatchCondition( assertThatThrownBy(() -> new ExchangeMatchCondition(
new AlertScope(null, null, null), new AlertScope(null, null, null),
new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()), new ExchangeMatchCondition.ExchangeFilter("FAILED", Map.of()),
null, null, null, null)) null, null, null))
.isInstanceOf(IllegalArgumentException.class) .isInstanceOf(IllegalArgumentException.class)
.hasMessageContaining("fireMode"); .hasMessageContaining("fireMode");
} }
@@ -63,8 +63,7 @@ class AlertConditionJsonTest {
"filter": {"status": "FAILED", "attributes": {}}, "filter": {"status": "FAILED", "attributes": {}},
"fireMode": null, "fireMode": null,
"threshold": null, "threshold": null,
"windowSeconds": null, "windowSeconds": null
"perExchangeLingerSeconds": null
} }
"""; """;
assertThatThrownBy(() -> om.readValue(json, AlertCondition.class)) assertThatThrownBy(() -> om.readValue(json, AlertCondition.class))