> Была ли важна атомарность изменений в хранилище и отправки сообщений (Go)

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

Компании: BetBoom

Стек: Go

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

Да, атомарность изменений в хранилище и отправки сообщений критически важна для обеспечения консистентности данных в распределённых системах. Без неё возможны ситуации, когда данные записаны, но сообщение не отправлено (или наоборот), что приводит к рассинхронизации между сервисами. В Go для этого применяются несколько подходов:

  1. Transactional Outbox - запись события в ту же БД, что и бизнес-данные, в рамках одной транзакции. Отдельный процесс (например, фоновая горутина) читает outbox и отправляет сообщения, гарантируя at-least-once delivery.

  2. Двухфазный коммит (2PC) - координация между хранилищем и брокером, но это добавляет сложность и снижает производительность.

  3. Идемпотентность обработчиков - если сообщение всё же будет отправлено повторно, получатель должен безопасно его обработать.

Пример реализации outbox на Go:

GO
type Order struct {
ID string
Items []string
}
type OutboxMessage struct {
ID string
Topic string
Payload []byte
CreatedAt time.Time
}
func CreateOrder(ctx context.Context, db *sql.DB, producer *kafka.Producer, order Order) error {
tx, err := db.BeginTx(ctx, nil)
if err != nil {
return err
}
defer tx.Rollback()
// 1. Сохраняем заказ
if _, err := tx.ExecContext(ctx, "INSERT INTO orders (id, items) VALUES ($1, $2)", order.ID, order.Items); err != nil {
return err
}
// 2. Сохраняем сообщение в outbox
msg := OutboxMessage{
ID: uuid.New().String(),
Topic: "order_created",
Payload: mustMarshal(order),
}
if _, err := tx.ExecContext(ctx, "INSERT INTO outbox (id, topic, payload, created_at) VALUES ($1, $2, $3, $4)",
msg.ID, msg.Topic, msg.Payload, time.Now()); err != nil {
return err
}
// 3. Коммитим транзакцию
if err := tx.Commit(); err != nil {
return err
}
// 4. Отправляем сообщение (уже после коммита)
// В случае ошибки - повторит фоновый процесс
return producer.SendMessage(ctx, msg.Topic, msg.Payload)
}

Фоновый процесс (например, запущенный в отдельной горутине) периодически проверяет outbox и повторно отправляет неотправленные сообщения, обеспечивая атомарность в конечном счёте.

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

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