Гай Харрисон, Эндрю Маршал, Чарльз Кастер

Архитектура распределённых

транзакционных приложений

Исходный текст, 2023 / Русский перевод, 2023


Оглавление

Глава 3
Распределение и масштабирование слоя хранения данных
К какой-то мере проблема распределения логики приложений — или в более общем смысле вычислительных сервисов — была решена несколько десятилетий назад. Даже в эпоху клиент-серверных систем, преобладающих в 1990-х годах, логика приложений обычно выполнялась на клиентском уровне в нескольких местах — фактически, на каждом клиентском ПК пользователя обычно запускалась одна копия логики приложений. У каждого ПК были свои вычислительные ресурсы и память, которые не зависели и не взаимодействовали с вычислительными ресурсами или памятью других пользователей.

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

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

В следующих разделах мы описываем характерные возможности современных распределённых транзакционных систем управления базами данных. К таким системам относятся: CockroachDB, Google Spanner, YugabyteDB, Microsoft Cosmos DB и другие.

Однако не все распределённые транзакционные базы данных реализуют все функции, описанные в этой главе, и этот отчёт не ставит перед собой цель сравнения наборов функций различных систем распределённых транзакционных баз данных. В большинстве случаев используется терминология и возможности, описанные в CockroachDB, с которыми авторы знакомы лучше всего. Другие базы данных могут использовать различную терминологию или обладать другими возможностями.
Транзакционные и нетранзакционные распределённые базы данных
В этом отчёте основное внимание уделяется транзакционным технологиям. Тем не менее, стоит кратко рассмотреть обоснование и использование нетранзакционных возможностей.
Теорема CAP, или Теорема Брьюера, утверждает, что в распределённой системе можно обладать не более чем двумя из трёх желательных характеристик:
Согласованность (Consistency)
Каждый пользователь видит одну и ту же версию состояния базы данных.
Доступность (Availability)
База данных остаётся доступной, пока не отказали все элементы распределённой системы.
Устойчивость к разделению (Partition tolerance)
Система может продолжать функционировать в случае разделения сети, при котором распределённая система распадается на две части или если два узла в сети не могут обмениваться информацией.
Давайте рассмотрим простой пример: у нас есть приложение с пользователями в США и Европе, и сеть между двумя континентами отказывает, нам необходимо выбрать между а) остановкой работы приложения на одном континенте или б) продолжением работы на обоих континентах, но с возникновением несогласованности между ними, так как локальные транзакции изменяют локальное представление базы данных.
В период начального внедрения первых глобальных интернет-приложений, разрыв сети были достаточно частым явлением, так что многие организации были готовы пожертвовать согласованностью ради обеспечения доступности. Однако сегодня ситуация иная. Разрыв сети всё еще возможен, но крупные облачные поставщики построили такую избыточность в своей сетевой инфраструктуре, что вероятность такого сбоя сети значительно снизилась. На Рис. 3-1 показан типичный набор сетевых связей между крупными регионами в общедоступном облаке.
Рисунок 3-1. Крупнейшие публичные облака имеют резервные сетевые соединения между каждым местоположением, что снижает вероятность возникновения сетевых разделов
С уменьшением возможности разделения сети изменился баланс преимуществ и недостатков между согласованностью и доступностью. Понимая это, Google разработал Spanner — распределённый и транзакционно согласованный сервис баз данных. С течением времени появились аналоги Spanner от сторонних разработчиков, такие как CockroachDB и YugabyteDB, и некоторые из них достигли такого уровня развития, что представляют собой готовое к промышленному применению кросс-платформенное решение для распределённых транзакционных приложений.
Стратегии хостинга для распределённых баз данных
У архитекторов приложений есть несколько вариантов развертывания слоя базы данных в современных распределённых транзакционных приложениях. Вкратце это следующие варианты:
Самостоятельное развёртывание на собственном оборудовании
В этом сценарии вы размещаете оборудование, на котором работают узлы распределённой базы данных, и развёртываете базу данных на этих узлах.
Самостоятельное развёртывание на облачной платформе
В этом сценарии вы используете облачные виртуальные машины для хостинга узлов распределённой базы данных и развёртываете базу данных на этих виртуальных машинах. Вы также можете выбрать этот подход для распределения баз данных по нескольким облачным платформам.
Полностью управляемый облачный сервис баз данных
Большинство поставщиков баз данных предлагают «полностью управляемую» (fully managed) облачную версию базы данных в виде сервиса. Вам не нужно заниматься развёртыванием или администрированием этих узлов, но вам всё же нужно определить относительное количество и мощность узлов.
Бессерверная облачная база данных
Бессерверные решения являются относительно новыми и предоставляют гибкую (elastic) производительность в зависимости от спроса. Количество вычислительных ресурсов, предоставляемых вашему приложению, автоматически настраивается, и вы платите только за использованные ресурсы.
Не все базы данных поддерживают все схемы развёртывания. Например, некоторые системы поддерживают только полностью управляемые развёртывания (например, Spanner). И хотя большинство поставщиков предлагают полностью управляемые облачные серверы, на момент написания этого текста очень немногие (например, CockroachDB) предлагают опцию бессерверного развёртывания.
Таблица 3-1 сравниваются некоторые преимущества и недостатки каждой схемы развёртывания.
Если ваш слой приложения работает в облаке, то вы почти наверняка выберете вариант развёртывания базы данных в облаке. Только если слой приложения работает на собственном оборудовании, вы можете рассмотреть вариант развёртывания базы данных на собственном оборудовании.
Существуют две основные причины, почему мы обычно выбираем «всё в облаке» или «всё на собственном оборудовании»:
Безопасность
Слой приложения обычно взаимодействует с базой данных в рамках частной сети. Если один компонент находится на собственном оборудовании, а другой в облаке, трафик базы данных должен переходить из одной сети в другую, что увеличивает уязвимость к кибератакам.
Задержка
Задержка между приложением и базой данных является одной из основных составляющих времени отклика конечного пользователя и общей пропускной способности. Обеспечение того, чтобы код приложения находился «близко» к узлу базы данных, минимизирует эту задержку. Такая близость невозможна, когда слой приложения и слой базы данных находятся на разных платформах облака или на разном оборудовании.
Во 2-ой главе и в других местах мы утверждали, что для большинства современных распределённых приложений решающими преимуществами являются возможности публичного облака. Если вы приняли этот аргумент, то скорее всего вы будете склонны к развёртыванию в облаке в какой-либо форме.
Из трёх вариантов облачного развёртывания баз данных — самостоятельное развертывание, полностью управляемая и бессерверная — мы считаем, что вариант с самостоятельным управлением обычно наименее привлекателен, особенно для небольшой команды. Оно связано с большими административными затратами и повышенным риском сбоев.
Бессерверное или выделенное серверное развёртывание?
Если вы решились на полностью управляемое развёртывание облачной базы данных, вам возможно также придется выбрать между выделенным серверным или бессерверным развёртыванием.
При выделенном серверном развёртывании вы выбираете количество и размер узлов базы данных. Эти узлы выделены для вашего кластера, находятся под вашим контролем и ваши счета в значительной степени будут определяться количеством и типами узлов, которые вы настроите.
В бессерверном развёртывании вам не нужно беспокоиться об этом. Вы просто регистрируетесь для получения учетной записи бессерверного сервиса — возможно, в определенном регионе — лимит ежемесячных расходов, которые вы готовы взять на себя. Ресурсы будут предоставляться к вашему сервису по мере необходимости вашей рабочей нагрузки, и вы будете плачитить только за используемые ресурсы.
Есть много преимуществ в бессерверном развертывании:
  • Вы платите только за используемые ресурсы, поэтому если ваше приложение имеет пики и спады активности, вы, вероятно, сэкономите деньги.

  • Ресурсы, применяемые к вашей рабочей нагрузке, будут динамически масштабироваться — по мере увеличения или уменьшения требований нагрузки, процессор и память будут автоматически настраиваться. В результате вам может не понадобиться выполнять предварительные тесты производительности или определять заранее требуемые аппаратные ресурсы для поддержки рабочей нагрузки приложения.

  • Обычно у вас есть месяц бесплатного периода на использование, поэтому вы можете разрабатывать бесплатно и затем плавно переходить к платному сервису, когда ваше приложение перейдёт в продакшн.
Эти преимущества весьма привлекательны для широкого круга применений. Однако есть некоторые ограничения:
  • При бессерверном развёртывании ваше приложение совместно использует некоторые физические ресурсы с другими пользователями бессерверного развёртывания. Конечно, вы не можете видеть данные других пользователей, но некоторые организации с особыми требованиями к безопасности могут считать такое соарендаторство неприемлемым.

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

  • В выделенном режиме стоимость и использование ресурсов относительно фиксированы, поэтому нет никакой непредсказуемости в выставлении счетов или в производительности. В бессерверном режиме вы «ограничиваете» свой счёт определенной суммой; если использование ресурсов превысит этот лимит, то ваша производительность будет ограничена до уровня, предоставленного бесплатным тарифом. Существуют, конечно, способы мониторинга и управления использованием ресурсов. Тем не менее, если вы не обращаете внимания на рабочую нагрузку вашего приложения, вы можете обнаружить, что потребляете ресурсы со скоростью, превышающей прогнозируемую и следовательно, ваши затраты превышают ожидания. При достижении лимита расходов и ограничении производительности, ресурсы, доступные для бессерверного кластера, будут уменьшены, что приведёт к увеличению задержек и снижению пропускной способности базы данных. Если ограничения ресурсов бесплатного тарифа недостаточны для поддержки рабочей нагрузки вашего приложения, вам, вероятно, потребуется увеличить лимит расходов. Бессерверные платформы предлагают пороговые значения и предупреждения, чтобы помочь вам отслеживать ваши расходы, но все же случайное увеличение рабочей нагрузки, возможно, даже из-за одного ошибочного SQL-запроса, может вызвать неожиданное увеличение стоимости или нежелательное снижение пропускной способности базы данных.

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

  • Не все поставщики баз данных предлагают вариант бессерверного развёртывания.
Для планирования бессерверного развёртывания вам необходимо установить верхний предел ежемесячных затрат, облачного провайдера и региона или регионов, в которых будет работать ваше бессерверное развёртывание.
Kubernetes
Мы подробно обсуждали Kubernetes во второй главе. Kubernetes практически является неотъемлемой частью современного приложения, обеспечивая преимущества в развёртывании, переносимости, управляемости и масштабируемости. Кроме того, это привлекательный фреймворк для запуска баз данных — большинство поставщиков баз данных используют Kubernetes в своих облачных развёртываниях.

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

Стандартные настройки Kubernetes редко подходят для развёртывания баз данных. Исторически Kubernetes использовался для приложений, а не для баз данных. Для приложений выделение ресурсов ЦП (центральный процессор) и памяти имело большее значение, чем управление вводом и выводом. Поэтому многие кластеры Kubernetes, особенно на облачных платформах, настроены с экономичными дисками «хранилище по гигабайтам». Например, при создании кластера Kubernetes на платформе Google Cloud, тип диска по умолчанию для пула узлов является стандартным HDD диском, в то время как постоянный диск SSD является более подходящим вариантом для развёртывания базы данных.

И последнее, как мы видели во второй главе, кластеры Kubernetes не могут легко охватывать несколько регионов. Если вам требуется развёртывание базы данных в нескольких регионах, Kubernetes может и не подойти.


Большинство поставщиков баз данных предлагают оператор Kubernetes, который формирует логику развёртывания и управления их базовым движком в кластере Kubernetes. Если вы развёртываете базу данных на Kubernetes, рекомендуется использовать эти операторы.
Политика размещения
Мы ранее обсуждали, что преимущества полностью управляемых или бессерверных облачных развёртываний для слоя баз данных являются убедительными. Полностью управляемое развёртывание снижает затраты на управление распрёделенной базой данных и обычно уменьшает операционные риски при сложном развёртывании в нескольких регионах. Однако есть несколько соображений, которые могут привести вас к самостоятельному управляемому развёртыванию в облаке.

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

Кроме того, у вас не будет полного контроля над размещением узлов базы данных. Большинство полностью управляемых вариантов позволяют определить регион, в котором будет существовать каждый узел, но обычно это не зона по умолчанию. Может быть сложно или невозможно обеспечить, чтобы узел приложения и узел базы данных находились в одной зоне (фактически, в пределах одного центра обработки данных).

В некоторых случаях вам может потребоваться контролировать размещение узлов даже в пределах центра обработки данных. Например, большинство облачных платформ позволяют использовать политики размещения, которые могут стимулировать расположение двух узлов физически близко друг к другу — например, в одном хранилище. Эта оптимизация недоступна при использовании полностью управляемой услуги.
Развёртывание межрегиональных баз данных
Мы обсуждали понятия зон и регионов в главе 2. Концепция регионов и зон характерна для основных облачных платформ и позволяет предоставлять услуги без единой точки отказа, как глобально, так и локально. Почти во всех случаях мы используем одинаковые определения регионов и зон для базы данных и слоя приложений. Любое несоответствие между регионами приложений и регионами базы данных приведёт к увеличению задержки и нарушению доступности в случае отказа региона.

Как и для слоя приложений, мы определяем регионы и зоны так, чтобы запросы с низкой задержкой могли быть обработаны в каждом регионе и приложение могло продолжать работу даже в случае сбоя вычислительных ресурсов в одной из зон.
Согласование в
распределённой
базе данных
Современные распределенные базы данных реализуют протокол распределённого согласования (consensus), который позволяет всем узлам, составляющим базу данных, достигать согласия о текущем состоянии любого элемента данных.
Чаще всего данные в базе данных будут разделены по диапазону первичных ключей или какому-либо другому механизму и распределены по узлам кластера. Обычно каждый раздел данных будет дублирован как минимум три раза. Большинство узлов должны всегда участвовать в любом изменении данных, чтобы в случае отказа одного узла была доступна копия самых последних данных. Для того чтобы база данных могла пережить несколько одновременных отказов, требуется большее количество копий.
Протокол Raft часто используется для координации процесса согласования. В Raft один из узлов выбирается «лидером» для определённого раздела данных.
Рисунок 3-2 иллюстрирует некоторые из этих принципов в типичной распределённой базе данных (в данном случае, CockroachDB).
Не обязательно полностью понимать внутренности распределённых баз данных. Но важно понять необходимость наличия нескольких копий и механизм согласования, так как это повлияет на нашу региональную и зонную концепцию.

Рисунок 3-2. Архитектура распределённой базы данных

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

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

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

  • В распределённых приложениях производительность повышается путём размещения большей части данных региона в этом регионе, поскольку это позволяет выполнять обновления только с помощью локальной записи. Однако повышение надежности обеспечивается распределением копий таким образом, чтобы отказ одного региона или разъединение сети не вызывали полного отключения системы.
Цели выживаемости
Распределённая база данных может реализовывать цели выживаемости (survival goals), которые определяют компромисс между задержкой и способностью к выживанию. Например, мы можем выбирать между следующими целями выживаемости:

Цель выживаемости при отказе зоны
База данных будет оставаться доступной для чтения и записи даже в случае отказа узла в зоне. Количество зон, которые могут отказать, будет зависеть от фактора репликации.

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

На рисунке 3-3 показан отказ зоны в базе данных с целью выживаемости зоны. Затронутые диапазоны данных поддерживаются в трех копиях в регионе США (регион по умолчанию) и отказ любой отдельной зоны (в данном случае, зоны Нью-Йорка) в этом регионе позволяет базе данных продолжать функционировать с двумя копиями. Однако если весь регион откажет, то весьма вероятно, что вся база данных станет недоступной, поскольку некоторые данные потеряют более одной копии.

Рисунок 3-3. Пример отказа зоны в цели выживания зоны

На рисунке 3-4 показан отказ региона в базе данных с целью выживания на региональном уровне. В связи с установкой региональных целей выживания для базы данных, фактор репликации автоматически увеличивается до пяти вместо стандартных трех, и копии распределены между регионами. Когда регион США отказывает, всё ещё остаются три копии данных в других регионах и операции с базой данных могут продолжаться.

Рисунок 3-4. Пример отказа региона в цели выживания региона

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

  • В крупных облачных платформах сбои целых регионов редки, но не исключены. Конфигурация выживания на уровне зон, показанная на рисунке 3-3, не могла оставаться полностью доступной во время сбоя региона. Нельзя полагаться на то, что на любой платформе региональный отказ невозможен.

  • Выживание на региональном уровне предполагает выживание на уровне зон. По умолчанию цель выживания на региональном уровне защищает от потери любого отдельного региона или отказа двух зон.

  • Так же, как и для обеспечения выживаемости в кластере одного региона нам требуется как минимум три узла, так же и для обеспечения выживаемости на региональном уровне нам требуется три региона. Если у нас всего два региона, сбой любого из них сделает недоступными большинство копий для некоторых элементов данных.
Правила определения местоположения
Независимо от целей выживания, мы можем настроить распределение данных в таблице для оптимизации доступа из конкретных регионов. Это прежде всего делается для оптимизации запросов с низкой задержкой из различных регионов и также может использоваться для выполнения юридических требований к размещению данных.

В таблицах распределённой базы данных могут быть правила локализации, определяющие, как их данные будут распределены по зонам:

Глобальная таблица
Эта таблица будет оптимизирована для чтения с низкой задержкой из любого региона.

Региональная таблица
Эта таблица будет оптимизирована для чтения и записи с низкой задержкой из одного региона.

Таблица с разбивкой региона по строкам
Эта таблица будет иметь определённые строки, оптимизированные для чтения и записи с низкой задержкой для определённого региона. Различным строкам в таблице можно назначить определенные регионы.
При использовании глобальной таблицы, копии для всех строк в таблице будут продублированы в каждом регионе. Это обеспечивает оптимизацию времени чтения, но создает наивысшие накладные расходы на записи, поскольку все регионы должны согласовывать запрос на запись. Глобальные таблицы подходят для относительно статических таблиц поиска, которые актуальны для всех регионов. Примером может служить таблица продуктов — информация о продукции часто общая для всех регионов и не подвержена частым обновлениям; следовательно, производительность оптимизирована, если каждый регион имеет полную копию таблицы продуктов. Однако недостатком является то, что записи в таблицу продуктов потребуют участия всех регионов и следовательно, будут относительно медленными.

При использовании региональной таблицы максимально возможный объем информации о копиях (с учетом целей выживания) для всех диапазонов в таблице находится в одном регионе. Это имеет смысл, если этот регион является более важным для бизнеса, чем другие регионы или если данные особенно актуальны для этого региона. Например, если в международном приложении коды ошибок для каждого языка были бы в отдельных таблицах, то имело бы смысл разместить их в определённых регионах (хотя это, конечно, возродило бы давнюю дискуссию о том, где должен находиться «английский»).
Таблица с определением по регионам, по строкам определяет местоположение копий для определённых строк в определённых регионах. Например, в таблице пользователей мы могли бы назначить строки для региона США, если их код страны был США, Канада или любая страна в Северной или Южной Америке. Определение региона по строкам является очень эффективным способом перемещения данных поближе к регионам, в которых они требуются.

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

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

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

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

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

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

Мы надеемся, что этот отчет будет полезной отправной точкой для тех, кто отправляется в путешествие к распределённой транзакционной архитектуре.