unbridled-41 opened a new pull request, #5071:
URL: https://github.com/apache/rocketmq-dashboard/pull/5071

   ## Problem
   
   A native ratio alert rule that has no stored `thresholdUnit` shows a 
nonsense threshold in the console, and the edit form writes that nonsense back 
into the rule.
   
   `formatThresholdCondition` converts the stored ratio to a percentage with a 
raw multiply (`web/src/pages/ops/alerts.tsx:133`):
   
   ```ts
   if (nativeRatioMetrics.has(rule.metric) && !rule.thresholdUnit) {
     return `${rule.operator} ${rule.threshold * 100}%`;
   }
   ```
   
   In binary floating point `0.55 * 100` is `55.00000000000001` and `0.29 * 
100` is `28.999999999999996`, so the 阈值 column of `/ops/alerts` renders `> 
55.00000000000001%` for a rule whose threshold is 0.55. The same multiply is 
used to seed the edit form (`:485`), so opening the rule and saving it stores 
`55.00000000000001` with `thresholdUnit: '%'` — a value nobody typed, in the 
field the evaluator compares against.
   
   The existing test for this branch (AlertsPage.test.tsx:272-281) uses 0.85, 
the one value in the fixture set where `0.85 * 100` happens to land on exactly 
85, so the defect is invisible in the suite.
   
   Which rules are in that state:
   - the legacy ratio representation the branch exists for (the conversion is 
also applied when the edit form loads such a rule, `:485`);
   - a rule created through the API without `thresholdUnit` — nothing couples 
the metric to a unit: `AlertRuleRequestDTO.java:38` has no constraint on it and 
`NativeAlertMetricCatalogService.validate` (`:70-85`) only checks that the 
metric is supported by the instance;
   - a rule imported through the YAML transfer, which copies the source rule's 
`thresholdUnit` verbatim (`AlertRuleTransferService.java:71`).
   
   Rules created from the console form are not affected: `handleSubmit` always 
forces `thresholdUnit: '%'` for a ratio metric.
   
   ## Evidence
   
   Pre-fix, on base `a562601d`, with the three new tests in place and only 
`alerts.tsx` at its base revision:
   
   ```
   $ cd web && npx vitest run src/pages/ops/__tests__/AlertsPage.test.tsx
    × renders a legacy native ratio threshold without the binary-float tail
    × shows a unitless ratio rule threshold as a round percentage in the table
    × saves a legacy ratio rule with the rounded percent the form showed
    Test Files  1 failed (1)
         Tests  3 failed | 28 passed (31)
   
   AssertionError: expected '> 55.00000000000001%' to be '> 55%'
   TestingLibraryElementError: Unable to find an element with the text: > 55%.
   AssertionError: expected { id: 99, …(18) } to match object { threshold: 55, 
thresholdUnit: '%' }
   ```
   
   The three failures are the three faces of the defect: the formatter, the 
rendered table cell, and the payload the edit form saves. The third is what 
makes this more than a display bug — the rounded number never reaches the 
database today.
   
   ## Root cause
   
   `rule.threshold * 100` (display, `:133`) and 
`Number(form.getFieldValue('threshold')) * 100` (edit prefill, `:485`) convert 
a ratio to a percent without rounding, so the binary representation error of 
the multiply is rendered and persisted.
   
   ## Fix
   
   One module-level helper, used by both sites:
   
   ```ts
   const ratioToPercent = (ratio: number): number => Number((ratio * 
100).toFixed(6));
   ```
   
   Six decimals is three orders of magnitude below where a double's noise 
starts (the tail appears at the 16th significant digit), so every threshold an 
operator can type survives the conversion exactly while the tail disappears. 
The display, the form seed and the saved value now agree.
   
   ## Score
   
   `PRIORITY = 60` (impact 18: the alerting console prints and then stores a 
threshold that is not the one the operator set, on a value the evaluator 
compares against; scope 10: the three native ratio metrics, and only rules 
without a stored unit; reproducibility 20: three deterministic assertions, one 
of them on the saved payload; maintenance value 12: the branch is documented as 
supported, and this is the number the feature is about).
   `FIX_CONFIDENCE = 88`.
   
   ## Tests
   
   ```
   $ cd web && npx vitest run src/pages/ops/__tests__/AlertsPage.test.tsx     # 
post-fix
    Test Files  1 passed (1)
         Tests  31 passed (31)
   
   $ cd web && npx tsc --noEmit -p tsconfig.json                            # 
exit 0
   $ cd web && npx eslint src/pages/ops/alerts.tsx \
         src/pages/ops/__tests__/AlertsPage.test.tsx                        # 0 
errors (5 pre-existing warnings)
   ```
   
   The pre-fix run of the same file (only `alerts.tsx` reverted to `a562601d`, 
tests kept) is the failure quoted under Evidence. Both conversion sites are 
asserted, not one representative: the table cell (page render) and the save 
payload (edit → OK), plus a direct unit assertion on both a downward (`0.29`) 
and an upward (`0.55`) rounding case.
   
   ## Risk
   
   Very low. The change only affects rules whose stored `thresholdUnit` is 
empty — a console-created rule always carries `'%'` and never reaches either 
conversion. For those rules the displayed and saved percent changes from the 
float tail to the round number, which is the intent of the conversion. No API 
shape, no rounding of user-entered values (`precision={2}` on the input is 
unchanged), and no evaluation semantics change: the stored threshold for a 
percent rule is the percent, and the server divides by 100 for ratio metrics 
with a `%` unit (`AlertRuleSemanticFingerprint.normalizedThreshold`), which is 
unaffected by the low-order bits.
   
   ## Dedup
   
   - `gh search prs/issues --repo apache/rocketmq-dashboard "ratio threshold 
percent"`, `"threshold"`, `"thresholdUnit"` → no PR or issue about the 
conversion (the hits are the `nativeRatioMetrics` unit work and rule-asset 
transfer).
   - `/tmp/open_pr_files.txt` lists `web/src/pages/ops/alerts.tsx` for PRs 
#4832/#4826/#4641/#5067 (counts, UTC stat, leftovers, other surfaces) — none 
touches `formatThresholdCondition` or the edit prefill.
   - `git log --oneline a562601d -- web/src/pages/ops/alerts.tsx` → the recent 
commits are localization and test-result fixes; the multiply is unchanged since 
the native-alerting feature.
   


-- 
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