Order messages

Message ordering is a feature in Pub/Sub that lets you receive messages in your subscriber clients in the order that they were published by the publisher clients.

For example, assume that a publisher client in a region publishes messages 1, 2, and 3 in order. With message ordering, the subscriber client receives the published messages in the same order. To be delivered in order, the publisher client must publish the messages in the same region.

Message ordering is a useful feature for scenarios such as database change capture, user session tracking, and streaming applications where preserving the chronology of events is important.

This page explains the concept of message ordering and how to set up your subscriber clients to receive messages in order. To configure your publisher clients for message ordering, see Use ordering keys to publish a message.

Overview of message ordering

Ordering in Pub/Sub is determined by the following:

  • Ordering key: This is a string that is used in the Pub/Sub message metadata and represents the entity for which messages must be ordered. The ordering key can be up to 1 KB in length. To receive a set of ordered messages in a region, you must publish all messages with the same ordering key in the same region. Some examples of ordering keys are customer IDs and the primary key of a row in a database.

    The publish throughput on each ordering key is limited to 1 MBps. The throughput across all ordering keys on a topic is limited to the quota available in a publish region. This limit can be increased to many units of GBps.

    An ordering key is not equivalent to a partition in a partition-based messaging system as ordering keys are expected to have a much higher cardinality than partitions.

  • Enable message ordering: This is a subscription setting. When a subscription has message ordering enabled, the subscriber clients receive messages published in the same region with the same ordering key in the order in which they were received by the service. You must enable this setting in the subscription.

    Assume that you have two subscriptions A and B attached to the same topic T. Subscription A is configured with message ordering enabled and subscription B is configured without message ordering enabled. In this architecture, both subscriptions A and B receive the same set of messages from the topic T. If you publish messages with ordering keys in the same region, subscription A receives the messages in the order they were published. Whereas, subscription B receives the messages without any ordering expected.

In general, if your solution requires publisher clients to send both ordered and unordered messages, create separate topics, one for ordered messages and the other for unordered messages.

Considerations when using ordered messaging

The following list contains important information regarding the behavior of ordered messaging in Pub/Sub:

  • Within-key ordering: Messages published with the same ordering key are expected to be received in order. Assume that for ordering key A, you publish messages 1, 2, and 3. With ordering enabled, 1 is expected to be delivered before 2 and 2 is expected to be delivered before 3.

  • Across-key ordering: Messages published with different ordering keys are not expected to be received in order. Assume you have ordering keys A and B. For ordering key A, messages 1 and 2 are published in order. For ordering key B, messages 3 and 4 are published in order. However, message 1 could arrive before or after message 4.

  • Message redelivery: Pub/Sub delivers each message at least once, so the Pub/Sub service might redeliver messages. Redeliveries of a message trigger redelivery of all subsequent messages for that key, even acknowledged ones. Assume that a subscriber client receives messages 1, 2, and 3 for a specific ordering key. If message 2 is redelivered (because the acknowledgement deadline expired or the best-effort acknowledgement did not persist in Pub/Sub), then message 3 is also redelivered. If both message ordering and a dead-letter topic are enabled on a subscription, this behavior might not be true, as Pub/Sub forwards messages to dead-letter topics on a best-effort basis.

  • Acknowledgment delays and dead-letter topics: Unacknowledged messages for a given ordering key can potentially delay delivery of messages for other ordering keys, especially during server restarts or traffic changes. To maintain order across such events, ensure timely acknowledgment of all messages. If timely acknowledgment is not possible, consider using a dead-letter topic to prevent indefinite message holding. Be aware that order might not be preserved when messages are written to a dead-letter topic.

  • Message affinity (streamingPull clients): Messages for the same key are usually delivered to the same streamingPull subscriber client. Affinity is expected when messages are outstanding for an ordering key to a specific subscriber client. If there are no outstanding messages, affinity might shift for load balancing or client disconnects.

    To ensure smooth processing even with potential affinity changes, it's crucial to design your streamingPull application in a way that it can handle messages in any client for a given ordering key.

  • Integration with Dataflow: Don't enable message ordering for subscriptions when configuring Dataflow with Pub/Sub. Dataflow has its own mechanism for total message ordering, ensuring chronological order across all messages as part of windowing operations. This method of ordering differs from Pub/Sub's ordering key-based approach. Using ordering keys with Dataflow can potentially reduce pipeline performance.

  • Automatic scaling: Pub/Sub's ordered delivery scales to billions of ordering keys. A larger number of ordering keys allows more parallel delivery to subscribers since ordering applies to all the messages with the same ordering key.

Ordered delivery does come with some tradeoffs. Compared with unordered delivery, ordered delivery decreases publish availability and increases end-to-end message delivery latency. In the ordered delivery case, failover requires coordination to ensure the messages are written to and read in the correct order.

For more information about how to use message ordering, see the following best-practices topics:

Subscriber client behavior for message ordering

Subscriber clients receive messages in the order they were published in a specific region. Pub/Sub supports different ways of receiving messages, such as subscriber clients connected to pull and push subscriptions. The client libraries use streamingPull (with the exception of PHP).

To learn more about these subscription types, see Choose a subscription type.

The following sections discuss what receiving messages in order means for each type of subscriber client.

StreamingPull subscriber clients

When using the client libraries with streamingPull, you must specify a user callback that runs whenever a message is received by a subscriber client. With client libraries, for any given ordering key, the callback is run to completion on messages in the correct order. If the messages are acknowledged within that callback, all computations on a message occur in order. However, if the user callback schedules other asynchronous work on messages, the subscriber client must ensure that the asynchronous work is done in order. One option is to add messages to a local work queue that is processed in order.

Pull subscriber clients

For subscriber clients connected to pull subscriptions, Pub/Sub message ordering supports the following:

  • All messages for an ordering key in the PullResponse are in the proper order in the list.

  • Only one batch of messages can be outstanding for an ordering key at a time.

The requirement that only one batch of messages can be outstanding at a time is necessary to maintain ordered delivery since the Pub/Sub service can't ensure the success or latency of the response that it sends for a subscriber's pull request.

Push subscriber clients

The restrictions on push are even tighter than those on pull. For a push subscription, Pub/Sub supports only one outstanding message for each ordering key at a time. Each message is sent to a push endpoint as a separate request. Thus, sending the requests out in parallel would have the same issue as delivering multiple batches of messages for the same ordering key to pull subscribers simultaneously. Push subscriptions might not be a good choice for topics where messages are frequently published with the same ordering key or where latency is extremely important.

Export subscriber clients

Export subscriptions support ordered messages. For BigQuery subscriptions, messages with the same ordering key are written to their BigQuery table in order. For Cloud Storage subscriptions, messages with the same ordering key might not all be written to the same file. When within the same file, messages for an ordering key are in order. When spread across multiple files, later messages for an ordering key can appear in a file with a name that has an earlier timestamp than the timestamp in the name of the file with the earlier messages.

Enable message ordering

To receive the messages in order, set the message ordering property on the subscription you receive messages from. Receiving messages in order might increase latency. You can't change the message ordering property after you create a subscription.

You can set the message ordering property when you create a subscription using the Google Cloud console, the Google Cloud CLI, or the Pub/Sub API.

Console

To create a subscription with the message ordering property, follow these steps:

  1. In the Google Cloud console, go to the Subscriptions page.

Go to Subscriptions

  1. Click Create subscription.

  2. Enter a Subscription ID.

  3. Choose a topic from which you want to receive messages.

  4. In the Message ordering section, select Order messages with an ordering key.

  5. Click Create.

gcloud

To create a subscription with the message ordering property, use the gcloud pubsub subscriptions create command and the --enable-message-ordering flag:

gcloud pubsub subscriptions create SUBSCRIPTION_ID \
  --enable-message-ordering

Replace SUBSCRIPTION_ID with the ID of the subscription.

If the request is successful, the command line displays a confirmation:

Created subscription [SUBSCRIPTION_ID].

REST

To create a subscription with the message ordering property, send a PUT request like the following:

PUT https://pubsub.googleapis.com/v1/projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID
Authorization: Bearer $(gcloud auth application-default print-access-token)

Replace the following:

  • PROJECT_ID: the project ID of the project with the topic
  • SUBSCRIPTION_ID: the ID of the subscription

In the request body, specify the following:

{
  "topic": TOPIC_ID,
  "enableMessageOrdering": true,
}

Replace TOPIC_ID with the ID of the topic to attach to the subscription.

If the request is successful, the response is the subscription in JSON format:

{
  "name": projects/PROJECT_ID/subscriptions/SUBSCRIPTION_ID,
  "topic": projects/PROJECT_ID/topics/TOPIC_ID,
  "enableMessageOrdering": true,
}

C++

Before trying this sample, follow the C++ setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub C++ API reference documentation.

namespace pubsub = ::google::cloud::pubsub;
namespace pubsub_admin = ::google::cloud::pubsub_admin;
[](pubsub_admin::SubscriptionAdminClient client,
   std::string const& project_id, std::string const& topic_id,
   std::string const& subscription_id) {
  google::pubsub::v1::Subscription request;
  request.set_name(
      pubsub::Subscription(project_id, subscription_id).FullName());
  request.set_topic(pubsub::Topic(project_id, topic_id).FullName());
  request.set_enable_message_ordering(true);
  auto sub = client.CreateSubscription(request);
  if (sub.status().code() == google::cloud::StatusCode::kAlreadyExists) {
    std::cout << "The subscription already exists\n";
    return;
  }
  if (!sub) throw std::move(sub).status();

  std::cout << "The subscription was successfully created: "
            << sub->DebugString() << "\n";
}

C#

Before trying this sample, follow the C# setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub C# API reference documentation.


using Google.Cloud.PubSub.V1;
using Grpc.Core;

public class CreateSubscriptionWithOrderingSample
{
    public Subscription CreateSubscriptionWithOrdering(string projectId, string topicId, string subscriptionId)
    {
        SubscriberServiceApiClient subscriber = SubscriberServiceApiClient.Create();
        var topicName = TopicName.FromProjectTopic(projectId, topicId);
        var subscriptionName = SubscriptionName.FromProjectSubscription(projectId, subscriptionId);

        var subscriptionRequest = new Subscription
        {
            SubscriptionName = subscriptionName,
            TopicAsTopicName = topicName,
            EnableMessageOrdering = true
        };

        Subscription subscription = null;
        try
        {
            subscription = subscriber.CreateSubscription(subscriptionRequest);
        }
        catch (RpcException e) when (e.Status.StatusCode == StatusCode.AlreadyExists)
        {
            // Already exists.  That's fine.
        }
        return subscription;
    }
}

Go

Before trying this sample, follow the Go setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub Go API reference documentation.

import (
	"context"
	"fmt"
	"io"
	"time"

	"cloud.google.com/go/pubsub"
)

func createWithOrdering(w io.Writer, projectID, subID string, topic *pubsub.Topic) error {
	// projectID := "my-project-id"
	// subID := "my-sub"
	// topic of type https://godoc.org/cloud.google.com/go/pubsub#Topic
	ctx := context.Background()
	client, err := pubsub.NewClient(ctx, projectID)
	if err != nil {
		return fmt.Errorf("pubsub.NewClient: %w", err)
	}
	defer client.Close()

	// Message ordering can only be set when creating a subscription.
	sub, err := client.CreateSubscription(ctx, subID, pubsub.SubscriptionConfig{
		Topic:                 topic,
		AckDeadline:           20 * time.Second,
		EnableMessageOrdering: true,
	})
	if err != nil {
		return fmt.Errorf("CreateSubscription: %w", err)
	}
	fmt.Fprintf(w, "Created subscription: %v\n", sub)
	return nil
}

Java

Before trying this sample, follow the Java setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub Java API reference documentation.

import com.google.cloud.pubsub.v1.SubscriptionAdminClient;
import com.google.pubsub.v1.ProjectSubscriptionName;
import com.google.pubsub.v1.ProjectTopicName;
import com.google.pubsub.v1.Subscription;
import java.io.IOException;

public class CreateSubscriptionWithOrdering {
  public static void main(String... args) throws Exception {
    // TODO(developer): Replace these variables before running the sample.
    String projectId = "your-project-id";
    String topicId = "your-topic-id";
    String subscriptionId = "your-subscription-id";

    createSubscriptionWithOrderingExample(projectId, topicId, subscriptionId);
  }

  public static void createSubscriptionWithOrderingExample(
      String projectId, String topicId, String subscriptionId) throws IOException {
    try (SubscriptionAdminClient subscriptionAdminClient = SubscriptionAdminClient.create()) {

      ProjectTopicName topicName = ProjectTopicName.of(projectId, topicId);
      ProjectSubscriptionName subscriptionName =
          ProjectSubscriptionName.of(projectId, subscriptionId);

      Subscription subscription =
          subscriptionAdminClient.createSubscription(
              Subscription.newBuilder()
                  .setName(subscriptionName.toString())
                  .setTopic(topicName.toString())
                  // Set message ordering to true for ordered messages in the subscription.
                  .setEnableMessageOrdering(true)
                  .build());

      System.out.println("Created a subscription with ordering: " + subscription.getAllFields());
    }
  }
}

Node.js

Before trying this sample, follow the Node.js setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub Node.js API reference documentation.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';

// Imports the Google Cloud client library
const {PubSub} = require('@google-cloud/pubsub');

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function createSubscriptionWithOrdering(
  topicNameOrId,
  subscriptionNameOrId
) {
  // Creates a new subscription
  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, {
      enableMessageOrdering: true,
    });
  console.log(
    `Created subscription ${subscriptionNameOrId} with ordering enabled.`
  );
  console.log(
    'To process messages in order, remember to add an ordering key to your messages.'
  );
}

Node.js

Before trying this sample, follow the Node.js setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub Node.js API reference documentation.

/**
 * TODO(developer): Uncomment these variables before running the sample.
 */
// const topicNameOrId = 'YOUR_TOPIC_NAME_OR_ID';
// const subscriptionNameOrId = 'YOUR_SUBSCRIPTION_NAME_OR_ID';

// Imports the Google Cloud client library
import {PubSub} from '@google-cloud/pubsub';

// Creates a client; cache this for further use
const pubSubClient = new PubSub();

async function createSubscriptionWithOrdering(
  topicNameOrId: string,
  subscriptionNameOrId: string
) {
  // Creates a new subscription
  await pubSubClient
    .topic(topicNameOrId)
    .createSubscription(subscriptionNameOrId, {
      enableMessageOrdering: true,
    });
  console.log(
    `Created subscription ${subscriptionNameOrId} with ordering enabled.`
  );
  console.log(
    'To process messages in order, remember to add an ordering key to your messages.'
  );
}

Python

Before trying this sample, follow the Python setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub Python API reference documentation.

from google.cloud import pubsub_v1

# TODO(developer): Choose an existing topic.
# project_id = "your-project-id"
# topic_id = "your-topic-id"
# subscription_id = "your-subscription-id"

publisher = pubsub_v1.PublisherClient()
subscriber = pubsub_v1.SubscriberClient()
topic_path = publisher.topic_path(project_id, topic_id)
subscription_path = subscriber.subscription_path(project_id, subscription_id)

with subscriber:
    subscription = subscriber.create_subscription(
        request={
            "name": subscription_path,
            "topic": topic_path,
            "enable_message_ordering": True,
        }
    )
    print(f"Created subscription with ordering: {subscription}")

Ruby

Before trying this sample, follow the Ruby setup instructions in Quickstart: Using Client Libraries. For more information, see the Pub/Sub Ruby API reference documentation.

# topic_id        = "your-topic-id"
# subscription_id = "your-subscription-id"

pubsub = Google::Cloud::Pubsub.new

topic        = pubsub.topic topic_id
subscription = topic.subscribe subscription_id,
                               message_ordering: true

puts "Pull subscription #{subscription_id} created with message ordering."

What's next