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]