Skip to main content

Topic Administration

Rafka treats topics as first-class entities — not implicit names that spring into existence on first produce, but registered objects with an identity, a config store, and an explicit lifecycle. P2.A ships four Kafka admin API keys that expose this model to standard Kafka clients.

Topic identity model​

Every registered topic carries three forms of identity:

FieldExamplePurpose
idtop_01ktb7wm5jvkbfgpzvfnk3kqqbStable globally-unique ULID-branded id. top_ prefix prevents cross-entity confusion.
slugmy-topicURL/routing-safe form. Derived by Slug::sanitize(name): lowercase, non-[a-z0-9-] → -, consecutive - collapsed, leading/trailing - trimmed, clamped to 63 chars.
RRLrafka/prod/east/topics/my-topicRafka Resource Locator: <org>/<env>/<cluster>/topics/<slug>. Uniquely addresses the topic within the mesh.

The raw Kafka name is stored verbatim alongside the slug. Kafka's naming rules ([a-zA-Z0-9._-], ≤249 chars, not . or ..) apply; the slug is derived from the name and is not independently validated as a Kafka name.

Example: topic named My.Topic → slug my-topic → RRL rafka/prod/east/topics/my-topic.

RRLs contain slashes, not colons. They are never placed in a URL path segment. Pass as a query parameter (?rrl=...) or request body field ({"rrl": "..."}) when needed.

Auto-create semantics​

Rafka distinguishes two topic-creation paths:

PathWhen it firesError code
AdminClient explicit createAdminClient.create_topics(...) → api_key 19Success: 0. Duplicate: 36.
Producer auto-createMetadata request with allow_auto_topic_creation=true (librdkafka producer default)Registers with 1 partition; no error.
AdminClient describeMetadata with allow_auto_topic_creation=false (AdminClient list/describe default)Topic absent → 3 UNKNOWN_TOPIC_OR_PARTITION.

The allow_auto_topic_creation flag is decoded from MetadataRequest (present at API version 4+). It is per-request, not a global config — the same gateway serves both producers (auto-create on) and AdminClients (auto-create off).

Produce operations are unaffected by the registry. Records are written to the WAL regardless of whether the topic is explicitly registered; the WAL materialises the virtual-topic key on first append.

Lifecycle operations​

Create (api_key 19 — CreateTopics)​

CreateTopics -> topic name, num_partitions, replication_factor, config map

Validations (Kafka-standard error codes):

  • Empty name / . / .. / chars outside [a-zA-Z0-9._-] / >249 chars → 17 INVALID_TOPIC_EXCEPTION
  • num_partitions < 1 → 37 INVALID_PARTITIONS
  • replication_factor < 1 → 38 INVALID_REPLICATION_FACTOR
  • cleanup.policy=compact with rafka.failover.enabled=true → 40 INVALID_CONFIG
  • Already registered → 36 TOPIC_ALREADY_EXISTS

On success: the topic entity is stored in the registry with the requested partition count, RF, and config map.

List (Metadata wildcard — api_key 3)​

Metadata(topics=null, allow_auto_topic_creation=false) -> all registered topics

AdminClient.list_topics() sends a null topic list with allow_auto_topic_creation=false. Rafka returns all registered topics for the org, each with their partition count from the registry.

Describe partition count (Metadata named — api_key 3)​

After create, a named Metadata request with allow_auto_topic_creation=false returns the topic's registered partition count. This is how list_topics() resolves the partition count — not from the CreateTopics response.

v4-cap note: CreateTopics is advertised at api_version 0–4. The num_partitions echo field in the response is only present at v5+ (flexible encoding). At v4, the field is absent and returns -1. This is benign: real AdminClients read the partition count from Metadata (see above), not from the create response. Verified by conformance test.

DescribeConfigs (api_key 32)​

Returns all stored configs for a topic. config_source=1 (DYNAMIC_TOPIC_CONFIG). Absent topic → error_code=3. Config names filter from the request is currently ignored; all stored configs are returned.

AlterConfigs (api_key 33)​

Full-replace semantics: the incoming config map entirely replaces the existing one. To add a key without removing others, include all desired keys in the request.

Config validations:

  • rafka.failover.enabled is set at CreateTopics time only (I2 immutability rule). Any AlterConfigs that changes the flag value — whether false→true or true→false — returns 40 INVALID_CONFIG. Set it in the config map of NewTopic if you need it enabled.
  • cleanup.policy containing compact while rafka.failover.enabled=true returns 40 INVALID_CONFIG (incompatible with Op 36 offset translation).

Delete (api_key 20 — DeleteTopics)​

Removes the topic from the registry and purges all WAL state for its partitions via KAFKA_OP_DELETE_TOPIC (mesh-op 0x05). Absent topic → error_code=3. The WAL purge is best-effort (registry remove is authoritative; broker errors are logged but do not fail the response).

Error code reference​

CodeNameWhen emitted
0—Success
3UNKNOWN_TOPIC_OR_PARTITIONAbsent topic in Delete / DescribeConfigs / AlterConfigs / Metadata(flag=false)
17INVALID_TOPIC_EXCEPTIONName violates Kafka naming rules
36TOPIC_ALREADY_EXISTSCreateTopics duplicate name
37INVALID_PARTITIONSnum_partitions < 1
38INVALID_REPLICATION_FACTORreplication_factor < 1
40INVALID_CONFIGConfig combination conflict