Channel Mapping
How Pub/Sub topics and subscriptions map to KubeMQ — the gcp.{topic} Events Store log, the gcp.sub.{subscription} Queue grammar, reserved tags, and project.
This is the master reference for how the embedded KubeMQ Pub/Sub connector maps Pub/Sub topics and
subscriptions onto KubeMQ. A topic is backed by exactly one KubeMQ Events Store log; each
subscription is backed by its own KubeMQ Queue channel. This deterministic mapping is the
contract that makes cross-protocol interop work — a Pub/Sub Publish and a native KubeMQ consume
meet on the same channel.
Resource → channel table
| Pub/Sub resource | KubeMQ object | Channel |
|---|---|---|
Topic projects/{p}/topics/{t} | Events Store log | gcp.{t} |
Subscription projects/{p}/subscriptions/{s} | Queue | gcp.sub.{s} |
| Snapshot / Schema | Registry record | — (no channel) |
The {p} project segment is parsed and validated but ignored — the connector is single-tenant,
like the emulator, so resource ids are global across projects. Two clients using different
project ids but the same topic id share one gcp.{t} log.
Topic grammar
A topic maps to exactly one KubeMQ Events Store log:
gcp.{t}
└┬┘ └┬┘
│ └─ the topic id (the bare id you pass to CreateTopic — NOT the projects/.../topics/ path)
└─ fixed connector prefix ("gcp.")| Pub/Sub topic | KubeMQ channel |
|---|---|
orders | gcp.orders |
events | gcp.events |
audit-log | gcp.audit-log |
Topic ids may not start with sub. (gotcha #7). sub. is the reserved namespace for the
subscription queues below, so a topic id beginning sub. is rejected at create (INVALID_ARGUMENT).
See Limits & Rules for the full id grammar.
Subscription grammar
Each subscription maps to its own KubeMQ Queue channel:
gcp.sub.{s}
└──┬───┘ └┬┘
│ └─ the subscription id (bare, not the projects/.../subscriptions/ path)
└─ fixed connector prefix + reserved sub-namespace ("gcp.sub.")| Pub/Sub subscription | KubeMQ channel |
|---|---|
orders-sub | gcp.sub.orders-sub |
analytics | gcp.sub.analytics |
A subscription id may not contain the reserved .k. or .h. infixes — those are owned by the
per-key channel grammar below.
Per-ordering-key channels
When a subscription enables message ordering, the connector routes each ordering key onto its own
queue channel so the broker preserves per-key order natively. The base gcp.sub.{s} channel
continues to serve keyless messages:
gcp.sub.{s}.k.{enc(key)}
└───┬────┘ └┬┘ └───┬────┘
│ │ └─ the ordering key, percent-encoded
│ └─ fixed ".k." per-key separator
└─ the subscription's base queue channelenc percent-encodes any byte outside [a-zA-Z0-9_-]. If the encoded channel name would exceed
its length budget, the connector falls back to a deterministic sha256 form
gcp.sub.{s}.h.{hash} — the same key always hashes to the same channel on every node, so per-key
order is still preserved (the fallback only couples multiple long keys onto one FIFO channel).
| Subscription | Ordering key | KubeMQ channel |
|---|---|---|
orders-sub | tenant-a | gcp.sub.orders-sub.k.tenant-a |
orders-sub | order/42 | gcp.sub.orders-sub.k.order%2F42 |
orders-sub | (very long key) | gcp.sub.orders-sub.h.{sha256hex} |
Write-once-then-fan-out
A Publish is not copied to every subscription on the wire. The connector:
- Writes once to the topic log
gcp.{t}viaArray.SendEventsStore— the authoritative, cross-protocol, replayable copy and the source forSeek. - Fans out one queue copy per subscription via
Array.SendQueueMessage(gcp.sub.{s}), applying each subscription's filter — a filtered-out message is never enqueued (≈ auto-acked). Detached subscriptions are skipped.
So the topic log holds the complete history; each subscription queue holds the filtered slice that subscription still owes its consumers.
Reserved tags
A PubsubMessage becomes a KubeMQ message whose body is data and whose Tags are the message
attributes plus three reserved tags carried across the wire:
| Reserved tag | Carries | Set by |
|---|---|---|
_pubsub_message_id | the server-assigned message id | connector on Publish |
_pubsub_publish_time | the publish timestamp | connector on Publish |
_pubsub_ordering_key | the ordering key (only if one was set) | connector on Publish |
Native consumers see these tags; Pub/Sub clients do not. When the connector delivers a message
back to a Pub/Sub client it strips the three _pubsub_* tags out of attributes and surfaces them
as the native fields (messageId, publishTime, orderingKey). A native KubeMQ consumer reading
gcp.{t} reads them as ordinary Tags — this is how a cross-protocol consumer recovers the message
id and publish time.
Attribute ⇄ tag round-trip
| Pub/Sub field | KubeMQ Tag | Notes |
|---|---|---|
message attribute {Name} | Tag {Name} | round-trips losslessly; ≤ 100 attrs, key ≤ 256 B (no goog), value ≤ 1024 B |
data | message body | ≤ 10 MiB total per message |
| ordering key | _pubsub_ordering_key (reserved) | ≤ 1024 B |
| (server-assigned) message id | _pubsub_message_id (reserved) | |
| (server-assigned) publish time | _pubsub_publish_time (reserved) |
Attribute keys must not start with goog (Google's reserved prefix); the connector enforces this at
publish (INVALID_ARGUMENT). See Limits & Rules.
Cross-protocol interoperability
Because the topic log is a normal KubeMQ Events Store channel, a Pub/Sub Publish to topic orders
is consumable by a native gRPC/REST Events Store subscriber on channel gcp.orders, and a
subscription's backlog is a native Queue channel gcp.sub.{s}.
Deterministic read. Subscribe to the Events Store log with start policy startAt = "new"
before issuing the Pub/Sub publish, so the published message is guaranteed in-window for the
native consumer (no startup race). See
Cross-Protocol Interop.
The registry is authoritative
Topics, subscriptions, snapshots, and schemas live in a per-node replicated registry
(synchronized across cluster nodes with a last-writer-wins rule). Only resources created through the
Pub/Sub API are visible to the Pub/Sub surface — a Pull from a subscription that was never
CreateSubscriptiond returns NOT_FOUND. (A native client can still read the raw gcp.{t} log
directly regardless of the registry.)
Related
Architecture
The gRPC listener, the fan-out model, and the write-once-then-fan-out service map.
Capabilities
The 38 supported RPCs and the documented gotchas behind this mapping.
Limits & Rules
The resource-id grammar and attribute limits this channel mapping enforces.
Error Codes
The gRPC status codes the channel and attribute mapping can raise.
Was this page helpful?
Capabilities
What the KubeMQ Pub/Sub connector supports — the 38 v1 RPCs across Publisher, Subscriber, SchemaService, and IAMPolicy stubs, and the operations it rejects.
Configuration reference
The thirteen CONNECTORS_GCP_* environment variables for the KubeMQ Pub/Sub connector, with defaults and TOML/env/Docker examples.