Skip to content

Fix consumerOffset deserialization compatibility#10283

Open
zhishengzhang wants to merge 14 commits intoapache:developfrom
zhishengzhang:fix/consumerOffset-unquoted-key-compatibility
Open

Fix consumerOffset deserialization compatibility#10283
zhishengzhang wants to merge 14 commits intoapache:developfrom
zhishengzhang:fix/consumerOffset-unquoted-key-compatibility

Conversation

@zhishengzhang
Copy link
Copy Markdown

@zhishengzhang zhishengzhang commented Apr 30, 2026

between 5.4.x and 5.5.x

When upgrading from 5.4.x to 5.5.x

Which Issue(s) This PR Fixes

Brief Description

How Did You Test This Change?

between 5.4.x and 5.5.x

When upgrading from 5.4.x to 5.5.x
@ChineseTony
Copy link
Copy Markdown
Contributor

ChineseTony commented May 6, 2026

hello,please keep fastjson2 parser/writer features with 1.x to maintain compatibility
QQ_1778050136658
this is fastjson1 default features

hello,I had test this case in rocketmq release-5.4.0 release-5.5.0 It doesn't work

    @Test
    public void testDecodeUnquotedFieldNames() {
        String jsonWithUnquotedKeys = " {\"dataVersion\":{},\"offsetTable\":{\"GID-test_rmq_5point4\":{0:0}},\"pullOffsetTable\":{\"rocketmq-tx\":{0:0}}}";
        ConsumerOffsetSerializeWrapper result = ConsumerOffsetSerializeWrapper.decode(jsonWithUnquotedKeys.getBytes(StandardCharsets.UTF_8), ConsumerOffsetSerializeWrapper.class);
        assertNotNull(result);
    }

QQ_1778053661909 QQ_1778053736150

just in rocketmq 5.3.x It's passed,beacase in this version use fastjson1.x beacase fastjson has default parser feature

QQ_1778053964680

@yx9o
Copy link
Copy Markdown
Contributor

yx9o commented May 6, 2026

Thanks for working on this.

I think the root cause is the response-shape mismatch, not numeric map key deserialization itself. Declared typed fields like offsetTable can deserialize the old-style {0:0} queueId key correctly.

GET_ALL_CONSUMER_OFFSET may return pullOffsetTable from ConsumerOffsetManager.encode(), but ConsumerOffsetSerializeWrapper does not declare it. fastjson2 then treats pullOffsetTable as an unknown field and fails while skipping its nested numeric queueId key.

Could we add pullOffsetTable to the wrapper, cover the mixed offsetTable + pullOffsetTable response with a regression test, and check other encode/decode wrappers for similar mismatches?

@ChineseTony
Copy link
Copy Markdown
Contributor

Thank you for the clear explanation!
You’re right. The root cause is the response shape mismatch. The pullOffsetTable returned by GET_ALL_CONSUMER_OFFSET is not declared in the wrapper class, leading to deserialization failure in fastjson2.

@ChineseTony
Copy link
Copy Markdown
Contributor

ChineseTony commented May 7, 2026

hello ,I fixed #10287

@zhishengzhang
Copy link
Copy Markdown
Author

Thanks for working on this.

I think the root cause is the response-shape mismatch, not numeric map key deserialization itself. Declared typed fields like offsetTable can deserialize the old-style {0:0} queueId key correctly.

GET_ALL_CONSUMER_OFFSET may return pullOffsetTable from ConsumerOffsetManager.encode(), but ConsumerOffsetSerializeWrapper does not declare it. fastjson2 then treats pullOffsetTable as an unknown field and fails while skipping its nested numeric queueId key.

Could we add pullOffsetTable to the wrapper, cover the mixed offsetTable + pullOffsetTable response with a regression test, and check other encode/decode wrappers for similar mismatches?

when upgrading RocketMQ from 4.9.x → 5.4.x → 5.5.x, the Slave node throws
com.alibaba.fastjson2.JSONException: not support unquoted name or
illegal number during consumerOffset synchronization.

Regardless of whether pullOffsetTable and groupTopicMap are added to ConsumerOffsetSerializeWrapper,
the try-catch fallback in RemotingSerializable is always necessary

… are added to ConsumerOffsetSerializeWrapper,

the try-catch fallback in RemotingSerializable is always necessary
@zhishengzhang
Copy link
Copy Markdown
Author

zhishengzhang commented May 7, 2026

Thanks for working on this.

I think the root cause is the response-shape mismatch, not numeric map key deserialization itself. Declared typed fields like offsetTable can deserialize the old-style {0:0} queueId key correctly.

GET_ALL_CONSUMER_OFFSET may return pullOffsetTable from ConsumerOffsetManager.encode(), but ConsumerOffsetSerializeWrapper does not declare it. fastjson2 then treats pullOffsetTable as an unknown field and fails while skipping its nested numeric queueId key.

Could we add pullOffsetTable to the wrapper, cover the mixed offsetTable + pullOffsetTable response with a regression test, and check other encode/decode wrappers for similar mismatches?

Completed checking all SerializeWrapper classes:

  • ConsumerOffsetSerializeWrapper - has unquoted numeric keys and version mismatch

no action needed

  • TopicConfigAndMappingSerializeWrapper, TopicConfigSerializeWrapper, SubscriptionGroupWrapper
  • OffsetSerializeWrapper, TopicQueueMappingSerializeWrapper, MessageRequestModeSerializeWrapper
  • DelayOffsetSerializeWrapper, KVConfigSerializeWrapper, RocksDBOffsetSerializeWrapper
  • TransactionMetricsSerializeWrapper

All other wrappers use String keys and have consistent fields across versions.

2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - getAllTopicConfig from seq 0, max 2000, dataVersion
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - getAllTopicConfig from seq 2000, max 2000, dataVersion {"counter":6377,"stateVersion":34,"timestamp":1777529141245}
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - get all topic config, totalTopicNum: 2102
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - Update slave topic config from master, 172.20.221.1:10911
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - Update slave consumer offset from master, 172.20.221.1:10911
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - load /alidata1/admin/rocketmq/store/config/delayOffset.json OK
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - Update slave delay offset from master, 172.20.221.1:10911
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - getAllSubscriptionGroup from seq 0, max 2000, dataVersion
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - getAllSubscriptionGroup from seq 2000, max 2000, dataVersion {"counter":5163,"stateVersion":34,"timestamp":1777532462192}
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - getAllSubscriptionGroup from seq 4000, max 2000, dataVersion {"counter":5163,"stateVersion":34,"timestamp":1777532462192}
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - get all subscription group config, totalGroupNum: 5013
2026-05-07 20:47:25 INFO BrokerControllerScheduledThread1 - Update slave Message Request Mode from master, 172.20.221.1:10911
2026-05-07 20:47:26 INFO brokerOutApi_thread_2 - Registering current broker to name server completed. TargetHost=172.20.221.2:9876
2026-05-07 20:47:26 INFO brokerOutApi_thread_1 - Registering current broker to name server completed. TargetHost=172.20.211.2:9876

za-zhangzhisheng added 7 commits May 8, 2026 09:55
between 5.4.x and 5.5.x

When upgrading from 5.4.x to 5.5.x
… are added to ConsumerOffsetSerializeWrapper,

the try-catch fallback in RemotingSerializable is always necessary
…compatibility' into fix/consumerOffset-unquoted-key-compatibility
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug] JSONException: not support unquoted name

4 participants