Exactly-once delivery Stay organized with collections Save and categorize content based on your preferences.
This page explains how to receive and acknowledge messages usingPub/Sub's exactly-once feature, which makes it possible for youto track and prevent duplicate processing of messages. When the feature isenabled, Pub/Sub provides the following semantics:
Subscribers can determine if message acknowledgments were successful.
No redelivery occurs after the message is successfully acknowledged.
No redelivery occurs while a message is outstanding. A message is consideredoutstanding until the acknowledgment deadline expires or the message isacknowledged.
In case of multiple valid deliveries, due to acknowledgment deadlineexpiration or client-initiated negative acknowledgment, only the latestacknowledgment ID can be used to acknowledge the message. Any requests witha previous acknowledgment ID fail.
With exactly-once enabled, subscribers can ensure messages are processed onetime by following these guidelines:
Acknowledge messages within the acknowledgment deadline.
Maintain information about the progress of processing a message until it issuccessfully acknowledged.
Use the information about the progress of processing a message to preventduplicate work when an acknowledgment fails.
Only the pull subscription typesupports exactly-once delivery, including subscribers that use theStreamingPull API.Push and export subscriptionsdon't support exactly-once delivery.
Pub/Sub supports exactly-once delivery, within a cloud region,based on a Pub/Sub-defined uniquemessage ID.
Recommended client library versions
- For best performance, use the latest version of the client library, Pythonv2.13.6 or higher,Javav1.139.0 or higher,PHPv1.39.0 or higher,C#v3.2.0 or higher,C++v2.1.0,Gov1.25.1 or higher,Nodev3.2.0 or higher andRubyv2.12.1 or higher.
Redelivery versus duplicate
It is important to understand the difference between expected and unexpectedredeliveries.
A redelivery can happen either because of client-initiated negativeacknowledgment of a message or when the client doesn't extend the acknowledgmentdeadline of the message before the acknowledgment deadline expires. Redeliveriesare considered valid and system working as intended.
To troubleshoot redeliveries, seeDealing with duplicates.
A duplicate is when a message is re-sent after a successful acknowledgmentor before acknowledgment deadline expiration.
A redelivered message retains the same message ID between redelivery attempts.
Subscriptions with exactly-once delivery enabled don't receive duplicatedeliveries.
Exactly-once delivery support in client libraries
Supported client libraries have an interface for acknowledgmentwith response(example:Go).You can use this interface to check if the acknowledgment request succeeded.If the acknowledgment request succeeds, the clients are guaranteed tonot receive a re-delivery. If the acknowledgment request fails, theclients can expect a re-delivery.
Clients can also use the supported client libraries without theacknowledgment interface. However, in such cases,the acknowledgment failures can lead to silent re-deliveries of messages.
Supported client libraries have interfaces for setting the minimumlease extension time (example:Go).You must set the value for the minimum lease extension to a high numberto avoid any network-related acknowledgment expirations.The maximum value is set at 600 seconds.
If you are using the Java client library and you initialize your subscriberwith a custom gRPC Channel using the
setChannelProvider()method, it is recommended that you also setmaxInboundMetadataSizeto at least1MB when building yourTransportChannelProvider. For this configuration, youcan use theInstantiatingGrpcChannelProvider.Builder.setMaxInboundMetadataSize()or theManagedChannelBuilder.maxInboundMetadataSize()method.
The default values and range for the variables related to exactly-once deliveryand the names of the variables might differ across client libraries. Forexample, in the Java client library, the following variables controlexactly-once delivery.
| Variable | Description | Value |
|---|---|---|
setEnableExactlyOnceDelivery | Enables or disables exactly-once delivery. | true or false Default=false |
minDurationPerAckExtension | The minimum time in seconds to use for extending the modify acknowledgment deadline. | Range=0 to 600 Default=none |
maxDurationPerAckExtension | The maximum time in seconds to use for extending the modify acknowledgment deadline. | Range=0 to 600 Default=none |
In the case of exactly-once delivery, themodifyAckDeadline oracknowledgmentrequest to Pub/Sub fails when the acknowledgment ID is already expired. In suchcases, the service considers the expired acknowledgment ID as invalid, since anewer delivery might already be in-flight. This is by design for exactly-oncedelivery. You then seeacknowledgment andModifyAckDeadline requests return anINVALID_ARGUMENT response. When exactly-once delivery is disabled, theserequests returnOK in cases of expired acknowledgment IDs.
To ensure thatacknowledgment andModifyAckDeadline requests have validacknowledgment IDs, consider setting the value forminDurationPerAckExtension to a high number.
Regional considerations
The exactly-once delivery guarantee only applies when subscribers connect to theservice in the same region. If your subscriber application is spread acrossmultiple regions, it can lead to duplicate message delivery, even whenexactly-once delivery is enabled. Publishers can send messages to any region and theexactly once guarantee is still maintained.
When you run your application within Google Cloud, by default itconnects to the Pub/Sub endpoint in the same region. Therefore,running your application in a single region within Google Cloudgenerally ensures you are interacting with a single region.
When you are running your subscriber application outside of Google Cloudor in multiple regions, you can guarantee you are connecting to a single regionby using a locational endpoint when configuring your Pub/Subclient. All location endpoints for Pub/Sub point to singleregions. To learn more about locational endpoints,seePub/Sub endpoints.For a list of all locational endpoints for Pub/Sub,seeList of locational endpoints.
Create subscriptions with exactly-once delivery
You can create a subscription with exactly-once delivery using the Google Cloud console, the Google Cloud CLI, client library, or the Pub/Sub API.
Pull subscription
Console
To create a pull subscription with exactly-once delivery, follow these steps:
In the Google Cloud console, go to theSubscriptions page.
ClickCreate subscription.
Enter theSubscription ID.
Choose or create a topic from the drop-down menu.
The subscription receives messages from the topic.
In theExactly once delivery section, selectEnable exactly once delivery.
ClickCreate.
gcloud
To create a pull subscription with exactly-once delivery, use thegcloud pubsub subscriptions createcommand with the--enable-exactly-once-delivery flag:
gcloudpubsubsubscriptionscreateSUBSCRIPTION_ID\--topic=TOPIC_ID\--enable-exactly-once-delivery
Replace the following:
- SUBSCRIPTION_ID: the ID of the subscription to create
- TOPIC_ID: the ID of the topic to attach to the subscription
REST
To create a subscription with exactly-once delivery, use theprojects.subscriptions.createmethod.
PUT https://pubsub.googleapis.com/v1/projects/PROJECT_ID/subscriptions/SUBSCRIPTION_IDAuthorization: Bearer $(gcloud auth print-access-token)
Replace the following:
- PROJECT_ID: the project ID for the project to create thesubscription in
- SUBSCRIPTION_ID: the ID of the subscription to create
To create a pull subscription with exactly-once delivery, specify thisin the request body:
{"topic":"projects/PROJECT_ID/topics/TOPIC_ID","enableExactlyOnceDelivery":true,}
Replace the following:
- PROJECT_ID: the project ID for the project with the topic
- TOPIC_ID: the ID of the topic to attach to the subscription
C++
Before trying this sample, follow the C++ setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/Sub C++ API reference documentation.
namespacepubsub=::google::cloud::pubsub;namespacepubsub_admin=::google::cloud::pubsub_admin;[](pubsub_admin::SubscriptionAdminClientclient,std::stringconst&project_id,std::stringconst&topic_id,std::stringconst&subscription_id){google::pubsub::v1::Subscriptionrequest;request.set_name(pubsub::Subscription(project_id,subscription_id).FullName());request.set_topic(pubsub::Topic(project_id,topic_id).FullName());request.set_enable_exactly_once_delivery(true);autosub=client.CreateSubscription(request);if(sub.status().code()==google::cloud::StatusCode::kAlreadyExists){std::cout <<"The subscription already exists\n";return;}if(!sub)throwstd::move(sub).status();std::cout <<"The subscription was successfully created: " <<sub->DebugString() <<"\n";}C#
Before trying this sample, follow the C# setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/Sub C# API reference documentation.
usingGoogle.Cloud.PubSub.V1;usingGrpc.Core;publicclassCreateSubscriptionWithExactlyOnceDeliverySample{publicSubscriptionCreateSubscriptionWithExactlyOnceDelivery(stringprojectId,stringtopicId,stringsubscriptionId){SubscriberServiceApiClientsubscriber=SubscriberServiceApiClient.Create();TopicNametopicName=TopicName.FromProjectTopic(projectId,topicId);SubscriptionNamesubscriptionName=SubscriptionName.FromProjectSubscription(projectId,subscriptionId);varsubscriptionRequest=newSubscription{SubscriptionName=subscriptionName,TopicAsTopicName=topicName,EnableExactlyOnceDelivery=true};Subscriptionsubscription=null;try{subscription=subscriber.CreateSubscription(subscriptionRequest);}catch(RpcExceptione)when(e.Status.StatusCode==StatusCode.AlreadyExists){// Already exists. That's fine.}returnsubscription;}}Go
The following sample uses the major version of the Go Pub/Sub client library (v2). If you are still using the v1 library, seethe migration guide to v2.To see a list of v1 code samples, seethe deprecated code samples.
Before trying this sample, follow the Go setup instructions inQuickstart: Using Client Libraries.For more information, see thePub/Sub Go API reference documentation.
import("context""fmt""io""cloud.google.com/go/pubsub/v2""cloud.google.com/go/pubsub/v2/apiv1/pubsubpb")funccreateSubscriptionWithExactlyOnceDelivery(wio.Writer,projectID,topic,subscriptionstring)error{// projectID := "my-project-id"// topic := "projects/my-project-id/topics/my-topic"// subscription := "projects/my-project/subscriptions/my-sub"ctx:=context.Background()client,err:=pubsub.NewClient(ctx,projectID)iferr!=nil{returnfmt.Errorf("pubsub.NewClient: %w",err)}deferclient.Close()pbSub:=&pubsubpb.Subscription{Name:subscription,Topic:topic,EnableExactlyOnceDelivery:true,}sub,err:=client.SubscriptionAdminClient.CreateSubscription(ctx,pbSub)iferr!=nil{returnfmt.Errorf("failed to create exactly once sub: %w",err)}fmt.Fprintf(w,"Created a subscription with exactly once delivery enabled: %v\n",sub)returnnil}Java
Before trying this sample, follow the Java setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/Sub Java API reference documentation.
importcom.google.cloud.pubsub.v1.SubscriptionAdminClient;importcom.google.pubsub.v1.ProjectSubscriptionName;importcom.google.pubsub.v1.ProjectTopicName;importcom.google.pubsub.v1.Subscription;importjava.io.IOException;publicclassCreateSubscriptionWithExactlyOnceDelivery{publicstaticvoidmain(String...args)throwsException{// TODO(developer): Replace these variables before running the sample.StringprojectId="your-project-id";StringtopicId="your-topic-id";StringsubscriptionId="your-subscription-id";createSubscriptionWithExactlyOnceDeliveryExample(projectId,topicId,subscriptionId);}publicstaticvoidcreateSubscriptionWithExactlyOnceDeliveryExample(StringprojectId,StringtopicId,StringsubscriptionId)throwsIOException{try(SubscriptionAdminClientsubscriptionAdminClient=SubscriptionAdminClient.create()){ProjectTopicNametopicName=ProjectTopicName.of(projectId,topicId);ProjectSubscriptionNamesubscriptionName=ProjectSubscriptionName.of(projectId,subscriptionId);Subscriptionsubscription=subscriptionAdminClient.createSubscription(Subscription.newBuilder().setName(subscriptionName.toString()).setTopic(topicName.toString())// Enable exactly once delivery in the subscription..setEnableExactlyOnceDelivery(true).build());System.out.println("Created a subscription with exactly once delivery enabled: "+subscription.getAllFields());}}}Python
Before trying this sample, follow the Python setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/Sub Python API reference documentation.
fromgoogle.cloudimportpubsub_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)withsubscriber:subscription=subscriber.create_subscription(request={"name":subscription_path,"topic":topic_path,"enable_exactly_once_delivery":True,})print(f"Created subscription with exactly once delivery enabled:{subscription}")Node.js
Before trying this sample, follow the Node.js setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/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 libraryconst{PubSub}=require('@google-cloud/pubsub');// Creates a client; cache this for further useconstpubSubClient=newPubSub();asyncfunctioncreateSubscriptionWithExactlyOnceDelivery(topicNameOrId,subscriptionNameOrId,){// Creates a new subscriptionawaitpubSubClient.topic(topicNameOrId).createSubscription(subscriptionNameOrId,{enableExactlyOnceDelivery:true,});console.log(`Created subscription${subscriptionNameOrId} with exactly-once delivery.`,);console.log('To process messages, remember to check the return value of ackWithResponse().',);}Node.js
Before trying this sample, follow the Node.js setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/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 libraryimport{PubSub}from'@google-cloud/pubsub';// Creates a client; cache this for further useconstpubSubClient=newPubSub();asyncfunctioncreateSubscriptionWithExactlyOnceDelivery(topicNameOrId:string,subscriptionNameOrId:string,){// Creates a new subscriptionawaitpubSubClient.topic(topicNameOrId).createSubscription(subscriptionNameOrId,{enableExactlyOnceDelivery:true,});console.log(`Created subscription${subscriptionNameOrId} with exactly-once delivery.`,);console.log('To process messages, remember to check the return value of ackWithResponse().',);}Ruby
The following sample uses Ruby Pub/Sub client library v3. If you are still using the v2 library, see the migration guide to v3.To see a list of Ruby v2 code samples, seethe deprecated code samples.
Before trying this sample, follow the Ruby setup instructions inQuickstart: Using Client Libraries.For more information, see thePub/Sub Ruby API reference documentation.
# project_id = "your-project-id"# topic_id = "your-topic-id"# subscription_id = "your-subscription-id"pubsub=Google::Cloud::PubSub.newproject_id:project_idsubscription_admin=pubsub.subscription_adminsubscription=subscription_admin.create_subscription\name:pubsub.subscription_path(subscription_id),topic:pubsub.topic_path(topic_id),enable_exactly_once_delivery:trueputs"Created subscription with exactly once delivery enabled: "\"#{subscription_id}"PHP
Before trying this sample, follow the PHP setup instructions inQuickstart: Using Client Libraries. For more information, see thePub/Sub PHP API reference documentation.
use Google\Cloud\PubSub\PubSubClient;/** * Creates a Pub/Sub subscription with `Exactly Once Delivery` enabled. * * @param string $projectId The Google project ID. * @param string $topicName The Pub/Sub topic name. * @param string $subscriptionName The Pub/Sub subscription name. */function create_subscription_with_exactly_once_delivery( string $projectId, string $topicName, string $subscriptionName): void { $pubsub = new PubSubClient([ 'projectId' => $projectId, ]); $topic = $pubsub->topic($topicName); $subscription = $topic->subscription($subscriptionName); $subscription->create([ 'enableExactlyOnceDelivery' => true ]); // Exactly Once Delivery status for the subscription $status = $subscription->info()['enableExactlyOnceDelivery']; printf('Subscription created with exactly once delivery status: %s' . PHP_EOL, $status ? 'true' : 'false');}Monitor exactly-once delivery subscriptions
Thesubscription/exactly_once_warning_countmetric records the number of events thatcan lead to possible redeliveries (valid or duplicate). This metric counts thetimes Pub/Sub fails to process requests associated withacknowledgment IDs (ModifyAckDeadline oracknowledgment request). The reasonsfor the failure could be server or client-based. For example, if the persistencelayer used to maintain the exactly-once delivery information is unavailable, itwould be a server-based event. If the client tries to acknowledge a message withan invalid acknowledgment ID, it would be a client based event.
Understand the metric
subscription/exactly_once_warning_count captures events that might or might notlead to actual redeliveries and can be noisy based on client behavior. Forexample: repeatedacknowledgment orModifyAckDeadline requests with invalidacknowledgment IDs increment the metric repeatedly.
The following metrics are also useful in understanding the client behavior:
subscription/expired_ack_deadlines_countmetric shows the number of acknowledgment ID expirations. AcknowledgmentID expirations can lead to failures for bothModifyAckDeadlineandacknowledgmentrequests.service.serviceruntime.googleapis.com/api/request_countmetric can be used to capture failures ofModifyAckDeadlineoracknowledgmentrequests in cases where the requests reach Google Cloud but don'treach Pub/Sub. There are failures that this metric won'tcapture—for example, when clients are disconnected from Google Cloud.
In most cases of failure events that can be retried, supported client librariesretry the request automatically.
Quotas
Exactly-once delivery subscriptions are subjected to additional quotarequirements. These quota are enforced on:
- Number of messages consumed from subscriptions with exactly-once deliveryenabled per region.
- Number of messages acknowledged or whose deadline is extended when usingsubscriptions with exactly-once delivery enabled per region.
For more information regarding these quotas, see the table in theQuotas topic.
Exactly-once delivery and ordered subscriptions
Pub/Sub supports exactly-once delivery withordered delivery.
When using ordering with exactly-once delivery, Pub/Sub expectsthe acknowledgments to be in order. If the acknowledgments are out-of-order, theservice fails the requests with temporary errors. If the acknowledgment deadlineexpires before an in-order acknowledgment for the delivery, the client willreceive a redelivery of message. Due to this, when you use ordering withexactly-once delivery, the client throughput is limited to an order of thousandmessages per second.
Exactly-once delivery and push subscriptions
Pub/Sub supports exactly-once delivery only with pull subscriptions.
Clients consuming messages from the push subscriptions acknowledge the messagesby responding to the push requests with a successful response. However, clientsdon't know if the Pub/Sub subscription received the response andprocessed it. This is different from pull subscriptions, where acknowledgmentrequests are initiated by the clients and the Pub/Sub subscriptionresponds if the request was successfully processed. Because of this,exactly-once delivery semantics don't align well with push subscriptions.
Things to know
If acknowledgment deadline is not specified at CreateSubscription time,exactly-once delivery enabled subscriptions will have a default acknowledgmentdeadline of 60 seconds.
Longer default acknowledgment deadlines are beneficial in avoidingredelivery caused by network events. Supported client libraries don't use thedefault subscription acknowledgment deadline.
Exactly-once delivery subscriptions have significantly higherpublish-to-subscribe latency compared to regular subscriptions.
If you require high throughput, your exactly-once delivery clients must also usestreaming pull.
A subscription might receive multiple copies of the same message due to publish side duplicates, even with exactly-once delivery enabled. Publish-side duplicates can be due to multiple unique publish retries by thepublishing client or the Pub/Sub service. Multiple unique publishes by the publishing client, across retries, leads to redeliveries withdifferentmessage IDs. Multiple unique publishes by the Pub/Sub service, to respond to a client publish request, leads to redeliveries with thesamemessage IDs.
You can retry failures in
subscription/exactly_once_warning_countand the supported client libraries retries theseautomatically. However, failures related to invalid acknowledgment IDs cannotbe retried.
Except as otherwise noted, the content of this page is licensed under theCreative Commons Attribution 4.0 License, and code samples are licensed under theApache 2.0 License. For details, see theGoogle Developers Site Policies. Java is a registered trademark of Oracle and/or its affiliates.
Last updated 2026-02-19 UTC.