https://github.com/apache/pulsar/pull/26447
Rendered version:
https://github.com/merlimat/pulsar/blob/mmerli/pip-494/pip/pip-494.md

------

# PIP-494: Scalable Topics Client Specification — authoritative
reference and change process

*Sub-PIP of [PIP-460: Scalable Topics](pip-460.md)*

> **Draft.** This PIP establishes a **process**; it deliberately does not 
> restate the specification's
> content, which lives in the repository at `spec/scalable-topics/`. The 
> current work-in-progress
> specification —
> <https://github.com/merlimat/pulsar/tree/mmerli/scalable-topics-spec/spec/scalable-topics>
>  — will be
> adopted as **version 1.0** of the specification. It reflects the current 
> client API and
> semantics as designed in the scalable-topics PIPs ([PIP-460](pip-460.md), 
> [PIP-468](pip-468.md),
> [PIP-483](pip-483.md), [PIP-486](pip-486.md)) and as implemented on the 
> `master` branch.

## Motivation

Scalable topics ([PIP-460](pip-460.md) and its sub-PIPs — the
controller [PIP-468](pip-468.md), auto
split/merge [PIP-483](pip-483.md), key-shared consumption
[PIP-486](pip-486.md)) introduce a client model
that is materially different from classic Pulsar topics: no
application-visible partitions, a dynamic
per-cluster segment layout the client must track, client-side key
routing, three distinct consumer
modes with different ordering/acknowledgment contracts, a checkpoint
position type, a controller-driven
consumer-assignment protocol, and a family of new binary-protocol commands.

Today the only complete description of that client behavior is the
**Java V5 client's source code**, with
the design rationale scattered across the PIPs. That is not a workable
basis for the ecosystem:

- **Other-language SDKs** (Go, Python, C++, Rust, Node, .NET) need a
precise, language-neutral definition
  of the API contract, the required client-side mechanisms, and the
exact wire interactions — not a Java
  implementation to reverse-engineer.
- **Interoperability** depends on every client routing keys
identically, applying layouts and assignments
  with the same staleness rules, and honoring the same ordering and
acknowledgment semantics. Without a
  normative reference, divergent clients are inevitable and hard to diagnose.
- **PIPs are design proposals, not contracts.** They explain *why* and
record decisions, but they are
  point-in-time documents that are never updated after acceptance. The
living behavior needs a living,
  normative document.
- **Stability and deprecation** need defined rules so SDK authors know
what they can rely on across
  Pulsar releases.

This PIP establishes a **Scalable Topics Client Specification** as the
single authoritative description
of the client API, its semantics, and its protocol-level interactions
— the base document for anyone
implementing scalable-topics support in a Pulsar client SDK — and
defines the **process** by which that
specification is maintained and changed.

## Background knowledge

**PIPs vs. a specification.** A PIP proposes and ratifies a change;
once merged it is a historical
record of a decision. A specification is a *current* normative
statement of behavior that implementations
conform to, and it evolves as behavior evolves. Mature
cross-implementation ecosystems keep both: the
OpenTelemetry project, for example, maintains a versioned
specification with explicit stability tiers and
a formal change process, separate from the proposals that motivated
each change. This PIP adopts that
model for scalable topics.

**Reference implementation.** The Java V5 client
(`pulsar-client-api-v5`, `pulsar-client-v5`) is the
reference implementation of scalable-topics client behavior. The
specification is derived from it and
verified against it, but the specification — not the Java code — is
the source of truth for conformance.

## Goals

### In Scope

- Establish `spec/scalable-topics/` in the `apache/pulsar` repository
as the **authoritative,
  language-neutral specification** for scalable-topics clients: the
API contract, its semantics, the
  required client-side mechanisms, and the wire-protocol interactions.
- Define the specification as the **required base for any new client
SDK** adding scalable-topics
  support, and the reference against which SDK **conformance** is judged.
- Define the **change process**: how normative changes to the
specification are proposed, reviewed, and
  landed, and how the specification is kept in sync with the reference
implementation.
- Define the specification's **versioning, stability tiers, and
deprecation/removal rules**, aligned with
  the Apache Pulsar release (LTS) cadence.

### Out of Scope

- The specification's content itself. It is a separate deliverable
(already drafted) and is not
  restated here; this PIP governs it, it does not define client behavior.
- Client behavior for classic (non-scalable) topics — the
specification covers scalable topics and their
  migration bridge from classic topics only.
- The mechanics of publishing the specification to the user-facing
documentation site
  (`apache/pulsar-site`); that is a follow-up task once the process is in place.

## High Level Design

### What is established

1. **Location and status.** The specification lives at
`spec/scalable-topics/` in `apache/pulsar`, as a
   set of Markdown documents with a top-level index (`README.md`). It
is a first-class artifact of the
   project, maintained alongside the code it describes.

2. **Authority.** For scalable-topics client behavior, the
specification is **normative**. Where an
   implementation (including the Java reference client) and the
specification disagree, the
   specification is the source of truth and the implementation is the
bug — unless a PIP changes the
   specification.

3. **Audience and use.** The specification is the base document for
implementing a scalable-topics
   client in any language. It separates the transport-agnostic **API
contract** (what an application
   observes: operations, inputs, guarantees, errors) from the
**implementation requirements** (what a
   client must do internally: routing, layout tracking, fan-out,
retries) and from the **wire protocol**
   (the exact command exchanges, with sequence diagrams), so an
implementer can build against the
   contract and bind it to the Pulsar protocol.

4. **Conformance.** The specification defines what a conformant client
MUST implement (the core
   producer, the three consumer modes, transactions) and which
capabilities are optional. An SDK claims
   scalable-topics support by conforming to the specification's
conformance document.

### How it is governed

The specification is **not itself a PIP**. It is a living document
maintained under the change process
below, which ties every normative change to a PIP and keeps the
document in lockstep with the reference
implementation.

## Detailed Design

### 1. Change process

- **Every normative change to the specification MUST be made through a
PIP.** A "normative change" is
  any change to a requirement (MUST/SHOULD/MAY), a guarantee, an
operation's contract, or a wire
  interaction. The PIP carries the design and rationale; reviewers
evaluate the proposed behavior and the
  specification edits **together**.
- **Specification edits land with the PIP.** When a PIP that changes
scalable-topics client behavior is
  accepted, the corresponding edits to `spec/scalable-topics/` are
merged together with it — normally in
  the **same pull request** as the implementation — so the contract
and its decision record never
  drift. A PIP that changes client-observable behavior is not complete
until its specification edits are
  merged.
- **Non-normative edits** (typos, wording clarifications that change
no requirement, added examples or
  diagrams, cross-reference fixes) do not require a PIP and follow the
normal pull-request review.
- **PIPs remain the rationale and decision record**; the specification
is the normative contract kept
  continuously in sync with the ratified PIPs. A reader who wants to
know *what* a client must do reads
  the specification; a reader who wants to know *why* follows its
references to the PIPs.

### 2. Relationship to the reference implementation

- The Java V5 client is the **reference implementation**. The
specification's Stable content describes
  behavior that is **implemented in a released broker and the
reference client, or ratified via an
  accepted PIP and committed to ship**; an SDK built to it
interoperates with current and announced
  brokers.
- A change to the reference implementation that alters
client-observable behavior MUST be accompanied
  by the matching specification change (via §1). Conversely, the
specification MUST NOT describe
  behavior the reference implementation does not (or will not, per an
accepted PIP) exhibit.
- The specification includes a **Java reference mapping** appendix
that maps each language-neutral
  operation and type to the Java V5 surface, so the reference
implementation and the specification can
  be checked against each other and other SDKs can align their naming.

### 3. Versioning, aligned with Pulsar LTS releases

- The specification carries its own version of the form `MAJOR.MINOR`,
declared in its index.
- **MAJOR** increments on a backward-incompatible change to a *Stable*
requirement and is **tied to the
  Apache Pulsar LTS cadence**: each new MAJOR corresponds to a new
Pulsar LTS release. Backward-incompatible
  changes and removals of deprecated features therefore land only at
an LTS boundary, never in an interim
  release.
- **MINOR** increments on backward-compatible additions or
clarifications and may occur in any release.
- The work-in-progress draft is currently at `0.x` (**Draft**).
Acceptance of this PIP adopts it as
  **`1.0`**, from which point the stability guarantees below take full
effect. Any later MAJOR
  transition is a normative change made through the change process (§1).

### 4. Stability tiers and deprecation

Every feature in the specification carries a stability tier expressing
its change contract:

- **Stable** — will not change incompatibly without a MAJOR bump;
conformant clients MUST implement all
  Stable, non-optional requirements.
- **Experimental** — may change in any way, including removal, in a
MINOR version; clients MAY implement
  it, and omitting it never makes a client non-conformant. Used for
designs that are documentable but not
  yet settled.
- **Deprecated** — Stable but scheduled for removal; retained for
compatibility, not to be newly adopted.

**Deprecation and removal mechanism.** A feature enters Deprecated
only through the change process (a
PIP) that records the reason, the replacement (or that none exists),
and the earliest version at which
removal is permitted. While Deprecated, brokers MUST keep honoring it,
clients SHOULD migrate, and
implementations SHOULD emit a non-fatal deprecation signal. A
Deprecated Stable feature MUST NOT be
removed before the next MAJOR version **and** MUST remain available at
least until the next Pulsar LTS
release, guaranteeing an LTS-to-LTS upgrade path in which the feature
and its replacement coexist.

### 5. Scope of the specification

- **In scope:** everything a client SDK must implement for scalable
topics — topic identity, the data
  model (message, message identifier, checkpoint, schema), the
producer and the three consumer modes,
  transactions, error semantics, the client-side mechanisms, the
complete wire protocol, conformance
  criteria, and compatibility with classic topics (the migration bridge).
- **Out of scope:** broker-internal behavior (controller, split/merge
execution, storage) except where
  observable by a client; and designs whose specification is not yet
settled (currently geo-replication
  of scalable topics), which enter the specification — initially as
Experimental — only once ratified.

### 6. Obligations on new client SDKs

An SDK adding scalable-topics support:

- MUST implement the specification's core (Stable, non-optional)
requirements and MAY implement optional
  capabilities, each fully per its section, gating on broker support
where the specification requires.
- MUST use the specification — not the Java client's source — as its
reference, and SHOULD map its
  idiomatic API to the specification's language-neutral operations (as
the Java mapping appendix does for
  Java), so cross-SDK behavior stays comparable.
- SHOULD contribute clarifications back through the change process
when the specification is found
  ambiguous or incomplete, rather than resolving the ambiguity privately.

## Public-facing changes

- **New repository artifact:** `spec/scalable-topics/` — the
specification document set. The
  work-in-progress draft linked above is adopted as **version 1.0**:
it reflects the current client
  API and semantics as designed in the scalable-topics PIPs and as
implemented on `master`; the
  stability guarantees of §3–§4 apply from that version.
- **Process:** the change process in §1 applies to every subsequent
PIP that touches scalable-topics
  client behavior, and to pull requests changing the reference
implementation's client-observable
  behavior.
- No code, wire-protocol, configuration, API, or metrics changes are
introduced by this PIP.

## Backward & Forward Compatibility

Not applicable to this PIP itself: it introduces a document and a
process, not a behavior change. The
compatibility guarantees the specification *makes* (protocol
extensibility by addition, cross-version
restorability of serialized identifiers and checkpoints, the
LTS-anchored deprecation window) are defined
within the specification and are governed by §3–§4 of this PIP.

## Alternatives

- **Keep the PIPs as the only description.** Rejected: PIPs are
point-in-time proposals that are never
  updated after acceptance; they cannot serve as a living, normative
contract, and they describe design
  rationale rather than exact client obligations.
- **Make the specification itself a PIP.** Rejected: a PIP is ratified
once and then frozen, whereas the
  specification must evolve continuously with the implementation.
Governing the specification *through*
  PIPs (§1) captures the review rigor without freezing the document.
- **Per-language documentation only (e.g. Javadoc as the reference).**
Rejected: language-specific
  documentation cannot define language-neutral contracts, wire
interactions, or conformance, and it
  invites divergence between SDKs.
- **Publish the specification only on the documentation site, not in
the repository.** Rejected for the
  source of truth: keeping it in `apache/pulsar` lets specification
edits merge in the same pull request
  as the implementation change (§1). Mirroring to the docs site is a follow-up.

## Links

<!-- Updated afterwards -->
* Specification (work in progress):
  
<https://github.com/merlimat/pulsar/tree/mmerli/scalable-topics-spec/spec/scalable-topics>
  — will live at `spec/scalable-topics/` in `apache/pulsar` (index: `README.md`)
* Related: [PIP-460](pip-460.md), [PIP-468](pip-468.md),
[PIP-483](pip-483.md), [PIP-486](pip-486.md)


--
Matteo Merli
<[email protected]>

Reply via email to