сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · FastAPI, Django и API

Идемпотентность и gRPC

Идемпотентность и gRPC

Два сюжета, где ломаются наивные API: ретраи, задваивающие эффект (оплата прошла дважды), и выбор протокола для межсервисного обмена. Собес проверяет, знаешь ли ты Idempotency-Key и понимаешь ли, что gRPC это трейдоф, а не «просто быстрее REST».

Типовые формулировки: «как защитить оплату от двойного списания при ретрае?», «когда gRPC, а когда REST?», «читается ли gRPC браузером».

Idempotency-Key против задвоения

Идемпотентная операция: повтор с теми же параметрами даёт тот же итог, что один вызов. Это критично для ретраев - они не задваивают эффект. POST по природе не идемпотентен: два POST /orders создадут два заказа, а сетевой ретрай оплаты реально приходит дважды.

Решение - Idempotency-Key: клиент шлёт уникальный ключ операции, сервер запоминает ключ вместе с результатом и на повтор с тем же ключом возвращает ПРЕЖНИЙ ответ, не выполняя эффект снова. Так одна оплата остаётся одной, сколько бы раз запрос ни повторился.

// Полагаться, что «POST не повторится», нельзя: ретраи сети, прокси и клиентов - обыденность, ключ обязателен для денежных операций.

POST /payments
Idempotency-Key: 7f3a-...   # повтор с тем же ключом ->
                            # прежний ответ, второго списания нет
идемпотентность
повтор операции не меняет итог
Idempotency-Key
ключ, по которому сервер не повторяет эффект POST

gRPC - трейдоф, не серебряная пуля

gRPC это RPC на protobuf поверх HTTP/2: бинарный формат, строгая .proto-схема, кодогенерация клиентов, стриминг из коробки. За счёт бинарности и HTTP/2 он быстрее JSON/REST, но не читается глазами и плохо работает напрямую из браузера.

Отсюда выбор по месту: gRPC уместнее во ВНУТРЕННЕМ межсервисном обмене, где важны скорость, строгая схема и стриминг. Наружу и в браузер берут REST/JSON - читаемо, кэшируемо, работает везде без прокси. Считать gRPC безусловно лучше REST - ошибка: это размен скорости на читаемость и совместимость.

// Для браузерного gRPC нужен gRPC-Web и прокси-прослойка - лишняя сложность, если наружу и так удобнее REST.

gRPC
RPC на protobuf поверх HTTP/2, бинарный и строго типизированный
protobuf
бинарный формат и схема контракта для gRPC

Как отвечать: «Как защитить оплату от двойного списания при ретрае?»

Через Idempotency-Key. POST не идемпотентен сам по себе, а сетевой ретрай оплаты реально приходит дважды, так что полагаться на «клиент не повторит» нельзя. Клиент генерирует уникальный ключ на операцию и шлёт его в заголовке. Сервер при первом запросе выполняет списание и сохраняет ключ вместе с результатом, а на любой повтор с тем же ключом возвращает уже сохранённый ответ, не списывая второй раз. Ключ храню с TTL, а саму запись результата делаю в одной транзакции со списанием, чтобы не было гонки между двумя параллельными ретраями.

Назван механизм, объяснено почему POST опасен, и добавлены продовые детали - транзакционность записи ключа и гонка параллельных ретраев; видно, что человек это реально строил.

На чём валят

  • Полагаться, что POST не повторится: ретраи сети реальны - для денег нужен Idempotency-Key.
  • Совать gRPC в публичный браузерный API: напрямую из браузера он неудобен, нужен gRPC-Web и прокси.
  • Считать gRPC безусловно лучше REST: это трейдоф скорости против читаемости и совместимости.
  • Писать эффект и сохранять ключ идемпотентности не атомарно - два параллельных ретрая проскочат оба.

Проверьте себя

Пять вопросов из банка по этой подтеме. Всего их 11, остальные разбираются в тренажёре.

  1. #idempotency_grpc1 / 5
    POST-оплата может прийти дважды из-за ретрая сети. Как не списать деньги повторно?
    A)Idempotency-Key: сервер запоминает ключ и не повторяет эффект
    B)Понадеяться, что сеть не повторит запрос дважды за короткий срок
    C)Обернуть списание в транзакцию — она сама отсечёт дубликат запроса
    D)Просто заменить POST на GET — его безопасно повторять
    показать ответ и разбор
    +A)Idempotency-Key: сервер запоминает ключ и не повторяет эффект

    // разбор: Клиент шлёт уникальный Idempotency-Key в заголовке; сервер сохраняет его с результатом первой обработки. Пришёл повтор с тем же ключом — сервер возвращает прежний ответ, не выполняя списание снова. Так POST-эффект становится безопасным для ретраев, чего сам метод не гарантирует.

  2. #idempotency_grpc2 / 5
    Что такое gRPC в двух словах?
    A)Текстовый REST-протокол с обменом JSON поверх обычного HTTP/1
    B)Библиотека очередей сообщений для асинхронной связи сервисов
    C)Формат базы данных для хранения удалённых вызовов процедур
    D)RPC на protobuf поверх HTTP/2 — бинарный контракт
    показать ответ и разбор
    +D)RPC на protobuf поверх HTTP/2 — бинарный контракт

    // разбор: gRPC — фреймворк удалённого вызова процедур: контракт описывают в .proto (Protocol Buffers), обмен идёт бинарно поверх HTTP/2 с поддержкой стриминга. Компактнее и быстрее JSON/REST, строгая типизация и кодогенерация клиентов. Цена — не читается глазами и хуже дружит с браузером напрямую.

  3. #idempotency_grpc3 / 5
    Когда gRPC уместнее REST/JSON?
    A)Публичный API для сторонних разработчиков и браузеров напрямую
    B)gRPC стоит брать для нового сервиса — он современнее и быстрее REST
    C)Только для статических сайтов, где важна отдача HTML-страниц
    D)Внутренний обмен сервисов: нужна скорость, схема, стриминг
    показать ответ и разбор
    +D)Внутренний обмен сервисов: нужна скорость, схема, стриминг

    // разбор: gRPC силён во внутреннем межсервисном обмене: низкая задержка, компактный бинарный формат, строгая схема с кодогенерацией, двунаправленный стриминг. Для публичных API и браузеров чаще берут REST/JSON — читаемо, кэшируемо, работает везде без прокси. Выбор — контекст, а не «что моднее».

  4. #idempotency_grpc4 / 5
    Что значит, что операция идемпотентна?
    A)Повтор с теми же данными даёт тот же итог, не дублируя эффект
    B)Операция выполняется настолько быстро, что её повтор практически незаметен
    C)Операция возвращает одинаковый ответ независимо от входных данных
    D)Операцию не получится повторить дважды за одну пользовательскую сессию
    показать ответ и разбор
    +A)Повтор с теми же данными даёт тот же итог, не дублируя эффект

    // разбор: Идемпотентность: многократное выполнение с одними и теми же данными приводит к тому же состоянию, что и однократное. GET, PUT, DELETE идемпотентны по семантике; POST — нет (два POST создадут две сущности). Свойство критично при ненадёжной сети: клиент может безопасно повторить запрос после таймаута.

  5. #idempotency_grpc5 / 5
    Оплата идёт через POST, клиент из-за таймаута повторяет запрос. Как не списать дважды?
    A)Полагаться на то, что второй запрос просто не дойдёт до сервера из-за сети
    B)Запретить клиенту повторы запросов после первой отправки платежа
    C)Ничего не делать: HTTP сам не даст одному POST выполниться дважды
    D)Idempotency-Key: сервер по ключу распознаёт повтор и возвращает прежний результат
    показать ответ и разбор
    +D)Idempotency-Key: сервер по ключу распознаёт повтор и возвращает прежний результат

    // разбор: POST не идемпотентен, а сеть допускает повторы после таймаутов — есть риск двойного списания. Клиент прикладывает уникальный Idempotency-Key к запросу; сервер запоминает ключ с результатом первой обработки и на повтор с тем же ключом возвращает тот же ответ, не выполняя операцию заново. Так небезопасный по природе POST делают безопасным для ретраев.

дальше

Теорию прочитали. Навык ставится повторением

В Сеньорчике эта подтема идёт в ежедневных сессиях: движок возвращает её, пока ответы не станут уверенными, и ведёт прогресс отдельно по каждой подтеме. Теория внутри тоже бесплатна, лимит только на количество вопросов в день.