Есть ли в Tarantool способ проверить состояние кортежа перед обновлением (оптимистическая проверка блокировки / шаблон CAS)?

Я пытаюсь найти информацию о том, как реализовать оптимистические блокировки в БД Tarantool. Этот случай не описан в документации, поэтому я не могу понять, как это сделать.

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


person cgi    schedule 13.07.2020    source источник


Ответы (2)


Это будет хранимая процедура, подобная следующей (код упрощен):

function update_cas(key, tuple, version)
   local old = space:get(key)
   if old.version ~= version then error('Oops!') end
   tuple[VERSION_FIELD_NUMBER] = version + 1
   space:replace(tuple)
end

Надеюсь, это дает вам идею.

person Dmitry Sharonov    schedule 16.07.2020
comment
Спасибо! Как я вижу из документации - такой подход подходит для движка памяти - здесь нет асинхронных операций - поэтому вся процедура выполняется без прерываний. Есть ли дыры, если то же самое идет в дисковый движок? - person cgi; 22.07.2020
comment
Да, вам следует обернуть его в box.begin / box.commit, чтобы убедиться, что такое изменение является транзакционным, тогда - person Dmitry Sharonov; 22.07.2020

Если я правильно понял ваш вопрос, могу предложить вам следующую схему.

Добавьте новую версию поля или отметку времени в кортеж. И проверьте это перед выполнением операции обновления.

Пример: у меня есть схема {id, value, version} и начальный кортеж {1, 0, 0}.

Первый запрос запроса получает кортеж {1, 1, 0} и пытается выполнить операцию обновления {{'+', 'value', 1}}, и только если версия == 0. Затем вы переходите в хранилище, чтобы сохранить результат. Перед сохранением вы получаете исходный кортеж и проверяете, что версия равна 0, а затем сохраняете кортеж. Теперь вы обновили кортеж {1, 1, 1}.

Представьте, что у вас есть второй запрос, связанный с обновлением того же кортежа с помощью операции {{'+', 'value', 2}} и снова только с if version == 0. Вы пытаетесь сохранить его, но когда вы получаете кортеж с идентификатором = 1 вы получите {1, 1, 1}. Это не удовлетворяет условию only_if_version == 0 (поскольку текущая версия кортежа равна 1). В этом случае вы возвращаете своему пользователю ошибку.

person Oleg Babin    schedule 15.07.2020
comment
Вы, вероятно, исправите подробное описание алгоритма оптимистической блокировки - мой вопрос в том, как реализовать его в части тарантула, только если версия == 0 из вашего описания. - person cgi; 22.07.2020