Repository navigation
[improve][cli] Add v4-client subcommands to pulsar-perf and pulsar-client - #26480
Conversation
…5 ones ### Motivation `pulsar-perf` was migrated to the V5 client API in apache#25887 (and apache#25917 did the same for `pulsar-client`), so every perf subcommand now drives the V5 SDK. That leaves no way to benchmark the v4 client, or v4 (non-scalable) topics, without the V5 SDK in the path — and several v4 capabilities became unreachable, because they have no V5 equivalent: - `--max-outstanding` / `--max-outstanding-across-partitions` and round-robin partition routing on the producer; - real `Exclusive` / `Failover` / `Key_Shared` subscription types, `MessageListener` dispatch on the client's listener threads, pooled messages, batch-index acknowledgment, the chunked-message knobs, the receiver-queue limits and the auto-scaled receiver-queue reporting on the consumer; - the v4 `Reader` itself — `read` measures a V5 `CheckpointConsumer`, a different broker-side entity — along with a `lid:eid` start message id, `--receiver-queue-size` and `--use-tls`; - the v4 transaction coordinator, which stays a live broker code path next to the v5 one (PIP-473 P5.4) with nothing driving it, and its acknowledgement round-trip latency (V5's `acknowledge` is a synchronous void, so its reported ack latency is a local measurement). ### Modifications Each benchmark is split into an abstract base holding everything that is not client-specific — the CLI options, the throughput/latency accounting, the run() skeleton and message loop, and the reports — plus two thin subclasses that bind the client types: the existing V5 command, and a new v4 one. PerformanceProducerBase -> PerformanceProducer / PerformanceProducerV4 (produce-v4) PerformanceConsumerBase -> PerformanceConsumer / PerformanceConsumerV4 (consume-v4) PerformanceReaderBase -> PerformanceReader / PerformanceReaderV4 (read-v4) PerformanceTransactionBase -> PerformanceTransaction / PerformanceTransactionV4 (transaction-v4) This is a split rather than a revert, so the bases keep the improvements made since the migration — instance recorders, the 3-significant-digit histograms and the latency clamps from apache#26466, class-based subcommand registration from apache#26467 — and both commands share one benchmark and one set of measurements. The v4 commands offer the same flags as their V5 counterparts (only the V5-only `--scalable-consumer-type` and `--scalable` are not mirrored). Each base logs through a logger named after the concrete subclass, so the report lines keep naming the subcommand that produced them. Where the two clients genuinely differ, the seam is explicit: the v4 consumer and reader keep `MessageListener`/`ReaderListener` dispatch while V5 keeps its poll threads; the v4 producer does not await a transaction's sends before committing (V5 must, because its transactional sends are queued onto an internal dispatch chain); only the first transaction of a V5 test thread waits out the coordinator's asynchronous connect, so the rollover loop still counts every failed open. `--jsse-provider` / `--jca-provider` are also wired into the v4 client builder, which previously read them only for the admin and V5 legs. ### Verifying this change New unit and end-to-end tests in `pulsar-testclient` cover subcommand registration and naming, conf-file defaults and case-insensitive enums on the v4 commands, the shared `-st` enum mapping onto the v4 client enum, produce / consume / read round trips through the v4 commands, the `lid:eid` start position, and the v4-only producer knobs. `PerfToolTest` gains v4 cases that run the commands from `bin/pulsar-perf` in a container.
… V5 ones ### Motivation `pulsar-client` was migrated to the V5 client API in apache#25917, which left a set of options that are still accepted but no longer do what they say: - `produce`: `--key-value-encoding-type` is rejected outright and `-kvk` / `-kvkf` / `-ks` became dead flags; `--disable-replication` only warns. - `consume`: `--subscription-mode NonDurable` silently creates a durable subscription; `--regex` is not a regex any more but a namespace subscription over the pattern's `tenant/namespace`; `Exclusive` / `Failover` degrade to work-queue semantics; `--start-timestamp` was dropped; `-mc` / `-ac` / `-pm` only warn. - `read`: the `<ledgerId>:<entryId>` start position is rejected (and its WebSocket base64 encoding is gone), `-i` and `-q` only warn. `read` also drives a V5 `CheckpointConsumer`, a different broker-side entity, so nothing exercises the v4 `Reader`. - Root: `loadConf` is gone, so every `client.conf` key without a dedicated CLI flag is silently ignored, and `http://` / `https://` service URLs no longer work. Two `PulsarClientToolTest` cases were disabled by that migration with a "deferred to a follow-up" note. This is that follow-up. ### Modifications The same split as the pulsar-perf change: an abstract base per command holds everything that is not client-specific — the CLI options, the argument validation and the WebSocket path, which speaks HTTP and has no client generation of its own — and two thin subclasses bind the client types. AbstractCmdProduce -> CmdProduce / CmdProduceV4 (produce-v4) AbstractCmdConsumeCommand-> CmdConsume / CmdConsumeV4 (consume-v4) AbstractCmdReadCommand -> CmdRead / CmdReadV4 (read-v4) `AbstractCmdConsume` keeps only what both generations share; the message rendering genuinely differs (the v4 one prints the encryption context, the formatted broker publish/event times, the ordering key, the schema version and the index, none of which the V5 `Message` exposes), so it lives in `V5MessageSupport` / `V4MessageSupport` rather than being forced into one shape. `PulsarClientTool` builds a second, v4 `ClientBuilder` for the `*-v4` commands. That one keeps `loadConf`, so every `client.conf` key still applies without a hand-written translation, and it accepts `http://` service URLs. `pulsar-shell` picks the new commands up for free, and so does `generate_documentation`. ### Verifying this change New unit tests cover subcommand registration and naming, case-insensitive enums on the v4 subcommands, KeyValue schema building, the `lid:eid` start position (including its WebSocket encoding) and the `--start-timestamp` validation. The three KeyValue `PulsarClientToolTest` cases disabled by apache#25917 are re-enabled against `produce-v4`, and a new `testNonDurableSubscribeWithV4Client` asserts that `consume-v4` really creates a non-durable subscription — the subscription disappears when the consumer disconnects, which is exactly what the V5-based `consume` cannot do.
|
This change is necessary to be able to profile the V4 client changes and ordinary V4 topic behavior in master branch. |
david-streamlio
left a comment
There was a problem hiding this comment.
Reviewed the full diff (34 files, +6439/-3020) by theme rather than file order, focusing on the
central claim that "the V5 commands are unchanged in behaviour". I rebuilt the pre-split files and
compared them line by line against base + V5 subclass for all seven splits.
What I ran locally (JDK 25, macOS): quickCheck — pass; :pulsar-testclient:compileTestJava
and :pulsar-client-tools:compileTestJava — pass; PulsarPerfTestToolTest 6/6,
CmdV4CommandsTest 11/11, PerformanceV4CommandsTest 9/9. I did not run
PulsarClientToolTest or the PerfToolTest container ITs.
The split itself is the right shape — abstract base + two sibling subclasses, with the seams placed
where the clients genuinely differ, is much better than reverting or duplicating. Six of the seven
splits are faithful. The findings below are concentrated in one file.
Blocking
1. ProducerSocket.onMessage lost its null guard — AbstractCmdProduce.java:441-442
Base (CmdProduce.java:522-527):
log.info().attr("ack", msg).log("Received ack");
if (this.result != null) {
this.result.complete(null);
}Now:
log.info().attr("msg", msg).log("ack= ");
this.result.complete(null);result is null until the first send(), so any text frame the WebSocket proxy sends outside a
pending send — before the first message, or after close() — now NPEs inside the Jetty
@OnWebSocketMessage callback and fails the session, where it was previously ignored. The
reordering in send() (finding 2) narrows the window but does not close it, because onMessage
fires for any text frame, not only acks.
This whole nested class looks copied from 820300f93b^ (pre-#25917) rather than moved from the PR
base — which would also explain the changed log line: every ack on the
pulsar-client produce ws://… path now logs ack= with attr msg instead of Received ack
with attr ack. Suggest restoring both the guard and the base's log text.
V5 behaviour changes that the description says do not exist
None of these look wrong — several are clear fixes — but the PR states the V5 commands are
unchanged, and a reviewer diffing later will trip over them. Worth a line in Modifications.
AbstractCmdProduce.java:408-409—send()now assignsthis.resultbeforesendText(...);
the base assigned it after. The base ordering races: an ack arriving in between completes a
stale future and the caller blocks the full 30 s. This is a real fix — keep it, disclose it.AbstractCmdProduce.java:450-451—close()gains asession != nullguard. Also a fix.PerformanceTransactionBase.java:457-469— the receive-failure path now catchesException
(not justPulsarClientException) andreturns afterexit(1). Under a test exit procedure the
base fell through intomessage.id()on a null message and NPE'd, so this is a fix too.PerformanceReaderBase.java:158-161—FutureUtil.waitForAll(futures).get()is dropped in
favour of drainingfuture.get()in list order. Reader creation no longer fails fast on
whichever future fails first. Minor, but it is a real difference.PerformanceConsumerBase.java:348-351— the per-topic log line loses itsconsumerType
attribute (moved to a one-time line). Changes what a per-topic grep sees.CmdRead.java:112-114— new log when-q > 0; the base ignoredreceiverQueueSizesilently.
Together with the new "Use consume-v4 / read-v4 / produce-v4" sentences in the V5 warnings
(CmdProduce.java:70,CmdConsume.java:85,94,CmdRead.java:88-89), this is new user-visible
output on commands described as unchanged. All sensible additions — just not "unchanged".
Test coverage
8. transaction-v4 has no functional test at all. PerformanceTransactionV4 is 144 lines of
new production code, and every test reference is metadata-only: registered under its own name
(PulsarPerfTestToolTest.java:51), its flag set is a subset of transaction
(:79-81), and its logger is named correctly (:115-116). Nothing opens, commits or aborts a
transaction through it. I grepped every test source tree to confirm. Driving the v4 transaction
coordinator is the stated motivation for the command, and the V5 counterpart has three real tests
in PerformanceTransactionTest. This is the one new subcommand of seven with zero behavioural
coverage.
9. consume-v4's --start-timestamp / --end-timestamp is only tested negatively.
CmdConsumeV4.java:134-135 calls consumer.seek(startTimestamp) and :147 filters on
getPublishTime(). The only coverage is two rejection messages
(CmdV4CommandsTest.java:218-229). Delete the seek(...) and the filter and every test in this PR
still passes — yet CmdV4CommandsTest.java:69 names timestamp seek as the reason consume-v4
exists.
Test quality
PerformanceV4CommandsTest.java:169-177— assertsgetDeliverAtTime()positionally on two
received messages, assuming the-dr 0,1message arrives first. A message stamped
deliverAtTime = nowgoes through the delayed-delivery tracker; one tick of deferral swaps the
order and the test fails for an unrelated reason. The payloads are already distinct
("delayed"/"plain") — key the assertions offnew String(msg.getData())instead.
(Passed 9/9 locally, but the race is structural.)PerformanceV4CommandsTest.java:213— asserts backlog with no Awaitility.exitLatchis
released fromPerfClientUtils.exit(0)(PerformanceConsumerBase.java:483) before
stopConsuming(); closeClient(client)flushes pending acks, and acks are grouped on a 100 ms
delay.Awaitility.untilAssertedis what CODING.md asks for here.PulsarClientToolTest.java:578,629— the two re-enabled cases carry notimeOut(every other
@Testin the file does), and theCompletableFuturethey create is completed exceptionally at
:648but never inspected, so a non-zeroproduce-v4exit surfaces asassertNotNull(message)
with the cause discarded.testNonDurableSubscribeWithV4Client:216-217gets this right.
To be clear, the three re-enabled KeyValue cases are otherwise genuine — they decode with a
Schema.KeyValueconsumer and would fail against a brokenproduce-v4.TestCmdConsume.java:36-38— the PR already retargets this reflection to
AbstractCmdConsumeCommand. SincesubscriptionNameisprotected
(AbstractCmdConsumeCommand.java:83) and the test is in the same package, the three lines and
thejava.lang.reflect.Fieldimport can just be deleted in favour of
cmdConsume.subscriptionName = "my-sub";— CODING.md's no-reflection rule, and this is the
moment since the line is being touched anyway.
v4-only correctness
PerformanceTransactionBase.java:522-532+PerformanceTransactionV4.java:119-124— on V5 the
ack callback runs inline on the worker thread, but v4'sacknowledgeAsynccompletes on a client
internal thread. Theexceptionallyblock interrupts that thread and takes the early
return null, skippingnumMessagesAckFailed.increment(). So ontransaction-v4an
interrupt-caused ack failure is silently uncounted and the worker never learns to stop. The same
pattern insendAndRecordis pre-existing; the ack side is new.CmdProduceV4.java:189-194—nativeAvroSchemaOrNullreturns null whengetNativeSchema()is
empty, andgenerateMessageBodiesthen ships the raw JSON text as the payload. The historic v4
code didnativeSchema.get()and threw.AUTO_PRODUCE_BYTESdelegatesgetNativeSchema(), so
the normal-s "avro:{…}"path is unaffected — but the fallback turns a loud failure into
silently wrong data. Prefer throwing.
Nits
AbstractCmdReadCommand.java:96—protected abstract String startMessageId()is implemented
twice and never called (webSocketStartMessageId()is the one used at:140). Dead; delete.PulsarClientTool.buildV4ClientBuilder— the"proxy-protocol must be provided with proxy-url"
throw is unreachable;updateConfig()already returns 1 for that case inpreRun().PulsarClientTool.buildV4ClientBuilder—serviceUrl(...)andtlsTrustCertsFilePath(...)are
applied unconditionally afterloadConf(conf), so a null--tlsTrustCertsFilePathnulls out
the valueclient.confjust supplied. Faithful to the pre-#25917 code, so not a new
regression — but since it is being written fresh,isNotBlankguards would fix a latent bug.PerfClientUtils.java:134-148— the new v4 block mirrors the admin block exactly, good. Note
the V5 path applies the providers only insideif (wantsTls(arguments))while v4/admin apply
them unconditionally, so--jsse-provideron a plaintext URL is a silent no-op on V5 and a
silent write on v4. Harmless, but inconsistent for a flag whose purpose is FIPS validation.CmdV4CommandsTest.java:231-234uses fully-qualifiedjava.util.Set/TreeSet/Arrays
inline; the siblingPulsarPerfTestToolTest.java:176-181imports them.
Verified clean
For the record, these all checked out: every LongAdder in the producer, consumer and transaction
splits increments on exactly the same paths (the commit/abort duplication collapsing into
endTransaction accounts for the whole delta); nextDeliverAfterSeconds() preserves the original
delay > 0 / delayRange precedence, and returning Long rather than a sentinel is the right call
for a drawn delay of 0; V5MessageSupport is character-identical to the base rendering code, so no
printed output changes on the V5 path; the lazily-supplied v4 ClientBuilder is reached only by the
three *-v4 commands and each wraps it in try-with-resources, so --help and
generate_documentation still run without a service URL and the client is never leaked; the
@CustomLog → Logger.get(getClass()) change is name-preserving, so PerfToolTest's aggregated-
throughput assertions still match; messageFormatter and executorShutdownNow going static →
instance is a genuine improvement; all new files carry the ASF header and quickCheck is clean.
|
One follow-up from reading #26466, which this PR builds on. Not a defect in either change — the rationale comments for
#26466 converted every one of those to an instance field in the same commit that added these comments — The conclusion is untouched — 3 digits is still clearly right, and the ceiling the test pins is still worth pinning. It is only the "every subcommand pays" justification that no longer applies; the cost is now bounded to the one command being run. Measured on HdrHistogram 2.1.9, for the ranges the test uses:
(The "11-22 MB" figure holds if you include the pre-#26466 33 h range, which came to 21.00 MB.) Since |
…ands Follow-up to the review on apache#26480, pulsar-perf half. - `transaction-v4` had no functional coverage: every existing reference was metadata-only, so the transaction binding itself was unpinned. Adds `PerformanceTransactionV4Test` with a commit run and an `-abort` run against plain `persistent://` topics. The commit run asserts both halves are durable (produced messages visible, consume backlog down by one per transaction); the abort run asserts neither is (nothing visible, backlog unchanged, and all ten messages redeliverable on the tool's own subscription, which is what separates a real abort from a transaction that was merely never ended). Mutation checked: making `sendMessage` or `acknowledgeAsync` ignore the transaction, dropping the acknowledgement entirely, or making `abortTransaction` a no-op each fail it. - `PerformanceConsumerBase` had dropped the `consumerType` attribute from the per-topic "Adding consumers" line, which the pre-split `PerformanceConsumer` logged. Restores it through a `consumerTypeForLog()` hook: the base reports the shared `--subscription-type`, `PerformanceConsumer` overrides it with `scalableConsumerType`, so the V5 line matches the pre-split output again. - `PerformanceTransactionBase` interrupted `Thread.currentThread()` from its failure callbacks. That is the worker thread for a V5 ack, whose future is already complete when returned, but a client-internal thread for a v4 ack and for the send and end-transaction futures on both clients. All three callbacks now capture the worker thread, which is the thread `runWorker` invokes them on. - `transaction-v4`'s and `produce-v4`'s `sendTimeout(0)` was annotated "a send timeout and a transaction are mutually exclusive on the v4 client". That guard was removed in apache#16519; the setting is still right because a send timeout fails the send on its own schedule and takes the transaction with it, so the comments now say that instead. - `PerformanceV4CommandsTest`: the backlog assertion now waits with Awaitility (the exit latch is released before the client close that flushes the acks, and the 30s join is unchecked), and the delivery-time assertions also pin each message's payload so a future reordering fails legibly. - The `LATENCY_HISTOGRAM_SIGNIFICANT_DIGITS` rationale still described the static recorders that apache#26466 replaced with instance fields in the same commit that added the comment. Rewords both copies, with the figure re-measured on HdrHistogram 2.2.2: 16.00 MB (1h in micros) and 14.00 MB (10d in millis) at 5 digits, not the pre-apache#26466 ranges' 11-22 MB.
…mmands Follow-up to the review on apache#26480, pulsar-client half. - `ProducerSocket.onMessage` had lost the `if (this.result != null)` guard, so a text frame arriving outside a pending send would NPE on the Jetty callback thread where it used to be ignored. Restores the guard and the pre-split log text ("Received ack" with attr `ack`); the version in the split was a hand-rendering of the pre-apache#25511 slf4j format string. Also drops the dead `throws InterruptedException` on `onConnect`, and comments the two deliberate changes in the same class: `send()` installs the completion future before `sendText(...)` (the old order let an ack complete the previous future and hang the caller for the full 30s), and `close()` null-checks the session that `onClose` clears. - `consume-v4`'s `--start-timestamp` seek and `--end-timestamp` filter were only covered by their rejection messages: deleting either line left every test passing, though timestamp seek is the stated reason the command exists. Adds `PulsarClientToolTest.testConsumeV4StartAndEndTimestamp`, which pins both against a real observed publish time. Mutation checked: deleting either line fails it. - `CmdProduceV4.nativeAvroSchemaOrNull` returned null for an Avro schema with no native definition, which would ship the raw JSON text as the payload; the pre-apache#25917 command threw. Throws again. Not reachable from any current CLI invocation, but the invariant is worth keeping explicit. - `PulsarClientTool.buildV4ClientBuilder`: removes the unreachable "proxy-protocol must be provided with proxy-url" throw — `updateConfig()` is the `preRun()` hook and already returns 1 for that case before the supplier that reaches this method is even created, and `ClientBuilderImpl` re-checks it. Keeps the outer `isNotBlank` guard, which unlike the V5 path is load-bearing here because this builder uses `loadConf()`. Documents why `serviceUrl` and `tlsTrustCertsFilePath` are applied unconditionally. - Deletes `AbstractCmdReadCommand.startMessageId()` and both overrides: never called, `webSocketStartMessageId()` is the one in use. - `TestCmdConsume` sets `subscriptionName` directly instead of reflecting into it; the field is protected and the test is in the same package. - `CmdV4CommandsTest` imports the `java.util` types its helper used inline.
|
Thank you for this — rebuilding the pre-split files and diffing them against Blocking1. One correction on provenance: the class was not copied from Also a small correction on reachability, which is why I have not treated it as blocking for the V5 behaviour changes not disclosed in the description (2, 3, 4, 6, 7)All correct, and the description was wrong to claim "unchanged in behaviour" without qualification.
Findings I could not reproduce (5, 10, 18)I checked each of these three carefully and I do not think the mechanism exists. Happy to be shown 5. 10. Positional That said, your instinct that the test reads fragilely is fair, so I took the half of the suggestion 18.
Test coverage (8, 9)8.
Both pass. Mutation-tested to confirm they are load-bearing:
I want to be straight about that last row rather than claim more than I verified — and chasing it So the line is still right, for a weaker reason: a send timeout is now permitted, but it still fails One correction to the finding: the V5 counterpart has one real test for the 9.
Worth noting Test quality (10, 11, 12, 13)
v4-only correctness (14, 15)
Nits (16, 17, 19, 20)
Follow-up comment: stale
|
…ir run budget The bounded join in PerformanceTransactionV4Test.run() allows 90s, but the method timeOut was 120s, leaving only 30s for assertions that themselves allow up to 30s of Awaitility plus ten 15s receives. A slow run would trip TestNG's opaque timeout instead of the helper's explicit "did not finish within" failure. Raise the method budget to 180s so the helper reports first.
…ient (apache#26480) ### Motivation `pulsar-perf` was migrated to the V5 client API in apache#25887 and `pulsar-client` in apache#25917, so every subcommand of both tools now drives the V5 SDK. Pulsar 5.0 does not deprecate ordinary (non-scalable) topics and the v4 client remains fully supported, so it is still useful to drive the CLI tools with the v4 client: for benchmark results comparable across broker versions (the v4 commands can also target pre-5.0 brokers, which the V5 SDK cannot talk to at all), and for testing the v4 client and ordinary topics without the V5 SDK in the path. The migration also left a set of options that are still accepted but no longer do what they say, with no way to get the behaviour back — round-robin partition routing and the outstanding-message limits on `produce`, the real subscription types and the receiver-queue knobs on `consume`, the v4 `Reader` and its `<ledgerId>:<entryId>` start position on `read`, the v4 transaction coordinator on `transaction`, KeyValue schemas and non-durable subscriptions on `pulsar-client`, and `loadConf` for `client.conf` keys without a dedicated CLI flag. ### Modifications Each command is split into an abstract base holding everything that is not client-specific, plus two thin sibling subclasses that bind the client types — the existing V5 command and a new v4 one: ``` PerformanceProducerBase -> PerformanceProducer / PerformanceProducerV4 (produce-v4) PerformanceConsumerBase -> PerformanceConsumer / PerformanceConsumerV4 (consume-v4) PerformanceReaderBase -> PerformanceReader / PerformanceReaderV4 (read-v4) PerformanceTransactionBase -> PerformanceTransaction / PerformanceTransactionV4 (transaction-v4) AbstractCmdProduce -> CmdProduce / CmdProduceV4 (produce-v4) AbstractCmdConsumeCommand -> CmdConsume / CmdConsumeV4 (consume-v4) AbstractCmdReadCommand -> CmdRead / CmdReadV4 (read-v4) ``` `PulsarClientTool` builds a second, lazily-supplied v4 `ClientBuilder` for the `*-v4` commands; it keeps `loadConf` and accepts `http://` service URLs. `pulsar-shell` picks the new `pulsar-client` commands up for free, and both tools' `gen-doc` / `generate_documentation` render them. New subcommands only. The V5 commands keep their behaviour apart from a few deliberate fixes that the split surfaced (the `ProducerSocket` send/ack ordering and its `close()` null guard, the transaction receive-failure path), plus some added "use the `-v4` command" pointers on existing warnings — all listed in the PR description. ### Verifying this change Covered by `PulsarPerfTestToolTest`, `PerformanceV4CommandsTest`, `PerformanceTransactionV4Test`, `CmdV4CommandsTest`, `PulsarClientToolTest` (including the three KeyValue cases disabled by apache#25917, now re-enabled against `produce-v4`), and new `produce-v4` / `consume-v4` / `read-v4` cases in the `PerfToolTest` integration test.
Motivation
pulsar-perfwas migrated to the V5 client API in #25887 andpulsar-clientin #25917, so everysubcommand of both tools now drives the V5 SDK. Pulsar 5.0 does not deprecate ordinary
(non-scalable) topics, and the v4 client remains fully supported, so it is still useful to be able
to drive the CLI tools with the v4 client:
pulsar-perfis comparing numbers between Pulsar releases. Those comparisons only hold if theclient under test is the same one, so the v4 commands can also be pointed at pre-5.0 brokers,
which the V5 SDK cannot talk to at all.
cannot influence the result.
Beyond that, the migration left a set of options that are still accepted but no longer do what they
say, with no way to get the behaviour back:
pulsar-perfproduce:--max-outstanding/--max-outstanding-across-partitionshave no effect, andround-robin partition routing is gone.
consume:Exclusive/Failover/Key_Sharedall degrade to work-queue semantics;--auto-scaled-receiver-queue-size,--batch-index-ack,--pool-messages,--receiver-queue-size-across-partitionsand the chunked-message knobs are inert. Dispatch movedfrom the client's listener threads to per-consumer poll threads.
read: measures a V5CheckpointConsumer, a different broker-side entity, so nothing exercisesthe v4
Reader. The<ledgerId>:<entryId>start position is rejected, and--use-tlsand--receiver-queue-sizeare inert.transaction: the v4 transaction coordinator stays a live broker code path next to the v5 one(PIP-473 P5.4) with nothing driving it. Acknowledgement latency is no longer measurable, because
V5's
acknowledgeis a synchronous void, so the reported figure times a local call rather than thebroker round trip.
pulsar-clientproduce:--key-value-encoding-typeis rejected outright and-kvk/-kvkf/-ksbecamedead flags;
--disable-replicationonly warns.consume:--subscription-mode NonDurablesilently creates a durable subscription;--regexisnot a regex any more but a namespace subscription over the pattern's
tenant/namespace;--start-timestampwas dropped;-mc/-ac/-pmonly warn.read: the<ledgerId>:<entryId>start position is rejected (and its WebSocket base64 encoding isgone);
-iand-qonly warn.loadConfis gone, so everyclient.confkey without a dedicated CLI flag is silentlyignored, and
http:///https://service URLs no longer work.Two
PulsarClientToolTestcases were disabled by #25917 with a "deferred to a follow-up" note. Thisis that follow-up.
Modifications
Rather than reverting or duplicating the commands, each one is split into an abstract base holding
everything that is not client-specific, plus two thin subclasses that bind the client types — the
existing V5 command and a new v4 one. The V5 and v4 classes are siblings, not parent and child.
The bases hold the CLI options, the argument validation, the accounting and reports (perf) and the
WebSocket paths (client), so the improvements made since the migration are shared rather than
duplicated — instance recorders, the 3-significant-digit histograms and the latency clamps from
#26466, and the class-based subcommand registration from #26467. Each perf base logs through a
logger named after the concrete subclass, so the report lines keep naming the subcommand that
produced them.
Where the two clients genuinely differ, the seam is explicit rather than papered over:
MessageListener/ReaderListenerdispatch, while V5 keeps itspoll threads;
transactional sends are queued onto an internal dispatch chain);
so the rollover loop still counts every failed open;
pulsar-client's message rendering is not forced into one shape: the v4 output keeps theencryption context, the formatted broker publish and event times, the ordering key, the schema
version and the index, none of which the V5
Messageexposes.PulsarClientToolbuilds a second, v4ClientBuilderfor the*-v4commands. It keepsloadConf,so every
client.confkey still applies without a hand-written translation, and it acceptshttp://service URLs. It is supplied lazily, becauseupdateConfig()is thepreRun()hook andruns for every invocation — building it eagerly would make
--help,generate_documentationandthe V5 commands depend on a service URL and on
client.confkeys only the v4 client parses.pulsar-shellpicks the newpulsar-clientcommands up for free, and both tools'gen-doc/generate_documentationrender them.Also wires
--jsse-provider/--jca-providerinto the v4 client builder inPerfClientUtils,which previously read them only for the admin and V5 legs.
The V5 commands are unchanged in behaviour except for the following, all of which are deliberate and
were verified line by line against the pre-split code:
ProducerSocket.send()now installs the completion future beforesendText(...). The old orderraced: an ack delivered on the Jetty read thread in between completed the previous future and left
the new one uncompleted, so the caller blocked the full 30s and the produce loop aborted with -1.
Latent since [pulsar-client-tools] Add support for websocket produce/consume command #3835.
ProducerSocket.close()gained asession != nullguard.onClosenulls the session, so aremote-initiated close before
close()used to NPE and turn a successful publish into exit -1.Exceptionrather thanPulsarClientExceptionandreturns after
exit(1). The widening is forced — the v4 and V5 overrides throw two differentPulsarClientExceptionclasses — and thereturnstops a fall-through intomessage.id()on anull message under a test exit procedure.
--queue-sizeis now reported as inert onreadinstead ofsilently ignored; the existing "no effect" warnings on
produce/consume/read(both tools)and on
transactiongained a "use the-v4command" pointer; andpulsar-perf consumelogs onenew unconditional INFO line naming the V5 consumer API.
Verifying this change
This change added tests and can be verified as follows:
pulsar-testclientPulsarPerfTestToolTest— subcommand registration and per-command naming, conf-file defaults andcase-insensitive enums on the v4 commands, the shared
-stenum mapping onto the v4 client enum,the logger naming the report lines depend on, and that a
--delay-rangedraw of0isdistinguishable from "no delay flag".
PerformanceV4CommandsTest— end-to-end produce / consume / read round trips through the v4commands against a mocked broker, partitioned-topic creation, the v4-only producer knobs asserted
on the built configuration, the
<ledgerId>:<entryId>start position (asserted positionally, soa reader that ignored it fails), and that
readrejects that form whileread-v4accepts it.PerfToolTest(integration,CLIgroup) gainsproduce-v4/consume-v4/read-v4cases thatrun the commands from
bin/pulsar-perfin a container.pulsar-client-toolsCmdV4CommandsTest— subcommand registration and naming, case-insensitive enums on the v4subcommands, KeyValue schema building, the
lid:eidstart position including its WebSocketencoding, the
--start-timestampvalidation, and that commands needing no v4 client (--help,generate_documentation) still run without a service URL and withclient.confkeys only the v4loader parses.
PulsarClientToolTest— the three KeyValue cases disabled by [improve][cli] Migrate pulsar-client to the V5 client API #25917 are re-enabled againstproduce-v4, and a newtestNonDurableSubscribeWithV4Clientasserts thatconsume-v4reallycreates a non-durable subscription, i.e. it disappears when the consumer disconnects.
The full pipeline is green in Personal CI for both halves (43/43 checks each).
Does this pull request potentially affect one of the following parts:
If the box was checked, please highlight the changes
New subcommands only; no existing command's options or behaviour change.
pulsar-perfgainsproduce-v4,consume-v4,read-v4,transaction-v4, andpulsar-clientgains
produce-v4,consume-v4,read-v4.Documentation
docdoc-requireddoc-not-neededdoc-completeThe generated CLI reference on the website is produced from the commands themselves
(
pulsar-perf gen-doc/pulsar-client generate_documentation), so the new subcommands are pickedup automatically.