You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@pulsar.apache.org by lh...@apache.org on 2026/09/09 15:14:56 UTC
(pulsar) 02/14: [improve][broker] Copy small entries into a flat buffer when prepending broker entry metadata (#26464)
This is an automated email from the ASF dual-hosted git repository.
lhotari pushed a commit to branch branch-4.2
in repository https://gitbox.apache.org/repos/asf/pulsar.git
commit 77bab3bb4ae829fc7adc7c71b719cd3c419c5175
Author: Matteo Merli <mm...@apache.org>
AuthorDate: Fri Sep 4 14:56:14 2026 -0700
[improve][broker] Copy small entries into a flat buffer when prepending broker entry metadata (#26464)
(cherry picked from commit 7010231da456da96d49dff50f90225d196451f2a)
---
.../apache/pulsar/common/protocol/Commands.java | 25 ++++++++++++++++++++++
.../pulsar/common/protocol/CommandUtilsTest.java | 24 +++++++++++++++++++++
2 files changed, 49 insertions(+)
diff --git a/pulsar-common/src/main/java/org/apache/pulsar/common/protocol/Commands.java b/pulsar-common/src/main/java/org/apache/pulsar/common/protocol/Commands.java
index 275b6160061f..1b43686b33a4 100644
--- a/pulsar-common/src/main/java/org/apache/pulsar/common/protocol/Commands.java
+++ b/pulsar-common/src/main/java/org/apache/pulsar/common/protocol/Commands.java
@@ -132,6 +132,13 @@ public class Commands {
public static final short magicCrc32c = 0x0e01;
@SuppressWarnings("checkstyle:ConstantName")
public static final short magicBrokerEntryMetadata = 0x0e02;
+
+ /**
+ * Payloads up to this size are copied into a single buffer when broker entry metadata is prepended; larger ones
+ * are wrapped in a composite instead. Matches the size below which the BookKeeper client copies entries anyway.
+ */
+ @VisibleForTesting
+ static final int BROKER_ENTRY_METADATA_COPY_THRESHOLD = 16 * 1024;
private static final int checksumSize = 4;
@VisibleForTesting
@@ -1761,6 +1768,24 @@ public class Commands {
}
int brokerMetaSize = brokerEntryMetadata.getSerializedSize();
+ int payloadSize = headerAndPayload.readableBytes();
+
+ if (payloadSize <= BROKER_ENTRY_METADATA_COPY_THRESHOLD) {
+ // For small payloads, produce a single contiguous buffer. A composite reaching the socket goes through
+ // nioBuffers(), which allocates two NIO views per component on every flush (BK write and each consumer
+ // dispatch), and makes every metadata parse walk the components. At this size the copy is cheaper than
+ // that, and it happens once at publish while the entry is written and parsed many more times.
+ int totalSize = 6 + brokerMetaSize + payloadSize;
+ ByteBuf entry = PulsarByteBufAllocator.DEFAULT.buffer(totalSize, totalSize);
+ entry.writeShort(Commands.magicBrokerEntryMetadata);
+ entry.writeInt(brokerMetaSize);
+ brokerEntryMetadata.writeTo(entry);
+ entry.writeBytes(headerAndPayload, headerAndPayload.readerIndex(), payloadSize);
+ headerAndPayload.release();
+ return entry;
+ }
+
+ // For large payloads the copy would cost more than the per-flush view allocations it saves.
ByteBuf brokerMeta =
PulsarByteBufAllocator.DEFAULT.buffer(brokerMetaSize + 6, brokerMetaSize + 6);
brokerMeta.writeShort(Commands.magicBrokerEntryMetadata);
diff --git a/pulsar-common/src/test/java/org/apache/pulsar/common/protocol/CommandUtilsTest.java b/pulsar-common/src/test/java/org/apache/pulsar/common/protocol/CommandUtilsTest.java
index baea2b3f0279..8bf2d6f389ca 100644
--- a/pulsar-common/src/test/java/org/apache/pulsar/common/protocol/CommandUtilsTest.java
+++ b/pulsar-common/src/test/java/org/apache/pulsar/common/protocol/CommandUtilsTest.java
@@ -148,6 +148,30 @@ public class CommandUtilsTest {
dataWithBrokerEntryMetadata.release();
}
+ @Test
+ public void testAddBrokerEntryMetadataLargePayload() throws Exception {
+ int mockBatchSize = 10;
+ byte[] data = new byte[Commands.BROKER_ENTRY_METADATA_COPY_THRESHOLD + 1];
+ for (int i = 0; i < data.length; i++) {
+ data[i] = (byte) i;
+ }
+ ByteBuf byteBuf = PulsarByteBufAllocator.DEFAULT.buffer(data.length, data.length);
+ byteBuf.writeBytes(data);
+ ByteBuf dataWithBrokerEntryMetadata =
+ Commands.addBrokerEntryMetadata(byteBuf, getBrokerEntryMetadataInterceptors(), mockBatchSize);
+ assertTrue(dataWithBrokerEntryMetadata instanceof CompositeByteBuf);
+
+ BrokerEntryMetadata brokerMetadata =
+ Commands.parseBrokerEntryMetadataIfExist(dataWithBrokerEntryMetadata);
+ assertEquals(brokerMetadata.getIndex(), mockBatchSize - 1);
+ assertEquals(data.length, dataWithBrokerEntryMetadata.readableBytes());
+
+ byte[] content = new byte[dataWithBrokerEntryMetadata.readableBytes()];
+ dataWithBrokerEntryMetadata.readBytes(content);
+ assertEquals(content, data);
+ dataWithBrokerEntryMetadata.release();
+ }
+
@Test
public void testSkipBrokerEntryMetadata() throws Exception {
String data = "test-message";