Статьи

Особенности RavenDB 4.1: транзакции ACID на уровне кластера

Одной из основных функций, появившихся в RavenDB 4.1, является возможность выполнять транзакции в кластере. До этого момента транзакции RavenDB применялись на каждом узле индивидуально, а затем отправлялись остальной части кластера. Это следует распределенной модели, изложенной в статье «Динамо». Другими словами, записи важны, всегда принимайте их. Это прекрасно работает для большинства сценариев, но есть несколько случаев, когда пользователь может явно выбрать согласованность вместо доступности. RavenDB 4.1 переносит это на стол, что я считаю очень естественным образом.

Эта функция основана на уже существующей функции сравнения обмена в RavenDB 4.0. Идея проста. Вы можете упаковать пакет изменений в документы и отправить их в кластер. Этот набор изменений будет применен ко всем узлам кластера (атомарным способом), если они были приняты большинством узлов в кластере. В противном случае вы получите ошибку, и изменения не будут применены.

using (var session = store.OpenSession(new SessionOptions
{
    //default is:     TransactionMode.SingleNode
    TransactionMode = TransactionMode.ClusterWide
}))
{
    var user = new User
    {
        Name = "Arava"
    };
    session.Store(user);
    var project = new Project
    {
        Name = "RavenDB QA",
        Owner = user.Id
    };
    session.Store(project);

    session.SaveChanges();
}

Вот команда, которая отправляется на сервер.

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

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

Давайте посмотрим, что я имею в виду под этим:

using (var session = store.OpenSession(new SessionOptions
{
    //default is:     TransactionMode.SingleNode
    TransactionMode = TransactionMode.ClusterWide
}))
{

    var user = new User
    {
        Name = "Arava",
    };
    session.Store(user);

    // this transaction is now conditional on this being 
    // successfully created (so, no other users with this name)
    // it also creates an association to the new user's id
    session.Advanced.ClusterTransaction
        .CreateCompareExchangeValue("usernames/Arava", user.Id);


    var project = new Project
    {
        Name = "RavenDB QA",
        Owner = user.Id
    };
    session.Store(project);

    session.SaveChanges();
}

Как видите, у нас есть новая команда: «ClusterTransaction.CreateCompareExchangeValue». Это добавление еще одной команды к транзакции. Команда сравнения обмена. В этом случае мы говорим, что хотим создать новое значение с именем «usernames / Arava» и установить его значение в качестве идентификатора документа.

Вот это команда, которая отправляется на сервер:

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

Мы добавляем как обмен сравнениями, так и документ (и документ проекта не показан) здесь как одну операцию.

Вот кикер. Что произойдет, если мы снова запустим этот код?

Вы получите следующую ошибку:

Raven.Client.Exceptions.ConcurrencyException: не удалось выполнить кластерную транзакцию из-за следующих проблем: Не удалось проверить параллелизм для ввода ключа ‘usernames / Arava’. Запрошенный индекс: 0, фактический индекс: 1243

Ничего не применяется и транзакция откатывается.

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

Конечно, мы предполагаем, что это будет использоваться для уникальности, но также и для операций с высокой стоимостью, где согласованность важнее доступности. Хорошим примером будет создание заказа на место в игре. Несколько клиентов могут попытаться приобрести одно и то же место в одно и то же время, и вы можете использовать эту функцию, чтобы избежать двойного бронирования. * Если вам удастся успешно претендовать на место, ваш документ заказа обновляется, и вы можете продолжить , В противном случае все сводится на нет.

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

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

* Да, я знаю, что на самом деле это не так, но это хороший пример.