Go: Повторный requestid возвращает прежнюю бронь — задача с проверкой

Практическая задача на Go по теме «Повторный request_id возвращает прежнюю бронь». Условие содержит контракт функций, предоставленные типы и требования к результату. Используйте выданный шаблон и сохраняйте его публичные объявления. Перед проверкой разберите успешный путь, обработку ошибок и отмену операции, когда она предусмотрена контрактом. Здесь оценивается конкретный компонент сервиса; в полном курсе микросервисов на Go эти компоненты связываются с HTTP, PostgreSQL, gRPC и Kafka.

Повторный request_id возвращает прежнюю бронь

Реализуйте функцию IdempotentBook, которая обеспечивает идемпотентное бронирование места на показ.

Дополните TODO в редакторе и отправьте весь код, сохранив package main, импорты, предоставленные типы и публичную функцию RunScenario. Не добавляйте свою функцию main: JSON-адаптер запуска платформа добавит сама. Скрытые входы и ожидаемые результаты находятся на стороне проверки.

Входные данные уже проверены и нормализованы; числовые поля не требуют дополнительной проверки или преобразования. Задача посвящена решениям над предоставленным интерфейсом хранилища. SQL, подключение к базе данных, сеть и внешние модули не нужны.

Предоставленное хранилище само управляет транзакцией: успешный Book возвращает сохранённую бронь после завершённого commit, а ErrSeatUnavailable и ErrRequestIDConflict — только после завершённого успешного rollback. Каждый вызов ByRequestID — независимое чтение текущего состояния вне предыдущей попытки; найденная запись относится точно к переданному RequestID.


type Booking struct {
    ID         int64
    ShowID     int64
    SeatNo     int32
    CustomerID int64
    RequestID  string
}

type BookingStore interface {
    ByRequestID(context.Context, string) (Booking, error)
    Book(context.Context, Booking) (Booking, error)
}

type Result struct {
    Status  string
    Booking Booking
}

// Сентинели: ErrNotFound, ErrSeatUnavailable, ErrRequestIDConflict, ErrConflict

func IdempotentBook(ctx context.Context, repo BookingStore, in Booking) Result

Требуемое поведение:

Для сравнения запроса и сохранённой брони значимы только ShowID, SeatNo, CustomerID. Предложенный ID не влияет на равенство; RequestID служит точным ключом поиска.

Наблюдаемый результат Статус и данные
Первичный поиск нашёл бронь с равными значимыми полями replayed, найденная бронь целиком; создание не требуется
Первичный поиск нашёл бронь с отличающимися значимыми полями conflict; создание не требуется
Первичный поиск завершился любой ошибкой, кроме ErrNotFound storage_error; создание не требуется
Первичный поиск подтвердил отсутствие ключа (ErrNotFound), а единственная попытка Book успешна created, бронь целиком из результата Book; дополнительного поиска нет
Book завершился ErrSeatUnavailable или ErrRequestIDConflict, а новое чтение нашло бронь с равными значимыми полями replayed, бронь целиком из нового чтения
После любой из этих двух ошибок новое чтение нашло бронь с отличающимися значимыми полями conflict
После ErrSeatUnavailable новое чтение подтвердило отсутствие ключа (ErrNotFound) seat_unavailable
После ErrRequestIDConflict новое чтение подтвердило отсутствие ключа (ErrNotFound) conflict
После любой из этих двух ошибок новое чтение завершилось иной ошибкой storage_error
Book завершился ErrConflict conflict, без дополнительного поиска
Book завершился любой иной ошибкой, включая ошибку commit storage_error, без дополнительного поиска

Правила обращений и результата:

  • Первичный ByRequestID обязателен ровно один раз. Единственный Book допустим только при подтверждённом отсутствии ключа. Новое чтение обязательно ровно один раз только после ErrSeatUnavailable или ErrRequestIDConflict от Book.
  • Во все обращения передавайте исходный ctx, даже если он уже отменён, и неизменённый in.RequestID; в Book передавайте исходный in целиком. Самостоятельная проверка отмены контекста не должна заменять обязательное обращение к хранилищу.
  • Ошибки распознавайте через errors.Is, включая обёрнутые ошибки. Одинаковый текст без совпадения по errors.Is не означает совпадение с сентинелем.
  • При ненулевой ошибке игнорируйте частично возвращённый Booking.
  • Booking заполняется только для created и replayed, причём авторитетна вся запись, возвращённая хранилищем. Для conflict, seat_unavailable и storage_error поле Booking имеет нулевое значение.
  • На любом пути допустимы не более одного Book и двух ByRequestID. Циклы и повторные попытки создания не допускаются.

Подсказки

  • Идемпотентный повтор относится к сохранённой брони: новый предложенный ID не меняет личность уже выполненного запроса.
  • Отсутствие ключа при новом чтении означает, что прежняя бронь для этого запроса не найдена. Значение этого результата зависит от причины неудачной попытки создания.
РешениеGo

Без регистрации. Гостевая проверка не сохраняет прогресс. Для сохранения перейдите к уроку.

Отправьте решение, чтобы увидеть результаты тестов.

Связанные задачи