Дедупликация неидемпотентных запросов¶
Идемпотентные запросы – это операции, повторный вызов которых дает тот же результат, что и при первом применении. Например, идемпотентными являются запрос на чтение данных или операция умножения на единицу. Соответственно, примером неидемпотентной операции можно назвать увеличение на единицу. При повторном применении такой операции значение для поля будет суммарно увеличено уже не на 1, а на 2.
Note
Любые запросы на запись, которые планируется выполнять повторно, должны быть идемпотентны. Идемпотентность таких операций гарантирует, что изменение из операции будет применено только один раз.
Повторный вызов запросов¶
Повторное выполнение запроса может понадобиться в том случае, если возникла ошибка со стороны сервера или клиента. При этом:
Запросы на чтение данных можно выполнять повторно. Для этого в модуле
vshard
у метода vshard.router.call() в режимеread
(mode=read
) используется параметрrequest_timeout
(доступно с версииvshard
0.1.28). Параметрыrequest_timeout
иtimeout
нужно передавать вместе, соблюдая следующее условие:timeout > request_timeout
Например, если
timeout = 10
, аrequest_timeout = 2
, то в течение 10 секунд роутер сможет сделать до 5 попыток обращения (по 2 секунды на каждую) с запросом на разные реплики, пока запрос наконец не преуспеет.Запросы на запись данных (vshard.router.callrw()) в общем случае нельзя выполнять повторно без проверки на то, что запрос не был применен раньше. Отсутствие такой проверки может привести к дубликатам записей или незапланированному изменению данных.
Например, клиент отправил запрос на сервер и ожидает ответа в течение заданного времени. Если сервер отправит ответ об успешном выполнении уже после истечения этого времени, клиент получит ошибку и при повторной отправке запроса без дополнительной проверки операция может быть применена дважды.
Выполнять запрос на запись повторно без проверки можно только в том случае, если ошибка возникла со стороны сервера – например,
ER_READONLY
.
Способы дедупликации запросов¶
Чтобы гарантировать идемпотентность запросов на запись, таких как вставка и обновление данных, а также автоинкремент, необходимо внедрить в код проверку, что запрос применяется впервые.
Например, при добавлении нового кортежа в спейс для проверки можно использовать уникальный ключ, по которому производится вставка. В таком запросе в рамках одной транзакции:
Проверяется, есть ли в спейсе
bands
кортеж с ключомkey
.Если записи с таким ключом в спейсе нет, выполняется вставка кортежа.
box.begin()
if box.space.bands:get{key} == nil then
box.space.bands:insert{key, value}
end
box.commit()
Для запросов на обновление кортежа можно создать отдельный спейс дедупликации, в который будут сохраняться ID запросов.
Спейс дедупликации – это пользовательский спейс, который содержит список уникальных идентификаторов.
Каждый такой идентификатор соответствует одному выполненному запросу.
Этот спейс может иметь любое имя, в примере он называется deduplication
.
В примере ниже в рамках одной транзакции:
В спейсе
deduplication
проверяется наличие ID запросаdeduplication_key
.Если такого ID нет, этот ID добавляется в спейс дедупликации.
После запрос увеличивает заданное поле в спейсе
bands
на единицу.
Такой подход гарантирует, что каждый запрос на изменение данных будет выполнен только один раз.
function update_1(deduplication_key, key)
box.begin()
if box.space.deduplication:get{deduplication_key} == nil then
box.space.deduplication:insert{deduplication_key}
box.space.bands:update(key, {{'+', 'value', 1 }})
end
box.commit()
end