KubeMQ
ConnectorsGoogle Cloud Pub/SubReference

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 resourceKubeMQ objectChannel
Topic projects/{p}/topics/{t}Events Store loggcp.{t}
Subscription projects/{p}/subscriptions/{s}Queuegcp.sub.{s}
Snapshot / SchemaRegistry 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 topicKubeMQ channel
ordersgcp.orders
eventsgcp.events
audit-loggcp.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 subscriptionKubeMQ channel
orders-subgcp.sub.orders-sub
analyticsgcp.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 channel

enc 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).

SubscriptionOrdering keyKubeMQ channel
orders-subtenant-agcp.sub.orders-sub.k.tenant-a
orders-suborder/42gcp.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:

  1. Writes once to the topic log gcp.{t} via Array.SendEventsStore — the authoritative, cross-protocol, replayable copy and the source for Seek.
  2. 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 tagCarriesSet by
_pubsub_message_idthe server-assigned message idconnector on Publish
_pubsub_publish_timethe publish timestampconnector on Publish
_pubsub_ordering_keythe 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 fieldKubeMQ TagNotes
message attribute {Name}Tag {Name}round-trips losslessly; ≤ 100 attrs, key ≤ 256 B (no goog), value ≤ 1024 B
datamessage 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.)

Was this page helpful?

On this page