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:
| Field | Example | Purpose |
|---|---|---|
id | top_01ktb7wm5jvkbfgpzvfnk3kqqb | Stable globally-unique ULID-branded id. top_ prefix prevents cross-entity confusion. |
slug | my-topic | URL/routing-safe form. Derived by Slug::sanitize(name): lowercase, non-[a-z0-9-] → -, consecutive - collapsed, leading/trailing - trimmed, clamped to 63 chars. |
RRL | rafka/prod/east/topics/my-topic | Rafka 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:
| Path | When it fires | Error code |
|---|---|---|
| AdminClient explicit create | AdminClient.create_topics(...) → api_key 19 | Success: 0. Duplicate: 36. |
| Producer auto-create | Metadata request with allow_auto_topic_creation=true (librdkafka producer default) | Registers with 1 partition; no error. |
| AdminClient describe | Metadata 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_PARTITIONSreplication_factor < 1→ 38 INVALID_REPLICATION_FACTORcleanup.policy=compactwithrafka.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_partitionsecho 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.enabledis 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 theconfigmap ofNewTopicif you need it enabled.cleanup.policycontainingcompactwhilerafka.failover.enabled=truereturns 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
| Code | Name | When emitted |
|---|---|---|
| 0 | — | Success |
| 3 | UNKNOWN_TOPIC_OR_PARTITION | Absent topic in Delete / DescribeConfigs / AlterConfigs / Metadata(flag=false) |
| 17 | INVALID_TOPIC_EXCEPTION | Name violates Kafka naming rules |
| 36 | TOPIC_ALREADY_EXISTS | CreateTopics duplicate name |
| 37 | INVALID_PARTITIONS | num_partitions < 1 |
| 38 | INVALID_REPLICATION_FACTOR | replication_factor < 1 |
| 40 | INVALID_CONFIG | Config combination conflict |