> Какие гарантии доставки сообщений предоставляет Google Pub/Sub? (Go)

Уровень: senior · Роль: backend · Язык: Go · Категория: Технические вопросы

Компании: InDrive

Стек: Go

> Пример ответа

Короткий ответ

Google Pub/Sub предоставляет as least-once delivery - минимум одну доставку сообщения. Это означает, что сообщение может быть доставлено более одного раза, но не потеряно при корректной обработке acknowledgements. Гарантия обеспечивается за счёт хранения сообщений в хранилище, привязанном к подписке, и повторной отправки до получения явного подтверждения (ack) от подписчика. Exactly-once доступен только для ограниченных сценариев через exactly-once delivery для подписок с определёнными настройками.

Подробное объяснение

Pub/Sub моделирует доставку как минимум один раз (at-least-once). Это фундаментальное свойство распределённой системы, где сетевые сбои и ретраи неизбежны. Ключевые механизмы:

  • Сообщение хранится в подписке до тех пор, пока не будет подтверждено (acked) или не истечёт срок хранения (по умолчанию 7 дней, настраивается до 10 дней).
  • Подписчик должен явно вызвать ack после успешной обработки. Если ack не получен в течение срока подтверждения (ack deadline, по умолчанию 10 секунд, максимум 600 секунд), сообщение становится доступным для повторной доставки.
  • Повторная доставка происходит с экспоненциальным backoff, но без гарантии порядка. Порядок гарантируется только при использовании message ordering (ordering keys), и даже тогда - только в пределах ключа.
  • Exactly-once delivery доступен как опция для подписок (начиная с 2021 года). Он гарантирует, что сообщение не будет доставлено повторно после успешного ack, но требует, чтобы подписчик использовал специальный клиент и обрабатывал дубликаты на своей стороне (например, через deduplication ID).

Важный нюанс: даже с exactly-once, Pub/Sub не гарантирует транзакционность между приёмом сообщения и записью в вашу базу данных. Это означает, что идемпотентность обработчика - ваша ответственность.

На практике

Для Go-разработчика это означает:

  • Всегда обрабатывайте сообщения идемпотентно - повторная доставка не должна ломать бизнес-логику.
  • Используйте modAck (modify ack deadline) для продления времени обработки, если задача занимает больше 10 секунд.
  • Настраивайте MaxOutstandingMessages и MaxOutstandingBytes в клиенте, чтобы контролировать буферизацию.
  • Для критичных сценариев включайте exactly-once delivery на подписке и используйте deduplication в вашей базе данных.
  • Помните, что Pub/Sub не гарантирует порядок сообщений без ordering keys, и даже с ними - только в пределах ключа и только если подписчик обрабатывает сообщения последовательно.

Пример кода

GO
package main
import (
"context"
"fmt"
"time"
"cloud.google.com/go/pubsub"
)
func main() {
ctx := context.Background()
client, err := pubsub.NewClient(ctx, "your-project-id")
if err != nil {
panic(err)
}
defer client.Close()
sub := client.Subscription("your-subscription")
sub.ReceiveSettings.MaxOutstandingMessages = 10
sub.ReceiveSettings.MaxOutstandingBytes = 10 << 20 // 10 MB
err = sub.Receive(ctx, func(ctx context.Context, msg *pubsub.Message) {
// Идемпотентная обработка: проверяем, обработано ли уже сообщение
if isProcessed(msg.ID) {
msg.Ack()
return
}
// Продлеваем ack deadline для долгих задач
msg.ModifyAckDeadline(30)
if err := processMessage(ctx, msg); err != nil {
// Не вызываем Nack - просто даём истечь deadline, сообщение будет переотправлено
fmt.Printf("processing failed: %v\n", err)
return
}
markProcessed(msg.ID)
msg.Ack()
})
if err != nil {
panic(err)
}
}
func isProcessed(id string) bool {
// реализация: проверка в Redis или БД
return false
}
func markProcessed(id string) {
// реализация: запись в Redis или БД
}
func processMessage(ctx context.Context, msg *pubsub.Message) error {
// бизнес-логика
return nil
}

Как отвечать на собеседовании

Начните с чёткого утверждения: at-least-once, затем объясните механизм ack и retry. Упомяните exactly-once как опцию, но подчеркните, что она не снимает ответственность за идемпотентность. Покажите понимание trade-off: почему система не может дать строго exactly-once в распределённой среде без дополнительных затрат. Приведите пример из практики, где дубликаты реально возникали и как вы с ними справлялись. Если спросят про порядок - скажите, что по умолчанию его нет, и что ordering keys решают только частично.

Что проверяет интервьюер

  • Понимание модели доставки и её ограничений.
  • Умение проектировать идемпотентные обработчики.
  • Знание клиентских настроек (ack deadline, flow control).
  • Осознание разницы между гарантиями Pub/Sub и гарантиями вашего приложения.
  • Способность объяснить, почему exactly-once в распределённых системах - сложная задача.

Типичные ошибки

  • Утверждение, что Pub/Sub даёт exactly-once по умолчанию.
  • Игнорирование необходимости идемпотентности - "мы полагаемся на гарантии брокера".
  • Непонимание, что Nack и истечение deadline - разные вещи: Nack немедленно делает сообщение доступным для ретрая, а deadline - только после таймаута.
  • Забывают про ModifyAckDeadline для долгих операций.
  • Путают порядок сообщений с гарантией доставки - это разные вещи.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью