From 36c2b2cb5194b9c20a207b47bed0dbe58ac68a76 Mon Sep 17 00:00:00 2001 From: KUAN-HAO HUANG <101171023+rich7420@users.noreply.github.com> Date: Tue, 25 Aug 2026 18:16:30 +0800 Subject: [PATCH] HDDS-16277. Reuse CharsetEncoder/Decoder in StringCodec StringCodecBase allocated a fresh CharsetEncoder/CharsetDecoder on every encode/decode call. Cache them per thread (ThreadLocal) since the codec instances are singletons and the coders are stateful and not thread-safe. The encoder is reset() before each reuse; the single-arg CharsetDecoder.decode resets internally, so behavior is unchanged. --- .../org/apache/hadoop/hdds/utils/db/StringCodecBase.java | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/db/StringCodecBase.java b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/db/StringCodecBase.java index f64f19318311..2ff7fe250859 100644 --- a/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/db/StringCodecBase.java +++ b/hadoop-hdds/common/src/main/java/org/apache/hadoop/hdds/utils/db/StringCodecBase.java @@ -42,6 +42,9 @@ abstract class StringCodecBase implements Codec { private final Charset charset; private final boolean fixedLength; private final int maxBytesPerChar; + // CharsetEncoder/CharsetDecoder are stateful and not thread-safe: reuse one per thread. + private final ThreadLocal threadLocalEncoder = ThreadLocal.withInitial(this::newEncoder); + private final ThreadLocal threadLocalDecoder = ThreadLocal.withInitial(this::newDecoder); StringCodecBase(Charset charset) { this.charset = charset; @@ -97,7 +100,7 @@ private int getSerializedSizeUpperBound(String s) { private PutToByteBuffer encode( String string, Integer serializedSize, Function newE) { return buffer -> { - final CoderResult result = newEncoder().encode( + final CoderResult result = threadLocalEncoder.get().reset().encode( CharBuffer.wrap(string), buffer, true); if (result.isError()) { throw newE.apply("Failed to encode with " + charset + ": " + result @@ -114,7 +117,7 @@ private PutToByteBuffer encode( String decodeNoFallback(ByteBuffer buffer) throws CodecException { try { - return newDecoder().decode(buffer.asReadOnlyBuffer()).toString(); + return threadLocalDecoder.get().decode(buffer.asReadOnlyBuffer()).toString(); } catch (Exception e) { throw new CodecException("Failed to decode " + buffer, e); } @@ -123,7 +126,7 @@ String decodeNoFallback(ByteBuffer buffer) throws CodecException { String decodeWithFallback(ByteBuffer buffer) { Runnable error = null; try { - return newDecoder().decode(buffer.asReadOnlyBuffer()).toString(); + return threadLocalDecoder.get().decode(buffer.asReadOnlyBuffer()).toString(); } catch (Exception e) { error = () -> LOG.warn("Failed to decode buffer with {}, buffer = (hex) {}", charset, StringUtils.bytes2Hex(buffer, 20), e);