leizhiyuan opened a new pull request, #1429:
URL: https://github.com/apache/rocketmq-clients/pull/1429
### Which Issue(s) This PR Fixes
Fixes #1428
### Brief Description
**Scenario.** During a migration to 5.x, producers often still use the
Remoting SDK while new consumers use the gRPC clients. The Remoting producer
compresses bodies over 4 KiB with ZLIB by default, and the proxy delivers them
with `body_encoding = GZIP`. The Rust client ignores `body_encoding` and
`body_digest`, so `MessageView::body()` returns the compressed bytes (a Java
consumer of the same group gets the original text) and a body that fails its
digest is delivered as valid.
**Change.** Align `MessageView::from_pb_message` and the push consumer with
Java (`MessageViewImpl#fromProtobuf`, `Utilities#decompressBytes`,
`StandardConsumeService`, `FifoConsumeService#getNextValidMessage`):
- **Digest.** Verify `body_digest` (CRC32 / MD5 / SHA1) on the body as
received, before decoding. The proxy computes it on the stored, still
compressed body. The checksum strings are built the same way as Java: uppercase
hex, CRC32 without zero padding (`Long.toHexString`).
- **Decoding.** When `body_encoding == GZIP`, decompress by magic number:
`1f 8b` → GZIP, `78` → ZLIB. The proxy labels every compressed body `GZIP`,
whatever the algorithm, so ZLIB has to be handled too.
- **Corrupted flag.** Add `MessageView::is_corrupted()`, true when the
digest does not match or the body can not be decompressed. A corrupted message
keeps the body as received. Same as Java `MessageView#isCorrupted`.
- **Push consumer.** A corrupted message is not passed to the listener. The
standard worker nacks it with the retry policy's next delay. The FIFO worker
forwards it to the dead letter queue. This covers both the sequential path and
the FIFO consume accelerator, since both go through
`consume_message_sequentially_static`.
- **Simple consumer.** Returns corrupted messages as before; the caller can
check `is_corrupted()`, as in Java.
#### Notes for reviewers
- **New dependencies.** `flate2 = "1"` (default `miniz_oxide` backend, pure
Rust), `crc32fast = "1"` (already a dependency of `flate2`), `md-5 = "0.10"`.
SHA-1 uses the existing `ring` dependency (`SHA1_FOR_LEGACY_USE_ONLY`), and hex
encoding the existing `hex` dependency. All three are MIT/Apache-2.0. I checked
that they build with Rust 1.74 (the MSRV used by CI) in a standalone project;
`master` itself no longer builds on 1.74 because of `tokio` 1.54, which is the
existing red MSRV check.
- **Not supported: LZ4 / ZSTD.** Java also recognizes LZ4 and ZSTD magic
numbers. The Remoting producer uses ZLIB unless `rocketmq.message.compressType`
is changed, so this PR handles GZIP and ZLIB and marks other formats as
corrupted (logged as "unsupported compression format"). Support can be added
later without changing the API.
- **Behavior change.** Consumers now see the decompressed body of
`GZIP`-encoded messages. A push consumer no longer passes corrupted messages to
the listener.
- **Conflicts.** `git merge-tree` is clean against #1381, #1404, #1407,
#1409, #1416, #1420, #1421, #1423, #1425 and #1427. It has textual conflicts in
`push_consumer.rs` with #1411 and #1414:
- #1411 changes the listener call in the standard worker to
`consume_message(..)`. Resolution: keep the corrupted check before it.
- #1414 rewrites the FIFO retry loop. Resolution: keep the corrupted check
before the loop and read `message.delivery_attempt()` there.
- Both also append tests at the end of the test module; keep both.
- I verified both on temporary merges: `cargo test` 99 passed (with #1411)
and 100 passed (with #1414). Overview in #1412.
### How Did You Test This Change?
**Unit tests.** All of them fail on `master` except `identity_body_is_kept`:
- `model::message::tests::body_decoding`
- `identity_body_is_kept`
- `gzip_body_is_decompressed`
- `zlib_body_marked_as_gzip_is_decompressed`
- `undecodable_body_is_corrupted`: a truncated gzip stream and an unknown
format; the body is kept as received.
- `digest_is_verified_on_the_received_body`: an MD5 over the compressed
body passes, an MD5 over the plain body is corrupted.
- `every_digest_type_is_verified`: CRC32, MD5, SHA1, matching and not
matching.
- `push_consumer::tests`
- `test_standard_consume_discards_corrupted_message`: the listener is not
called, `change_invisible_duration` once, no ack.
- `test_fifo_consume_forwards_corrupted_message_to_dlq`: the listener is
not called, `forward_to_deadletter_queue` once, no ack.
```
$ cargo test -- --test-threads=1
test result: ok. 97 passed; 0 failed; 1 ignored (lib; master: 89)
test result: ok. 3 passed; 0 failed (lite_integration_test)
test result: ok. 2 passed; 0 failed; 3 ignored (doc-tests)
$ cargo fmt --check # clean
$ RUSTDOCFLAGS='-D warnings' cargo doc --no-deps # no warnings
$ cargo build --examples --all-features # ok
```
`cargo clippy --all-targets` reports the same findings as `master`.
`--test-threads=1` avoids the existing flaky
`client::tests::client_get_session` (it uses `MockSession::new_context()`
without holding `MTX`), unrelated to this change.
**End-to-end.** Against a managed RocketMQ 5 server, with a normal topic and
group, received with `SimpleConsumer`:
| message | before | after | Java 5.2.2 |
|---|---|---|---|
| 5090-byte text from the Remoting producer (`rocketmq-client` 5.3.1,
compressed because > 4 KiB) | 472 bytes, `78 5e ..` (ZLIB) | the original
5090-byte text, not corrupted | the original text |
| 15-byte text from a gRPC producer with `body_encoding = GZIP` | the
35-byte gzip stream | the original 15-byte text, not corrupted | the original
text |
Both messages carry the proxy's MD5 digest, which now passes. The other
SimpleConsumer end-to-end cases (send/receive/ack, change invisible duration,
tag and SQL filters, idle long polling, delay message) behave as before.
---
Tracked in #1412 (overview of the related Rust issues / PRs and a suggested
merge order).
--
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]