> Какие гарантии доставки сообщений предоставляет 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, и даже с ними - только в пределах ключа и только если подписчик обрабатывает сообщения последовательно.
Пример кода
GOpackage mainimport ("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 = 10sub.ReceiveSettings.MaxOutstandingBytes = 10 << 20 // 10 MBerr = 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для долгих операций. - Путают порядок сообщений с гарантией доставки - это разные вещи.
> Похожие задачи по Go
Какой интерфейс реализовать для JSON маршаллинга и анмаршаллинга в Go и почему методы должны иметь ресивер по указателю
Почему вы сменили PHP на Go, что было мотиватором
Какой механизм используется для отправки событий?
Как отправляются события об изменении статусов?
> Похожие задачи по backend
Какие категории и типы данных существуют
Работаете ли вы с Python и Go
Какой механизм используется для отправки событий?
Как отправляются события об изменении статусов?
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью