unbridled-41 opened a new issue, #4747:
URL: https://github.com/apache/rocketmq-dashboard/issues/4747

   ### Studio Version
   
   branch: rocketmq-studio
   git commit id: cd448e17f14b4bd6e6d4ade87300caf19ca91bc2
   
   ### Problem
   
   One enabled alert rule stored without a metric aborts the whole 
reconciliation pass for its instance and domain, so every active alert of that 
scope stays active forever: no `RESOLVED` state, no `RESOLVED` event, no 
recovery notification.
   
   Chain:
   
   - 
`server/src/main/java/org/apache/rocketmq/studio/ops/alert/NativeAlertProcessor.java:113-116`
 filtered the domain's rules with 
`scope.metricKeys().contains(StringUtils.trimWhitespace(rule.getMetric()))`.
   - `MetricCollectionScope` stores its keys as `Set.copyOf(...)` 
(`server/src/main/java/org/apache/rocketmq/studio/cluster/metrics/MetricCollectionScope.java:31`).
 Every `Set.of`/`Set.copyOf` implementation rejects a null lookup: 
`ImmutableCollections.Set12.contains` dereferences the argument 
(`o.equals(e0)`) and `SetN.contains` calls `Objects.requireNonNull`. Verified 
on this JDK:
   
   ```
   size=1 class=Set12 contains(null) THREW java.lang.NullPointerException: 
Cannot invoke "Object.equals(Object)" because "o" is null
   size=2 class=Set12 contains(null) THREW java.lang.NullPointerException: ...
   size=3 class=SetN  contains(null) THREW java.lang.NullPointerException
   ```
   
     `StringUtils.trimWhitespace(null)` returns null, so a rule with a null 
metric turns the filter predicate into that throwing lookup. The size 
dependency some readers expect (only 3+ elements) does not exist.
   - A metric-less rule is a storable, first-class state: 
`AlertRuleRequestDTO.metric` (`ops/alert/AlertRuleRequestDTO.java:34`) carries 
no `@NotBlank`/`@Size`, `metric` is nullable in `db/schema.sql:274`, and the 
JSON transfer import explicitly tolerates it - 
`AlertRuleTransferService.java:33-46` (`if (candidate.getMetric() != null)`) 
plus `NativeAlertMetricCatalogService.java:70-73` (`if (rule == null || 
rule.getMetric() == null) return;`), reachable through 
`AlertRuleController.java:85-86` and `ClusterAlertRuleController.java:69-70`. 
Legacy rows migrated without a metric are the other source.
   - `NativeAlertProcessor.processSuccessfulCollection` (`:63-71`) calls 
`reconcileMissingActiveStates` (`:101-116`) at the end of every successful 
collection, and the scheduler swallows the failure: 
`CollectorScheduler.java:282-284` (business) and `:298-300` (cluster) catch 
`RuntimeException` and only log a warning.
   
   Trigger (deterministic): create or import one enabled rule with no metric 
into a domain, then let the collector finish a pass for that instance. The 
reconcile pass throws before it can iterate the active states, so an alert 
whose samples stopped arriving keeps its `FIRING`/`ACKED` row and never emits 
`RESOLVED`.
   
   ### Evidence
   
   - Defect: `ops/alert/NativeAlertProcessor.java:105` before the fix 
(`scope.metricKeys().contains(StringUtils.trimWhitespace(rule.getMetric()))`).
   - Reachability: `AlertRuleTransferService.java:33-46`, 
`NativeAlertMetricCatalogService.java:70-73`, `AlertRuleRequestDTO.java:34`, 
`db/schema.sql:274`.
   - Swallowed failure: `CollectorScheduler.java:282-284` and `:298-300`.
   - Regression test added with this report 
(`NativeAlertProcessorTest.reconcilesRemainingRulesWhenAnotherRuleHasNoMetricTest`),
 failing on cd448e17:
   
   ```
   [ERROR] 
NativeAlertProcessorTest.reconcilesRemainingRulesWhenAnotherRuleHasNoMetricTest 
-- Time elapsed: 0.016 s <<< FAILURE!
     "java.lang.NullPointerException: Cannot invoke "Object.equals(Object)" 
because "o" is null
   [ERROR] Tests run: 24, Failures: 1, Errors: 0, Skipped: 0
   ```
   
   ### Impact
   
   Alert recovery stops working for the affected instance and domain: operators 
keep seeing alerts that already recovered, no `RESOLVED` notification is 
delivered, and the visible symptom is indistinguishable from a genuine ongoing 
incident. The failure is silent - the only trace is a `Native metric collector 
failed` warning that names the collector, not the rule.
   
   ### Expected behavior
   
   A rule that carries no metric is not part of any collection scope; it is 
filtered out and the remaining rules of the scope are still reconciled.
   
   ### Related work
   
   - #4571 (`fix(ai): tolerate alert rules without a metric in the rule list 
tool`) and #4711 (`fix(alert): reject an alert-rule test run without a metric`) 
— the same input class breaking two other surfaces; both are open and neither 
touches `NativeAlertProcessor`, so this report deliberately fixes only the 
reconcile membership test and leaves rule-write validation to those.
   - #4647/#4648 (`CollectorScheduler` snapshot-retention parsing) — a 
different unguarded parse in the same scheduler.
   
   #
   
   ## PR
   
   Fix: #4748.
   
   This PR targets the development trunk `rocketmq-studio`. GitHub interprets 
closing keywords only for pull requests that target the repository default 
branch, and this repository default branch is still `master`, so merging the PR 
will not close this issue automatically — it needs to be closed by hand.
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to