Skip to content

[ISSUE #10467] Reduce per-message allocation in createUniqID and valueOfMagicCode#10469

Open
wang-jiahua wants to merge 1 commit into
apache:developfrom
wang-jiahua:perf/uniqid-magiccode-alloc
Open

[ISSUE #10467] Reduce per-message allocation in createUniqID and valueOfMagicCode#10469
wang-jiahua wants to merge 1 commit into
apache:developfrom
wang-jiahua:perf/uniqid-magiccode-alloc

Conversation

@wang-jiahua

Copy link
Copy Markdown

Which Issue(s) This PR Fixes

Fixes #10467

Brief Description

Two independent per-message allocation reductions:

  1. MessageClientIDSetter.createUniqID(): replaces new char[LEN * 2] per call with a ThreadLocal<char[]> that is allocated once per thread and reused. The char array is only used to build the msgId string within the method and does not escape. JFR shows ~405 char[] allocation events/60s from this site eliminated.

  2. MessageVersion.valueOfMagicCode(int): replaces Enum.values() (which copies the backing array per JDK spec) + O(n) loop with direct if-else matching on the two known magic code constants (MESSAGE_MAGIC_CODE and MESSAGE_MAGIC_CODE_V2). Eliminates the per-call array copy on the broker dispatch path.

Compatibility

  • createUniqID() produces the same output format — only the internal char buffer allocation strategy changes.
  • valueOfMagicCode() behavior is identical for all valid inputs. Invalid magic codes still throw IllegalArgumentException.

How Did You Test This Change

mvn -pl common -Dcheckstyle.skip=false -Dspotbugs.skip=true validate
# 0 Checkstyle violations

mvn -pl common -Dcheckstyle.skip -Dspotbugs.skip=true -Djacoco.skip=true test
# All tests pass

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR optimizes message version resolution and reduces allocations when generating unique message IDs.

Changes:

  • Replace enum iteration in MessageVersion#valueOfMagicCode with direct magic-code comparisons for v1/v2.
  • Reuse a per-thread char[] buffer in MessageClientIDSetter#createUniqID via ThreadLocal to reduce allocations.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
common/src/main/java/org/apache/rocketmq/common/message/MessageVersion.java Switches magic-code lookup from iterating enum values to direct conditional checks.
common/src/main/java/org/apache/rocketmq/common/message/MessageClientIDSetter.java Introduces a ThreadLocal<char[]> buffer to reuse memory during unique ID generation.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +74 to 80
if (magicCode == MessageDecoder.MESSAGE_MAGIC_CODE) {
return MESSAGE_VERSION_V1;
}
if (magicCode == MessageDecoder.MESSAGE_MAGIC_CODE_V2) {
return MESSAGE_VERSION_V2;
}

throw new IllegalArgumentException("Invalid magicCode " + magicCode);

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional trade-off. A static lookup map built from values() would achieve the same result but adds complexity for exactly two known constants. The if-else is trivially verifiable and a new version would just add one more line. The values() call this replaces was the problem — it copies the enum backing array on every invocation (JDK spec), which this fix eliminates.

Comment on lines +114 to +117
private static final ThreadLocal<char[]> UNIQ_ID_BUF = ThreadLocal.withInitial(() -> new char[LEN * 2]);

public static String createUniqID() {
char[] sb = new char[LEN * 2];
char[] sb = UNIQ_ID_BUF.get();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The retained buffer is LEN * 2 bytes (~32–64 bytes depending on IP version) per thread — negligible even with hundreds of threads. RocketMQ producer threads are long-lived pool threads, so the ThreadLocal lifecycle aligns with thread lifecycle. Added a brief inline comment noting the per-thread retention.


public static String createUniqID() {
char[] sb = new char[LEN * 2];
char[] sb = UNIQ_ID_BUF.get();

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point — renamed sb to buf for clarity.

…d valueOfMagicCode

- MessageClientIDSetter.createUniqID(): reuse a ThreadLocal<char[]>
  instead of allocating a new char[LEN*2] on every send.
- MessageVersion.valueOfMagicCode(): replace Enum.values() array copy +
  O(n) loop with direct if-else on the two known magic codes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@wang-jiahua wang-jiahua force-pushed the perf/uniqid-magiccode-alloc branch from a8e39ce to a3986cd Compare June 10, 2026 14:42

@oss-sentinel-ai oss-sentinel-ai left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review: Approved ✅

PR: #10469 — Reduce per-message allocation in createUniqID and valueOfMagicCode
Type: Performance (2 files, +12/-10)

Assessment

Optimizes hot path allocations:

  • ThreadLocal<char[]> for createUniqID() (eliminates ~405 char[] allocations/60s)
  • Direct if-else for valueOfMagicCode() (eliminates Enum.values() array copy)

Verdict

✅ Solid performance optimization based on JFR profiling. Fixes #10467.


🤖 Automated review by oss-sentinel-ai

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.

Reduce per-message allocation in createUniqID and valueOfMagicCode

3 participants