unbridled-41 opened a new pull request, #4737:
URL: https://github.com/apache/rocketmq-dashboard/pull/4737
Fixes #4735.
### Problem
`rmq.message.redelivery` published the message explorer's **display
projection** instead of the stored payload: the body cut at 64 KiB, a binary
body replaced by the Base64 text of at most 48 KiB, and the user-property map
capped at 64 entries / 1024 code points per value. A redelivery therefore
stored a silently rewritten copy while reporting success.
Chain: `MessageRedeliveryToolHandler.execute`
(`ops/ai/tool/handler/message/MessageRedeliveryToolHandler.java:63,76`) →
`MetadataService.redeliverMessage(String,String,String,String,String)`
(`instance/topic/MetadataService.java:293-298`) → `findMessageForRedelivery`
(`:342-350`) → `MessageService.queryMessages` →
`RocketMQMessageProvider.toRecordVO`
(`provider/apache/RocketMQMessageProvider.java:811-832`, display caps applied
at `:813,:824` via `displayBody` `:836-861`) →
`MetadataService.redeliverMessage(..., MessageRecordVO, ...)` (`:300-315`)
publishes `original.getBody()`.
The tool's plan text promises "Publishes a new message containing the source
payload and properties" (`MessageRedeliveryToolHandler.java:43-45`) and
`findMessageForRedelivery` is documented as loading "the exact source message"
(`MetadataService.java:341`), so the display caps are leaking into a write
path. The sibling DLQ resend path already uses the raw bytes
(`provider/apache/RocketMQDLQProvider.java:583`).
### Root cause
`findMessageForRedelivery` reuses the message-explorer query, whose
`MessageRecordVO` is a *rendering* projection: `body`, `bodyEncoding`,
`bodyTruncated`, `properties` and `propertiesTruncated` exist so the UI can
show a bounded body, not so a writer can republish one. The redelivery path
read `getBody()`/`getProperties()` and ignored the truncation flags that say
the value is not the stored one.
### Fix
`MetadataService.redeliverMessage(..., MessageRecordVO, ...)` now refuses a
lossy source before building the send request: `isBodyTruncated()`,
`bodyEncoding=BASE64`, and `isPropertiesTruncated()` each raise
`BusinessException(409, …)` naming the reason. Publishing the exact bytes is
not expressible through `SendMessageDTO` (its body is a `String` that the admin
client turns into UTF-8 bytes), so the honest outcome is a refusal instead of a
corrupt copy. The display contract of `MessageRecordVO` is untouched — the caps
stay where they belong, in the explorer.
### Priority
**71** = impact 28 + reach 8 + reproducibility 20 + maintenance value 15.
- Impact 28/40: silent data corruption of a published message (truncated
body, Base64 text as payload, dropped properties) that reports success; the
loss is only discoverable downstream.
- Reach 8/20: one tool (`rmq.message.redelivery`), and only for sources
whose body exceeds 64 KiB, are not valid UTF-8, or carry more than 64 user
properties.
- Reproducibility 20/20: deterministic unit test on the service — no cluster
needed.
- Maintenance value 15/20: removes a display-versus-write conflation; the
refusal message names exactly which projection limit was hit.
`FIX_CONFIDENCE`: **88** — the tool's own plan text and the DLQ sibling fix
the intended behavior; the change is one guard plus three tests, and it cannot
regress the exact-source path (a VO with the flags unset behaves as before).
### Tests
Red (source at base commit `7ce9a682`, three new tests added to
`MetadataServiceTest`):
```
mvn -B -ntp -o test -Dtest=MetadataServiceTest
[ERROR] Tests run: 55, Failures: 3, Errors: 0, Skipped: 0
redeliverMessageShouldRefuseATruncatedSourceBodyTest
redeliverMessageShouldRefuseABase64SourceBodyTest
redeliverMessageShouldRefuseATruncatedSourcePropertySetTest
```
Each failed because the redelivery proceeded and the provider captured the
truncated/encoded payload; the tests now assert a 409 whose message names the
reason and that `sendMessage` was never called.
Green (this branch):
```
mvn -B -ntp -o test
-Dtest='MetadataServiceTest,MessageServiceTest,MessageRedeliveryToolHandlerTest'
[INFO] Tests run: 80, Failures: 0, Errors: 0, Skipped: 0
[INFO] BUILD SUCCESS
```
Full module suite on this branch (`fix/redelivery-exact-source` @ f1d1bd57):
```
mvn -B -ntp -o test
[ERROR] Tests run: 3054, Failures: 0, Errors: 17, Skipped: 0
```
The 17 errors are **pre-existing and environmental**: they are Spring
`ApplicationContext` failures caused by an unreachable MySQL in this sandbox
(`CannotGetJdbcConnectionException: Communications link failure`), and they are
byte-identical to the pristine `rocketmq-studio` baseline run measured in the
same session — same 9 test classes, same 17 methods (`StudioApplicationTest`,
`HealthProbeIntegrationTest`, `AuthServiceSessionOverviewIntegrationTest`,
`QueryHistoryServiceIntegrationTest`,
`NotificationOutboxMapperIntegrationTest`,
`RmqAlertStateMapperIntegrationTest`, …). Test count moved 3051 → 3054, exactly
the three new tests, with no new failures.
### Risk
Low. The guard only fires on a source VO that is explicitly flagged as
lossy, which is exactly the case that previously produced a corrupt copy; a
fully projected message is unchanged. The behavior change worth stating in the
release notes: redelivering a message whose body is larger than 64 KiB (or
whose body/properties were cut for display) now answers `409` instead of
publishing a partial copy — an operator who needs that payload must use the DLQ
resend path, which already works from the raw stored bytes.
### Base branch
Targets `rocketmq-studio`, the development trunk. Note the repository's
default branch is still `master`; GitHub interprets the closing keyword only
for PRs that target the default branch, so the `Fixes` link above does not
auto-close #4735 when this merges — the issue 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]