问题
How can I implement resolution of conflicts when I use Tarantool in multi master scenario?
I'm developing a service which should be highly available, so decided to use nginx as a load balancer (with backup directive) for two nodes of tarantool (with disabled read only option). It retries failed requests to other node, but in case of network issues (for instance, between nodes of tarantool) conflicts may occur.
How can I implement one of the following scenarios:
- Choose a newer tuple on each node
- Custom logic (may be another space for conflics and so on)
Another question is how can I define unique nullable compound index (null is a value which can occur multiple times)
| id | user_id | type | {some data} |
Indexes:
id - PK
user_id + type - unique nullable tree index (type is nullable)
user_id has non unique tree index
回答1:
1) You need a before_replace trigger on the space which may have conflict to implement the rules of conflict resolution of your application.
https://www.tarantool.io/en/doc/2.1/book/box/box_space/#box-space-before-replace
In the trigger you can compare old and new replica record and choose which one to use (or skip the update entirely, or merge two records together).
2) You need to set the trigger at the right time, before the space starts to receive any updates. The way you usually set the before_replace trigger is right when the space is created, so you need a trigger set another trigger on system space _space, to capture the moment when your space is created and set the trigger there. This can be on_replace trigger, https://www.tarantool.io/en/doc/2.1/book/box/box_space/#box-space-on-replace the difference between before_replace and on_replace is that *on_replace is called after a row is inserted into the space, and before_replace is called before. 3) To set _space:on_replace() trigger you also need the right timing. The best timing to use is when _space is just created, which is box.ctl.on_schema_init() trigger. https://www.tarantool.io/en/doc/2.1/book/box/box_ctl/#lua-function.box.ctl.on_schema_init
回答2:
Regarding second point from Kostja's answer (combination of on_ctl_init+_space:on_replace), there is one more trick: you'll need to utilize box.on_commit to get acces to the space being created. Resulting snippet would be the following:
local my_space_name = 'ny_space'
local my_trigger = function(old, new) ... end
box.schema.on_schema_init(function()
box.space._space:on_replace(function(_, new_space)
if new_space.name == my_space_name then
box.on_commit(function()
box.space[my_space_name]:before_replace(my_trigger)
end
end
end)
end)
回答3:
Regarding 2) I hit the problem. Enabling trigger "right when the space is created" in my case cause read_only errors. The reason is: trigger tries to upsert to statistic table while reading from WAL.
local function before_replace(old, new)
-- collision resolving here
if box.session.type() ~= 'applier' then
box.space.stat:upsert(
{ "key", 0 },
{
{"+", stat.COUNT, 1}
})
end
return
end
In that case i need to enable trigger only then WAL had been read. And before replication sync starts(otherwise i can hit collision, or loose statistic).I found this is the right time to do it. I enable a trigger after box.info.status changed from "loading". Like this:
local my_space_name = 'myspace'
local function loading_before_replace(old, new)
if box.info.status == "loading" then
return
end
box.space.my_space_name:before_replace(before_replace, loading_before_replace)
return before_replace(old,new)
end
local function _space_on_replace(old, new)
if not new or not new.name then
return
end
-- skip system spaces
if string.startswith(new.name, "_") then
return
end
if new.name == my_space_name then
box.on_commit(function()
box.space.my_space_name:before_replace(loading_before_replace)
end)
end
end
local function set_triggers()
box.ctl.on_schema_init(function()
box.space._space:on_replace(_space_on_replace)
end)
end
So before_replace()
trigger will be executed and enabled on the first commitment to myspace
after initial WAL reading.
Maybe it's possible to hit trigger on box.info.status changed? This could make code cleaner. But i don't know if it's possible.
来源:https://stackoverflow.com/questions/56734280/conflict-resolution-in-tarantool-how-to-fix-replication-in-master-master-mode-i