[Bug] A cleared group forbidden state is persisted as the literal "null" and then fails the config load on the next broker start
Maintainer thường phản hồi trong vòng 1 ngày
Đánh giá
- Độ khó
- 3/5
- Thời gian dự kiến
- 1-2 ngày
- Mức phù hợp với người mới
- 25/100
Hướng nghiên cứu
Start in broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManager.java, where updateForbidden, setForbidden and clearForbidden write JSON.toJSONString of the group's table entry, and in decodeForbidden, which dereferences the parsed body. Run RocksDBSubscriptionGroupManagerTest to see the "null" write and the decode failure. Done means the cleared key is removed and a null body is skipped on load. Linked PR #11430 is already open, so check it before starting.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Mô tả
Before Creating the Bug Report
- I found a bug, not just asking a question, which should be created in GitHub Discussions.
- I have searched the GitHub Issues and GitHub Discussions of this repository and believe that this is not a duplicate.
- I have confirmed that this bug belongs to the current repository, not other repositories of RocketMQ.
Runtime platform environment
OS: any (broker with the RocksDB config manager: enableRocksDBStore=true, i.e. RocksDBSubscriptionGroupManager)
RocketMQ version
branch: develop
Git commit id: a7edeecfb
JDK Version
Compiler: Temurin 1.8.0_504 (target 1.8, same as CI)
Describe the Bug
The RocksDB variant of the subscription group manager persists the group's forbidden bits with JSON.toJSONString(this.getForbiddenTable().get(group)), and after the last bit of a group is cleared the table no longer holds an entry for that group — so the value written to the forbidden column family is the string "null":
@Override
public void updateForbidden(String group, String topic, int forbiddenIndex, boolean setOrClear) {
super.updateForbidden(group, topic, forbiddenIndex, setOrClear);
this.rocksDBConfigManager.put(FORBIDDEN_COLUMN_FAMILY_NAME, group,
JSON.toJSONString(this.getForbiddenTable().get(group))); // -> "null" once the group has no forbidden bits left
}
The same shape appears in setForbidden and clearForbidden (broker/src/main/java/org/apache/rocketmq/broker/config/v1/RocksDBSubscriptionGroupManager.java:287-306; the updateForbidden variant is at :276-284).
On the next start the stored value is fed back into the loader, which expects a JSON object of topic → bits:
protected void decodeForbidden(byte[] key, byte[] body) {
JSONObject jsonObject = JSON.parseObject(new String(body, RocksDBConfigManager.CHARSET));
Set<Map.Entry<String, Object>> entries = jsonObject.entrySet(); // NPE for a "null" body
...
}
(:263-273.) JSON.parseObject("null") returns null, so the NullPointerException leaves loadForbidden through its catch and the method returns false (:101-109), loadSubscriptionGroupAndForbidden() returns false (:95-99), then RocksDBSubscriptionGroupManager.load() returns false (:81-83), BrokerController.initializeMetadata() returns false (BrokerController.java:863) and BrokerStartup answers that with System.exit(-3) (BrokerStartup.java:287-291) — the broker does not start until that key is repaired by hand.
The trigger needs nothing more than un-forbidding the last forbidden topic of a group; with a single forbidden topic that is the very first clear, so no multi-topic scenario is required. (The related cross-topic loss when a group has several forbidden topics is reported separately in #11093 — this report is about the value that gets persisted and what it breaks on the next load; the two fixes compose.)
Steps to Reproduce
- Start a broker with
enableRocksDBStore=trueand forbid a topic for a group, e.g. viaUPDATE_AND_GET_GROUP_FORBIDDEN(AdminBrokerProcessor.updateAndGetGroupForbidden,:466-489) withreadable=falseforgroup=g,topic=t. - Un-forbid the same topic (
readable=true). Theforbiddencolumn family ofgroupnow holds the literalnull. - Restart the broker: the config load fails and the process exits (
initialize()false →System.exit(-3)), withloadForbidden exception … java.lang.NullPointerExceptionatdecodeForbidden.
At the unit level, RocksDBSubscriptionGroupManagerTest already injects a mocked RocksDBConfigManager (no real RocksDB needed): forbidding then clearing one topic shows the "null" value being written today, and calling decodeForbidden(key, "null".getBytes()) throws.
What Did You Expect to See?
Clearing a group's forbidden state persists "no forbidden state": the key is removed (or at least stores a valid empty JSON object), and the loader tolerates an already written null body so that brokers which ran with the broken value can still start after upgrading.
What Did You See Instead?
A "null" value is written, and the next broker start aborts because the loader dereferences the parsed null; the failure mode is a broker that cannot start from its own persisted configuration.
Additional Context
Minimal fix in RocksDBSubscriptionGroupManager: route the three overrides through one helper that deletes the column-family key when the group has no forbidden bits left instead of writing JSON.toJSONString(null), and ignore an empty (null) body in decodeForbidden so existing stores recover. The sibling JSON mode persists the whole table, which is why it never hits this.
I will open a PR with those changes plus regression tests in RocksDBSubscriptionGroupManagerTest (red before: a "null" value is written / decodeForbidden throws; green after: the key is deleted and a null body is skipped).
- Ngôn ngữ chính
- Java
- Star
- 22.7k
- Fork
- 12k
- Merge trung bình
- 3 ngày 18 giờ
- Pull request đã merge (30 ngày)
- 17
Chuẩn bị môi trường
- Không có Dockerfile hay tệp Docker Compose
- Có mẫu pull request
- Đọc hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Issue khác của apache/rocketmq
-
[Bug] RemotingHelper.connect swaps the socket send and receive buffer configurationCó thể đã có người làm @amalsaidov đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
CompressionType.of fails under the Turkish localeCó thể đã có người làm @sharanggupta đã nhận hôm nay. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 83/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[rocketmq-client-python] TransactionMQProducer allocates an unused producer during initializationĐang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
Maintainer thường phản hồi trong vòng 1 ngày
-
[Bug] notifyMasterOnline aborts the long-polling wake-up with a NullPointerException when an arrival drains a bucket firstCó thể đã có người làm @iloveeyjafjalla đã nhận 29 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 82/100
apache/rocketmq#11441 · 1 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
-
[Enhancement] FilterFactory duplicate-registration and unregister semantics lack testsCó thể đã có người làm @chennaji9 đã nhận 34 ngày trước. Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
apache/rocketmq#11340 · 2 bình luận ·
Maintainer thường phản hồi trong vòng 1 ngày
Tất cả issue của apache/rocketmq
Issue tương tự
-
bug emitter:client:csharp
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 84/100
microsoft/typespec#12572 · 1 bình luận · 1 reaction ·
Maintainer thường phản hồi trong vòng 1 ngày
-
GCC 15.2: -Wcast-qual warning - cast discards const qualifier from pointer target type (H5trace.c)Đang mở
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 73/100
Maintainer thường phản hồi trong vòng 1 ngày
-
needs-area-label
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 78/100
Maintainer thường phản hồi trong vòng 2 ngày
-
area/ai area/backend component/zeebe kind/task
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 68/100
Maintainer thường phản hồi trong vòng 1 ngày
-
Độ khó 2/5 1-3 giờ Mức phù hợp với người mới 72/100
spAnser/reacharound#38 ·