Запись в нескольких регионах: где на самом деле фиксируется изменение?
Маршрутизация доставляет запрос в регион; атомарной запись делает только граница транзакции. Назовите инвариант, выберите владельца, затем спроектируйте повтор вокруг него.
Два сервера приложений в двух регионах, один клиент, пишущий через VPN, и вопрос, который никто не записал: какой компонент решает, что запись состоялась? Маршрутизация доставляет запрос процессу. Граница транзакции решает, когда изменение становится долговечным и видимым. Это разные задачи, выполняемые разными системами, и разрыв между ними — то место, где живёт большинство ошибок записи в нескольких регионах.
Начните с бизнес-правила, а не с карты
Начните с фразы о бизнес-правиле, а не о географии: какие чтения и записи должны преуспеть вместе и какие одновременные изменения должны быть невозможны, пока это происходит. Для резервирования запасов это значит, что проверка остатка и резерв либо оба вступают в силу, либо ни один, и никакой второй заказ не может израсходовать ту же единицу между ними.
| Requirement | What satisfies it | What does not |
|---|---|---|
| All-or-nothing application | A transaction inside one owning system | A VPN path between regions |
| No double spend | Isolation strong enough to exclude the conflict | Atomicity alone |
| Survive a region loss | Replication the database itself documents | Two independent databases behind one client |
Именно эту среднюю строку команды и пропускают. Атомарность без достаточной изоляции позволяет двум транзакциям каждой подтвердиться корректно и всё же породить состояние, которое бизнес считает невозможным, и никакое количество логики повторов это потом не исправит.
Одна авторитетная граница побеждает два региональных сервера
Региональные серверы приложений могут оставаться на месте, если они передают операцию службе, выполняющей её внутри одной поддерживаемой границы транзакции. Уровень приложения становится маршрутизатором намерения; уровень данных владеет истиной.
Держите эту границу короткой и близко к данным, которых она касается. Транзакция, остающаяся открытой, пока человек отвечает на запрос или пока вызов пересекает два региона, тратит время на удержание блокировок и повышает шанс, что что-то другое истечёт за ней. Этот сбой выглядит как проблема базы данных и на самом деле является проблемой проектирования, надевшей такой костюм. Перенос решения на уровень данных обычно устраняет его полностью.
BEGIN;
UPDATE inventory SET reserved = reserved + 1
WHERE sku = 'A-100' AND available >= 1;
INSERT INTO reservation (sku, order_id) VALUES ('A-100', 'ord-9f');
COMMIT;
Читайте этот набросок как границу, а не как рекомендацию по производительности. Суть в том, что проверка и вставка делят одно подтверждение, поэтому одновременный заказ либо видит запас израсходованным, либо не видит его вовсе.
Что даёт и чего не даёт реплицированная база данных
Реплицированная база данных может предоставить более широкую границу, когда это поддерживает её собственная модель транзакций. Spanner документирует атомарные транзакции чтения-записи в пределах одной базы данных по таблицам, с определённой изоляцией и поведением повторов. Это гарантия, которую читают, проверяют и на которую намеренно опираются.
Это также гарантия, которая исходит от продукта, а не от маршрута. VPN между регионами меняет то, как путешествуют пакеты и как проявляются отказы. Он не расширяет границу транзакции ни на одну строку, как бы здраво ни выглядел туннель. Смешение двух вещей порождает архитектуры, где каждый компонент проходит свою проверку, а данные всё равно расходятся.
Две базы данных не становятся одной системой
Две независимо управляемые базы данных не приобретают общий менеджер транзакций из-за того, что клиенты могут дотянуться до обеих. Если бизнесу действительно нужна атомарная фиксация по этой паре, механизм координации, участники и процесс восстановления должны быть выбраны и аттестованы, а не приняты по умолчанию.
Подумайте о моменте, когда участник или связь исчезает после того, как работа подготовлена. Поддерживаемое распределённое подтверждение может оставить неопределённое состояние, требующее процедуры восстановления и человеческого решения, и оно не обещает, что каждый регион останется доступным для записи при каждом разделении.
Если вместо этого приемлема асинхронная координация, скажите это явно и запишите промежуточные бизнес-состояния и правила сверки. Цепочка локально подтверждённых шагов с компенсацией — законный проект; просто это не одна атомарная транзакция под другим именем.
Спроектируйте повтор до первого сбоя
Клиент может потерять ответ после того, как операция подтверждена, поэтому вызывающая сторона не может считать тайм-аут отказом. Сохраняйте устойчивую идентичность запроса и перед повтором изменения используйте результат службы или механизм идемпотентности, иначе повтор станет второй записью.
Автоматические обратные вызовы транзакций тоже могут выполняться более одного раза после повторяемого прерывания, что делает их плохим местом для необратимых внешних эффектов. Держите их вне обратного вызова, если только рабочий процесс не определяет для них явный безопасный протокол. Та же осторожность относится ко всему, что стоит денег или отправляет сообщение. Двойное списание — куда более тяжёлый разговор, чем двойная строка журнала.
Приёмка должна проиграть четыре сценария: два клиента соревнуются за одну строку, прерывание, которое служба называет повторяемым, подтверждение, ответ на которое никогда не приходит, и потеря региона или связи между ними. Запишите подтверждённый бизнес-результат и нерешённые состояния рядом с проектом маршрутизации, чтобы никто не принял сеть за компонент, дающий гарантию.