merlimat opened a new pull request, #601:
URL: https://github.com/apache/pulsar-client-cpp/pull/601

   ### Motivation
   
   Second implementation PR for the scalable-topics C++ SDK (`pulsar::st`), 
building on the protocol foundation merged in #600. It adds the **producer**: 
publishing to a scalable topic by fanning out to per-segment producers, driven 
by the DAG-watch layout and routing keys to segments exactly like the Java v5 
client (`ScalableTopicProducer`), so the two clients place keys on the same 
segments.
   
   Per PIP-468 a scalable topic's segments are ordinary persistent topics, so 
the produce data path reuses the classic `ProducerImpl` wholesale — one per 
active segment — and `lib/st` adds only the fan-out, routing, and id-mapping on 
top.
   
   ### Modifications
   
   One commit per step, in dependency order:
   
   1. **`segment://` topic domain** (classic client) — `TopicName` learns the 
segment domain (always v2; `<parent>/<hhhh-hhhh-id>`; persistent; 
`isSegment()`), and all public entry points reject segment topics; 
`ClientImpl::createSegmentProducerAsync()` is the `lib/st` bypass that may 
target one.
   
   2. **st `MessageId`** — segment-qualified ids: `MessageIdImpl` (v4 id + 
segment id + position vectors) and the `MessageIdV5` big-endian wire layout, 
field-for-field compatible with the Java client for id interop. The producer 
fills only the v4 id + segment id.
   
   3. **Broker-pin seam** — thread an optional `assignedBrokerUrl` from 
`createSegmentProducerAsync` through `ProducerImpl` to its initial `grabCnx`, 
so a per-segment producer connects straight to the DAG-provided owner broker 
instead of doing a fresh lookup. `HandlerBase::start()` delegates to a new 
`start(assignedBrokerUrl)` overload (`grabCnx()` already equals 
`grabCnx(nullopt)`, so other handlers are unaffected); every new parameter 
defaults to `nullopt`, leaving the public producer path unchanged.
   
   4. **Producer seam** — `pulsar::st::ProducerImplBase` (the abstract impl the 
public `detail::ProducerCore` forwards to) + `ProducerCore.cc` (the out-of-line 
forwarders, required once a core is actually minted from a `PULSAR_PUBLIC` 
class).
   
   5. **`StProducerImpl`** — the producer, a port of Java v5 
`ScalableTopicProducer`. It owns one `DagWatchSession` + one `SegmentRouter` 
per topic; per active segment it creates a classic `pulsar::Producer` (pinned 
to the segment's broker) cached as its creation `Future` so concurrent cold 
senders share one attempt. `sendAsync` routes each message by key (round-robin 
when keyless) to a segment, publishes it, and wraps the returned classic id 
with the segment id into a `pulsar::st::MessageId`. Route failure is terminal; 
a sealed/terminated segment retries with backoff (≤10, `min(100·(n+1), 500)`ms) 
and re-routes onto the layout the DAG watch delivers. Layout changes retire 
producers for departed segments; exclusive access modes eagerly attach (and 
fail `create()` up front if the claim is refused), Shared attaches lazily. 
`lastSequenceId`/`flush`/`close` behave as documented.
   
      No per-segment dispatch chain is needed — `st::Future` fires its 
listeners in FIFO registration order, so same-segment sends dispatch in call 
order for free (Java needs the chain only because `CompletableFuture` gives no 
such guarantee); a `FutureTest` locks that in.
   
   6. **Broker-free unit tests** — per-segment config isolation (a regression 
guard: `pulsar::ProducerConfiguration`'s copy constructor shares its impl, so 
each segment's config is built fresh, not copy-then-mutated), access-mode 
mapping by name (the st and classic enums differ in numeric order), 
segment-gone classification, and MessageId minting.
   
   ### What works after this PR
   
   `client.newProducer<T>().topic("segment://…").create()` returns a live 
producer; `send`/`sendAsync` route by key to the owning segment, publish 
through a per-segment classic producer pinned to the DAG broker, and return a 
segment-qualified `MessageId`; `lastSequenceId`, `flush`, and `close` work; 
layout changes retire departed segments and (for exclusive modes) eagerly 
attach; a send whose segment was sealed transparently retries and re-routes.
   
   ### Next phases (follow-up PRs)
   
   End-to-end validation against a scalable-topics broker (produce keyed + 
keyless, verify per-segment routing, segment ids, and message counts), then the 
Queue/Stream/Checkpoint consumers, the C API, and transactions. Entry-bucket 
batching (PIP-486) is deferred until a batcher-builder seam exists — today only 
the encryption-disables-batching branch is ported.
   
   ### Verifying this change
   
   - **88 broker-free `pulsar-st-tests`**: `Expected`/`Future` (incl. the 
FIFO-listener ordering the producer relies on), the segment layout/router, and 
the producer's config isolation, access-mode mapping, segment-gone 
classification, and id minting.
   - Full local build (vcpkg, macOS); **clang-format-11 clean**; **clang-tidy 
(LLVM 18 / libstdc++)** clean on the `lib/st` TUs and the changed classic files.
   - The full produce/route/retry behavior gets its end-to-end validation 
against a 5.0.0-M1 broker in the follow-up.
   
   ### Documentation
   
   - [ ] `doc` <!-- Your PR contains doc changes -->
   - [x] `doc-not-needed` <!-- Your PR changes do not impact docs -->
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to