# Publishing (/connectors/gcp-pub-sub/how-to/publishing)



This guide covers the publish surface end to end: topic lifecycle, a single publish, batch publish
(≤ 1000 messages), the **atomic batch-validation** rule, ordering keys, and message attributes.
Every topic is a native KubeMQ **Events Store** log `gcp.{topic}` (see
[Channel mapping](/connectors/gcp-pub-sub/reference/channel-mapping)).

## Topic lifecycle [#topic-lifecycle]

The `Publisher` surface ships **9 RPCs** (see
[Capabilities](/connectors/gcp-pub-sub/reference/capabilities)):

* `CreateTopic` — validates the name; `kms_key_name` is accepted-and-ignored; ingestion configs are
  **rejected** (`INVALID_ARGUMENT`); requested retention is clamped to the broker ceiling.
* `GetTopic` — returns the **requested** (un-clamped) retention.
* `ListTopics` — opaque page token.
* `UpdateTopic` — a `FieldMask` over `labels`, `message_retention_duration`, `schema_settings`.
* `DeleteTopic` — a **tombstone**: the record is retained so existing subscriptions survive, and
  re-creating the topic reuses the same log.
* `ListTopicSubscriptions`, `ListTopicSnapshots`, `DetachSubscription`, and `Publish` (below).

<Callout type="warn">
  **Topic ids may not start with `sub.`.** That prefix is the reserved broker namespace for
  subscription queues (`gcp.sub.{s}`). Resource ids must be 3..255 chars, start with a letter, use
  the charset `[A-Za-z0-9._~%+-]`, and carry no `goog` prefix. See
  [Limits & rules](/connectors/gcp-pub-sub/reference/limits-and-rules).
</Callout>

## A single publish [#a-single-publish]

`Publish` returns a server-assigned **message id**. The connector writes the message **once** to
the Events Store log `gcp.{topic}` — the authoritative, cross-protocol, replayable copy and the
source for `Seek` — then fans out one queue copy per subscription, applying each subscription's
filter:

1. The SDK sends a `PubsubMessage { data, attributes, ordering_key }`.
2. The connector assigns a **message id** and a **publish time** and returns the id.
3. The message lands in `gcp.{topic}` via the Events Store send, then is fanned out to each
   subscription's queue `gcp.sub.{s}`.

A filtered-out message is never enqueued for that subscription (it is effectively auto-acked);
detached subscriptions are skipped.

<Callout type="info">
  **A publish writes once to the topic log, then fans out per subscription.** The single write to
  `gcp.{topic}` is the source of truth; the per-subscription copies on `gcp.sub.{s}` are derived from
  it. A native KubeMQ consumer of `gcp.{topic}` therefore sees every published message regardless of
  which subscriptions exist. See
  [Architecture](/connectors/gcp-pub-sub/concepts/architecture).
</Callout>

## Batch publish [#batch-publish]

`Publish` accepts a **batch of 1..1000** messages. Server-assigned ids are returned **in request
order**, so a client can correlate each id with its input message.

### Atomic batch validation [#atomic-batch-validation]

<Callout type="warn">
  **The whole batch is validated before anything is enqueued.** If any message fails validation, the
  **entire batch** is rejected with `INVALID_ARGUMENT` and **nothing is published** — there is no
  partial publish.
</Callout>

Per-message validation rules:

| Rule               | Limit                                        |
| ------------------ | -------------------------------------------- |
| Batch size         | 1..1000 messages                             |
| Total message size | ≤ 10 MiB                                     |
| Attributes         | ≤ 100 per message                            |
| Attribute key      | ≤ 256 B, no `goog` prefix                    |
| Attribute value    | ≤ 1024 B                                     |
| Ordering key       | ≤ 1024 B                                     |
| Body               | `data` **or** `attributes` must be non-empty |

If the topic has a **schema** (see
[Schema validation](/connectors/gcp-pub-sub/how-to/schema-validation)), every message is also
validated against it and the whole batch is rejected on the first non-conforming message. The full
limit table is in [Limits & rules](/connectors/gcp-pub-sub/reference/limits-and-rules).

A batch publish that prints its server-assigned ids in order:

```python
from google.cloud import pubsub_v1

publisher = pubsub_v1.PublisherClient()      # honours PUBSUB_EMULATOR_HOST
topic_path = publisher.topic_path("my-project", "events")

futures = [publisher.publish(topic_path, f"event-{i}".encode()) for i in range(5)]
for fut in futures:                          # ids returned in request order
    print(fut.result())
```

## Ordering keys [#ordering-keys]

Set a per-message `ordering_key` and **enable ordering on the subscription**
(`enable_message_ordering`). Messages sharing an `ordering_key` are then delivered in **publish
order**, with **at most one in flight per key** — the head of a key blocks until it is acked or
redelivered, and redelivery is in order. A round-robin cursor spreads delivery fairly across
contended keys; keyless messages are delivered unordered.

<Callout type="info">
  **Ordering is opt-in on the subscriber side.** Publishing with an `ordering_key` is necessary but
  not sufficient — the **subscription** must set `enable_message_ordering` for ordered delivery. The
  ordering key travels as the reserved tag `_pubsub_ordering_key`. See
  [Ordered delivery](/connectors/gcp-pub-sub/how-to/ordered-delivery).
</Callout>

## Message attributes [#message-attributes]

A `PubsubMessage`'s `attributes` map (string → string) round-trips as KubeMQ message **tags**. On
top of the user attributes the connector carries **three reserved tags** across the wire:

* `_pubsub_message_id` — the server-assigned id;
* `_pubsub_publish_time` — the publish timestamp;
* `_pubsub_ordering_key` — the ordering key (if any).

<Callout type="info">
  **Reserved tags are visible to native consumers, hidden from Pub/Sub clients.** A native KubeMQ
  consumer of `gcp.{topic}` sees all three reserved tags plus the user attributes; when the connector
  delivers the message back to a Pub/Sub client, the reserved tags are **stripped** from
  `attributes`. See [Channel mapping](/connectors/gcp-pub-sub/reference/channel-mapping).
</Callout>

Attribute constraints (enforced in the atomic validation above): ≤ 100 attributes; key ≤ 256 B with
no `goog` prefix; value ≤ 1024 B.

## Error quick reference [#error-quick-reference]

| Trigger                                                                                | Result                                                                        |
| -------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- |
| Batch > 1000 messages, or any message > 10 MiB / > 100 attributes / oversize key/value | `INVALID_ARGUMENT` — **whole batch** rejected                                 |
| `data` and `attributes` both empty                                                     | `INVALID_ARGUMENT`                                                            |
| Topic id starts with `sub.`, bad charset, or `goog` prefix                             | `INVALID_ARGUMENT` on `CreateTopic`                                           |
| Ingestion config on `CreateTopic`                                                      | `INVALID_ARGUMENT`                                                            |
| Message fails the topic's schema                                                       | `INVALID_ARGUMENT` — **whole batch** rejected on first non-conforming message |

## Related [#related]

<Cards>
  <Card title="Subscribing" href="/connectors/gcp-pub-sub/how-to/subscribing" description="Pull vs StreamingPull, the ack-deadline lease, flow control, and exactly-once delivery." />

  <Card title="Message filtering" href="/connectors/gcp-pub-sub/how-to/filtering" description="The CEL-subset attribute filter applied at publish fan-out, and the fan-out pattern it enables." />

  <Card title="Limits & rules" href="/connectors/gcp-pub-sub/reference/limits-and-rules" description="The full numeric-limit and validation-rule table — batch size, message size, attributes, and more." />
</Cards>
