сеньорчикОткрыть в Telegram
← вся теориятеория к собесу · Архитектура

Транзакции и согласованность

Транзакции и согласованность

На складе одна последняя коробка. Два человека нажали «купить» с разницей в тридцать миллисекунд. Оба запроса прочитали остаток - единица, оба решили, что товар есть, оба записали ноль. Продано две коробки, в наличии одна. Дальше кто-то из двоих получает письмо с извинениями, а поддержка - обращение.

Стержень: транзакция делает изменения неделимыми внутри одной базы. Между сервисами такой гарантии нет, там выбирают между строгостью и доступностью осознанно.

// Формулировки: «где нужна строгая согласованность?», «два оператора правят одну заявку», «заказ в одном сервисе, резерв в другом»

Что обязано случиться вместе

Списали деньги, создали заказ. Между этими двумя записями упал сервер. Без транзакции остаётся состояние «деньги ушли, заказа нет», и разбирать его будет человек в поддержке, по одному обращению за раз.

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

// Для аналитика отсюда следует конкретный вопрос к бизнесу: перечисли изменения, которые обязаны произойти вместе. Ответ определяет и границы транзакции, и то, можно ли вообще разносить эти операции по разным сервисам - потому что общей транзакции между ними не будет.

Где отставание стоит денег, а где нет

Отложенная согласованность означает, что копии догоняют источник не мгновенно. Реплика базы отстаёт на десятки или сотни миллисекунд, поисковый индекс - на секунды, витрина отчётов - на часы. Это нормальный режим работы, а не поломка.

Считаем цену отставания. Счётчик просмотров отстал на минуту - никто не заметил. Остаток товара отстал на секунду - продали то, чего нет, и платишь возвратом, доставкой и репутацией. Разница не в технологии, а в том, принимается ли по этим данным решение о деньгах.

// Жалоба, которая приходит первой: «поменял телефон в профиле, обновил страницу - старый». Запись ушла в основной узел, а чтение балансировщик отправил в отставшую реплику. Лечится чтением своих записей с основного узла в течение короткого окна после изменения.

Два оператора и одна заявка

Аня открыла заявку в 10:00 и ушла на созвон. Борис открыл её же в 10:05, поправил сумму, сохранил в 10:07. Аня вернулась, дописала комментарий и сохранила в 10:12 - той формой, которую загрузила в 10:00. Сумма Бориса стёрта, оба уверены, что всё сохранилось. Это потерянное обновление.

Три рабочих правила. Блокировка на время редактирования: просто, но заявка виснет, если человек ушёл на обед. Проверка версии при сохранении: форма помнит номер версии, полученный при открытии, и если в базе версия уже другая - сохранение отклоняют и показывают конфликт. Слияние по полям: когда правки затронули разные поля и обе можно применить.

// Выбор здесь не технический. Решает бизнес, что дороже - заблокированная на час заявка или тихо потерянная правка. Аналитик обязан этот вопрос задать: иначе правило выберет разработчик молча, и это будет «побеждает сохранивший последним».

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

Между сервисами - сага

Единой транзакции через границу сервисов нет. Двухфазная фиксация формально существует, но держит блокировки на всех участниках и подвисает целиком, если координатор упал в середине. В живых системах её обходят стороной.

Вместо неё берут сагу: цепочку шагов, у каждого своя компенсация. Создали заказ, зарезервировали товар, списали деньги. Списание не прошло - снимаем резерв, отменяем заказ. Компенсация не откат, а обратное действие, которое видно пользователю и попадает в историю.

// Работа аналитика здесь не в схеме, а в двух списках: компенсация для каждого шага и статус, который человек видит между шагами. Без второго списка пользователь смотрит на «заказ создан» три минуты и звонит в поддержку.

сага
цепочка локальных транзакций в разных сервисах, где у каждого шага описано компенсирующее действие

Как отвечать: «Заказ создаётся в одном сервисе, резерв товара в другом. Как согласовать?»

Сагой с компенсациями, а не распределённой транзакцией: двухфазная фиксация через границы сервисов держит блокировки на всех участниках и виснет, если координатор умер посередине. Расписываю последовательность: создали заказ, попытались зарезервировать товар, не получилось - отменяем заказ либо переводим его в ожидание поставки. Главная часть моей работы тут не схема, а компенсация для каждого шага и промежуточные статусы, которые увидит пользователь. Без них система застревает между шагами: заказ висит в неопределённом состоянии, пока кто-то не разберёт руками. И отдельно проговариваю случай, когда компенсация невозможна - деньги уже ушли поставщику, тогда нужен ручной сценарий с ответственным.

Кандидат отвергает распределённую транзакцию с обоснованием, переводит разговор в свою зону и вспоминает про необратимые шаги.

На чём валятся

  • − Пишут «данные всегда актуальны во всех системах» - требование невыполнимо физически.
  • − Не задают правило разрешения конкурентной правки, и правки исчезают молча.
  • − Тянут распределённую транзакцию между сервисами вместо саги.
  • − Забывают, что чтение из реплики вернёт состояние до только что сделанной записи.
  • − Описывают шаги саги без компенсаций и без промежуточных статусов.
  • − Не предусматривают шаг, который компенсировать нельзя.

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

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

  1. #ana_arc_consistency1 / 5
    Где отложенная согласованность обычно неприемлема?
    A)В проверке остатка перед списанием
    B)В счётчике просмотров страницы
    C)В ленте новостей приложения
    D)В списке рекомендованных товаров
    показать ответ и разбор
    +A)В проверке остатка перед списанием

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

  2. #ana_arc_consistency2 / 5
    Два оператора одновременно редактируют одну заявку. Что должен описать аналитик?
    A)Размер формы редактирования
    B)Порядок полей на экране
    C)Поведение при конфликте изменений
    D)Частоту автосохранения черновика
    показать ответ и разбор
    +C)Поведение при конфликте изменений

    // разбор: Без явного правила побеждает тот, кто сохранил последним, и правки первого исчезают молча. Варианты решения известны: блокировка на время редактирования, проверка версии записи с сообщением о конфликте, слияние по полям. Выбор зависит от того, насколько часто такие пересечения случаются.

  3. #ana_arc_consistency3 / 5
    Что означает оптимистичная блокировка?
    A)Запись блокируется на всё время правки
    B)Конфликт обнаруживается при сохранении по версии
    C)Изменения применяются без всяких проверок
    D)Правки разных пользователей сливаются автоматически
    показать ответ и разбор
    +B)Конфликт обнаруживается при сохранении по версии

    // разбор: Оптимистичный подход исходит из того, что пересечения редки: запись не блокируют, но при сохранении сверяют версию, которую клиент получил при открытии. Если она устарела, сохранение отклоняется и пользователю показывают конфликт. Так не страдают те, кто открыл форму и ушёл на обед.

  4. #ana_arc_consistency4 / 5
    Заказ создаётся в одном сервисе, резерв товара — в другом. Как обеспечить согласованность?
    A)Общей транзакцией через две базы
    B)Сверкой раз в сутки по расписанию
    C)Блокировкой обоих сервисов на время операции
    D)Последовательностью шагов с компенсациями
    показать ответ и разбор
    +D)Последовательностью шагов с компенсациями

    // разбор: Единая транзакция через границы сервисов дорога и хрупка, поэтому применяют сагу: создали заказ, зарезервировали товар, при неудаче второго шага отменяем первый. Бизнес обязан описать компенсацию для каждого шага и промежуточные статусы, которые увидит пользователь.

  5. #ana_arc_consistency5 / 5
    Почему требование «данные всегда актуальны во всех системах» практически невыполнимо?
    A)Так запрещают стандарты интеграции
    B)Базы не умеют реплицировать данные
    C)Передача не мгновенна, узлы отказывают
    D)Системы используют разные форматы данных
    показать ответ и разбор
    +C)Передача не мгновенна, узлы отказывают

    // разбор: Между системами всегда есть задержка, а при отказе сети приходится выбирать: отказать в обслуживании либо продолжить работу с расхождением. Поэтому требование формулируют иначе — задают допустимое отставание и поведение при его превышении: показать предупреждение, запретить операцию, поставить в очередь.

дальше

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

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