You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
filterByTableNameTestCaseBelongsTo returns true for every event whose entityType is not testCase, so a conversation on a test case bypasses the filter and is delivered to an alert scoped
to a different table.
This is the last surviving instance of the "a matcher that cannot evaluate an event returns true" pattern in AlertsRuleEvaluator. Sibling of #30555 (the five scoping matchers, fixed by #30571) and of #29112 (the observability trigger matchers). Part of family 1 of #27889.
Root cause
AlertsRuleEvaluator.java:316-324:
publicbooleanfilterByTableNameTestCaseBelongsTo(List<String> tableFqns) {
if (changeEvent == null) {
returnfalse;
}
if (!changeEvent.getEntityType().equals(TEST_CASE)) {
returntrue; // <-- "deliver", not "does not match"
}
TestCasetestCase = (TestCase) getEntity(changeEvent);
StringparentFqn = resolveParentTableFqn(testCase);
returnparentFqn != null && tableFqns.contains(parentFqn);
}
Returning true from a filter that cannot be evaluated means "deliver", not "does not match", which
is the same defect #30555 describes for the scoping matchers.
Reachability on main / 2.0
Narrow but real, and worth stating precisely because the Task System Redesign changed it.
The filter is declared on exactly one resource, observability testCase, in EntityObservabilityFilterDescriptor.json, alongside filterByFqn, filterByDomain and filterByOwner. The observability resource list is [table, topic, container, pipeline, ingestionPipeline, testCase, testSuite, dataContract], with no all, so the resources == ["all"] early return in AlertUtil.shouldTriggerAlert cannot apply. Routing is then config.getResources().contains(event.getEntityType()), which means the only non-testCase event
that can reach this matcher is a THREAD event through the branch added by #28122.
Reaching it therefore needs all of:
An observability alert on testCase carrying filterByTableNameTestCaseBelongsTo.
A Conversation whose about is a test case. FeedRepository does not restrict the about entity
type (Entity.supportsFeed exists at Entity.java:909 but is referenced nowhere in the service),
so POST /v1/feed with about: <#E::testCase::{fqn}::description> is accepted. The UI does not
offer this today, so in practice this is an API-created conversation.
The redesign shrank this, it did not cause it. Before #25894, DQ incidents were thread-tasks whose about pointed at the test case, so entityRef.type == testCase and they travelled the THREAD path
straight into a testCase-resource alert. On main, TestCaseResolutionStatusRepository creates Task entities through TaskRepository, so incident events carry entityType = task, which never
matches resources = ["testCase"]. Only plain Conversations remain on this path.
For a THREAD event, resolve thread.entityRef, require type == testCase, load the test case and
reuse the existing resolveParentTableFqn(testCase). Return false for anything else, including a
thread about a non-test-case entity and an unresolvable parent, so the matcher never answers "deliver"
to a question it could not evaluate.
Resolution of the parent must tolerate a missing entity. An exception escaping a matcher aborts the
whole change-event batch, the failure mode fixed in #28304 and #29674.
Acceptance criteria
A zero-trigger observability alert on testCase with filterByTableNameTestCaseBelongsTo = [tableA]
does not receive a conversation on a test case belonging to tableB.
The same alert does receive a conversation on a test case belonging to tableA.
The matcher returns false, never true, for a thread about a non-test-case entity, a thread
with no entityRef, and a thread whose parent test case no longer resolves.
No matcher in AlertsRuleEvaluator returns true for an event it cannot evaluate.
Unit coverage for match, non-match and each inapplicable case.
Summary
filterByTableNameTestCaseBelongsToreturnstruefor every event whoseentityTypeis nottestCase, so a conversation on a test case bypasses the filter and is delivered to an alert scopedto a different table.
This is the last surviving instance of the "a matcher that cannot evaluate an event returns
true" pattern inAlertsRuleEvaluator. Sibling of #30555 (the five scoping matchers, fixed by#30571) and of #29112 (the observability trigger matchers). Part of family 1 of #27889.
Root cause
AlertsRuleEvaluator.java:316-324:Returning
truefrom a filter that cannot be evaluated means "deliver", not "does not match", whichis the same defect #30555 describes for the scoping matchers.
Reachability on
main/ 2.0Narrow but real, and worth stating precisely because the Task System Redesign changed it.
The filter is declared on exactly one resource, observability
testCase, inEntityObservabilityFilterDescriptor.json, alongsidefilterByFqn,filterByDomainandfilterByOwner. The observability resource list is[table, topic, container, pipeline, ingestionPipeline, testCase, testSuite, dataContract], with noall, so theresources == ["all"]early return inAlertUtil.shouldTriggerAlertcannot apply. Routing is thenconfig.getResources().contains(event.getEntityType()), which means the only non-testCaseeventthat can reach this matcher is a
THREADevent through the branch added by #28122.Reaching it therefore needs all of:
testCasecarryingfilterByTableNameTestCaseBelongsTo.config.getActions()is non-empty, and the trigger section is a form list with no minimum, so a zero-trigger
observability alert is savable and does receive thread events. fix(alerts): scope thread events by their parent entity instead of bypassing filters #30571 verified this same shape
end to end for
filterByFqn.aboutis a test case.FeedRepositorydoes not restrict theaboutentitytype (
Entity.supportsFeedexists atEntity.java:909but is referenced nowhere in the service),so
POST /v1/feedwithabout: <#E::testCase::{fqn}::description>is accepted. The UI does notoffer this today, so in practice this is an API-created conversation.
The redesign shrank this, it did not cause it. Before #25894, DQ incidents were thread-tasks whose
aboutpointed at the test case, soentityRef.type == testCaseand they travelled the THREAD pathstraight into a
testCase-resource alert. Onmain,TestCaseResolutionStatusRepositorycreatesTaskentities throughTaskRepository, so incident events carryentityType = task, which nevermatches
resources = ["testCase"]. Only plain Conversations remain on this path.Why this is not part of #30555
Filed separately rather than folded into #30571 because the shape differs:
entityType != testCase), not a thread bypass. It fires for anynon-
testCaseevent, whereas the five matchers in Entity-scoped alert filters are bypassed for thread events (alerts deliver conversations outside their scope) #30555 bypass specifically onTHREAD.thread.entityRef; this one needs two hops, thread -> test case -> parent table.observability-only and is not among them.
Suggested fix
For a
THREADevent, resolvethread.entityRef, requiretype == testCase, load the test case andreuse the existing
resolveParentTableFqn(testCase). Returnfalsefor anything else, including athread about a non-test-case entity and an unresolvable parent, so the matcher never answers "deliver"
to a question it could not evaluate.
Resolution of the parent must tolerate a missing entity. An exception escaping a matcher aborts the
whole change-event batch, the failure mode fixed in #28304 and #29674.
Acceptance criteria
testCasewithfilterByTableNameTestCaseBelongsTo = [tableA]does not receive a conversation on a test case belonging to
tableB.tableA.false, nevertrue, for a thread about a non-test-case entity, a threadwith no
entityRef, and a thread whose parent test case no longer resolves.AlertsRuleEvaluatorreturnstruefor an event it cannot evaluate.