blob: 711aa0b526c04e818fdf2c6c706ce1ee7ca47c04 [file] [edit]
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/*
Package pubsub provides an easy way to publish and receive Google Cloud Pub/Sub
messages, hiding the details of the underlying server RPCs.
Pub/Sub is a many-to-many, asynchronous messaging system that decouples senders
and receivers.
If you are migrating from the v1 library, please read over the [migration guide].
More information about Pub/Sub is available at the [product documentation page].
See the [main Google Cloud Go package] for authentication, timeouts,
connection pooling and similar aspects of this package.
# Publishing
Pub/Sub messages are published to topics via publishers.
A Topic may be created like so:
ctx := context.Background()
client, _ := pubsub.NewClient(ctx, "my-project")
topic, err := client.TopicAdminClient.CreateTopic(ctx, &pubsubpb.Topic{
Name: "projects/my-project/topics/my-topic",
})
A [Publisher] client can then be instantiated and used to publish messages.
publisher := client.Publisher(topic.GetName())
res := publisher.Publish(ctx, &pubsub.Message{Data: []byte("payload")})
[Publisher.Publish] queues the message for publishing and returns immediately. When enough
messages have accumulated, or enough time has elapsed, the batch of messages is
sent to the Pub/Sub service.
[Publisher.Publish] returns a [PublishResult], which behaves like a future: its Get method
blocks until the message has been sent to the service.
The first time you call [Publisher.Publish] on a [Publisher], goroutines are started in the
background. To clean up these goroutines, call [Publisher.Stop]:
publisher.Stop()
# Receiving
To receive messages published to a topic, clients create a subscription
for the topic. There may be more than one subscription per topic; each message
that is published to the topic will be delivered to all associated subscriptions.
You then need to create a [Subscriber] client to pull messages from a subscription.
A subscription may be created like so:
ctx := context.Background()
client, _ := pubsub.NewClient(ctx, "my-project")
subscription, err := client.SubscriptionAdminClient.CreateSubscription(ctx,
&pubsubpb.Subscription{
Name: "projects/my-project/subscriptions/my-sub",
Topic: "projects/my-project/topics/my-topic"}
),
}
A [Subscriber] client can be instantiated like so:
sub := client.Subscriber(subscription.GetName())
You then provide a callback to [Subscriber] which processes the messages.
err := sub.Receive(ctx, func(ctx context.Context, m *Message) {
log.Printf("Got message: %s", m.Data)
m.Ack()
})
if err != nil && !errors.Is(err, context.Canceled) {
// Handle error.
}
The callback is invoked concurrently by multiple goroutines, maximizing
throughput. To terminate a call to [Subscriber.Receive], cancel its context.
Once client code has processed the [Message], it must call Message.Ack or
Message.Nack. If Ack is not called, the Message will eventually be redelivered. Ack/Nack
MUST be called within the [Subscriber.Receive] handler function, and not from a goroutine.
Otherwise, flow control (e.g. ReceiveSettings.MaxOutstandingMessages) will
not be respected. Additionally, messages can get orphaned when Receive is canceled,
resulting in slow redelivery.
If the client cannot or does not want to process the message, it can call Message.Nack
to speed redelivery. For more information and configuration options, see
Ack Deadlines below.
Note: It is possible for a [Message] to be redelivered even if Message.Ack has
been called unless exactly once delivery is enabled. Applications should be aware
of these deliveries.
Note: This uses pubsub's streaming pull feature. This feature has properties that
may be surprising. Please refer to the [Streaming Pull API] for more details on
how streaming pull behaves.
# Emulator
To use an emulator with this library, you can set the PUBSUB_EMULATOR_HOST
environment variable to the address at which your emulator is running. This will
send requests to that address instead of to Pub/Sub. You can then create
and use a client as usual:
// Set PUBSUB_EMULATOR_HOST environment variable.
err := os.Setenv("PUBSUB_EMULATOR_HOST", "localhost:8085")
if err != nil {
// TODO: Handle error.
}
// Create client as usual.
client, err := pubsub.NewClient(ctx, "my-project-id")
if err != nil {
// TODO: Handle error.
}
defer client.Close()
# Ack Deadlines
The default ack deadlines are suitable for most use cases, but may be
overridden. This section describes the tradeoffs that should be considered
when overriding the defaults.
Behind the scenes, each message returned by the Pub/Sub server has an
associated lease, known as an "ack deadline". Unless a message is
acknowledged within the ack deadline, or the client requests that
the ack deadline be extended, the message will become eligible for redelivery.
As a convenience, the pubsub client will automatically extend deadlines until
either:
- Message.Ack or Message.Nack is called, or
- The "MaxExtension" duration elapses from the time the message is fetched from
the server. This defaults to 60m.
Ack deadlines are extended periodically by the client. The period between extensions,
as well as the length of the extension, automatically adjusts based on the time it takes the
subscriber application to ack messages (based on the 99th percentile of ack latency).
By default, this extension period is capped at 10m, but this limit can be configured
by the Min/MaxDurationPerAckExtension settings. This has the effect that subscribers that process
messages quickly have their message ack deadlines extended for a short amount, whereas
subscribers that process message slowly have their message ack deadlines extended
for a large amount. The net effect is fewer RPCs sent from the client library.
For example, consider a subscriber that takes 3 minutes to process each message.
Since the library has already recorded several 3-minute "ack latencies"s in a
percentile distribution, future message extensions are sent with a value of 3
minutes, every 3 minutes. Suppose the application crashes 5 seconds after the
library sends such an extension: the Pub/Sub server would wait the remaining
2m55s before re-sending the messages out to other subscribers.
Please note that the client library does not use the subscription's
AckDeadline for the MaxExtension value.
# Fine Tuning PubSub Receive Performance
This section describes how to adjust [ReceiveSettings] for best performance.
Subscriber.ReceiveSettings.MaxExtension
This is the maximum amount of time that the client will extend a message's deadline.
This value should be set to the maximum expected processing time, plus some
buffer. The higher the extension allowed, the longer it takes before the server considers
messages lost and re-sends them to some other, healthy instance of your application.
Subscriber.ReceiveSettings.MaxDurationPerAckExtension
This is the maximum amount of time to extend each message's deadline per
ModifyAckDeadline RPC. Normally, the deadline is determined by the 99th percentile
of previous message processing times. However, if normal processing time takes 10 minutes
but an error occurs while processing a message within 1 minute, a message will be
stuck and held by the client for the remaining 9 minutes. By setting the maximum amount
of time to extend a message's deadline on a per-RPC basis, you can decrease the amount
of time before message redelivery when errors occur. However, the downside is that more
ModifyAckDeadline RPCs will be sent.
Subscriber.ReceiveSettings.MinDurationPerAckExtension
This is the minimum amount of time to extend each message's deadline per
ModifyAckDeadline RPC. This is the complement setting of MaxDurationPerAckExtension and
represents the lower bound of modack deadlines to send. If processing time is very
low, it may be better to issue fewer ModifyAckDeadline RPCs rather than every
10 seconds. Setting both Min/MaxDurationPerAckExtension to the same value
effectively removes the automatic derivation of deadlines and fixes it to the value
you wish to extend your messages' deadlines by each time.
Subscriber.ReceiveSettings.MaxOutstandingMessages
This is the maximum number of messages that are to be processed by the callback
function at a time. Once this limit is reached, the client waits for messages
to be acked or nacked by the callback before requesting more messages from the server.
This value is set by default to a fairly conservatively low number. We strongly
encourage setting this number as high as memory allows, since a low setting will
artificially rate limit reception. Setting this value to -1 causes it to be unbounded.
Subscriber.ReceiveSettings.MaxOutstandingBytes
This is the maximum amount of bytes (message size) that are to be processed by
the callback function at a time. Once this limit is reached, the client waits
for messages to be acked or nacked by the callback before requesting more
messages from the server.
Note that there sometimes can be more bytes pulled and being processed than
MaxOutstandingBytes allows. This is due to the fact that the server
does not consider byte size when tracking server-side flow control.
For example, if the client sets MaxOutstandingBytes to 50 KiB, but receives
a batch of messages totaling 100 KiB, there will be a temporary overflow of
message byte size until messages are acked.
Similar to MaxOutstandingMessages, you can set this higher to maximize
processing throughput. Setting this value to -1 causes it to be unbounded.
Subscriber.ReceiveSettings.NumGoroutines
This is the number of goroutines spawned to receive messages from the Pubsub server.
Each goroutine opens a StreamingPull stream, so this also directly sets the number of
open StreamingPull streams.
According to the [Resource Limits] page, each stream can handle about 10 MB/s of messages.
Leaving this value as the default would be good for most use cases.
If increasing this value to be greater than 1, please set `EnablePerStreamFlowControl` to true.
This helps align the server-side flow control with what is available locally. For more
explanation of this issue, see [this issue].
Going above 100 streams can lead to poor behavior, such as acks/modacks not succeeding in a reasonable
amount of time and resulting in high message expiration rates. In these cases,
increase the number of subscriber client applications rather than increasing this value.
By default, the number of connections in the gRPC conn pool is min(4,GOMAXPROCS). Each connection supports
up to 100 streams. Thus, if you have 4 or more CPU cores, the default setting allows a maximum of 400 streams
which is already excessive for most use cases.
If you want to change the limits on the number of streams, you can change the number of connections
in the gRPC connection pool as shown below:
opts := []option.ClientOption{
option.WithGRPCConnectionPool(2),
}
client, err := pubsub.NewClient(ctx, projID, opts...)
This [medium post] describes tuning Pub/Sub performance in more detail.
# General tips
Each application should use a single PubSub client instead of creating many.
In addition, when publishing to a single topic, a publisher should be instantiated
once and reused to take advantage of flow control and batching capabilities.
[product documentation page]: https://cloud.google.com/pubsub/docs.
[Streaming Pull API]: https://docs.cloud.google.com/pubsub/docs/pull#streamingpull-api
[main Google Cloud Go package]: https://pkg.go.dev/cloud.google.com/go
[migration guide]: https://github.com/googleapis/google-cloud-go/blob/main/pubsub/MIGRATING.md
[medium post]: https://medium.com/google-cloud/pub-sub-flow-control-batching-9ba9a75bce3b
[this issue]: https://issuetracker.google.com/352592079
[Resource Limits]: https://docs.cloud.google.com/pubsub/quotas#resource_limits
*/
package pubsub // import "cloud.google.com/go/pubsub/v2"