2

在多主场景中使用 Tarantool 时如何解决冲突?

我正在开发一个应该具有高可用性的服务,因此决定使用 nginx 作为 tarantool 的两个节点的负载均衡器(带有备份指令)(禁用只读选项)。它会重试对其他节点的失败请求,但如果出现网络问题(例如,tarantool 的节点之间),可能会发生冲突。

如何实现以下场景之一:

  1. 在每个节点上选择一个更新的元组
  2. 自定义逻辑(可能是冲突等的另一个空间)

另一个问题是如何定义唯一的可为空的复合索引(null 是一个可以多次出现的值)

| id | user_id | type | {some data} |

索引:

id - PK
user_id + type - unique nullable tree index (type is nullable)
user_id has non unique tree index
4

3 回答 3

3

关于 Kostja 回答的第二点(on_ctl_init+_space:on_replace 的组合),还有一个技巧:您需要利用 box.on_commit 来访问正在创建的空间。结果片段如下:

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)
于 2019-07-11T13:09:37.437 回答
3

1)您需要在可能有冲突的空间上使用 before_replace 触发器来实现应用程序的冲突解决规则。

https://www.tarantool.io/en/doc/2.1/book/box/box_space/#box-space-before-replace

在触发器中,您可以比较新旧副本记录并选择使用哪一个(或完全跳过更新,或将两条记录合并在一起)。

2)您需要在正确的时间设置触发器,在空间开始接收任何更新之前。您通常设置 before_replace 触发器的方式是在创建空间时正确的,因此您需要一个触发器在系统空间 _space 上设置另一个触发器,以捕捉创建空间的时刻并将触发器设置在那里。这可以是 on_replace 触发器,https: //www.tarantool.io/en/doc/2.1/book/box/box_space/#box-space-on-replace before_replaceon_replace的区别是 *on_replace 在 a 之后调用行被插入到空间中,并且before_replace之前被调用。3) 要设置 _space:on_replace() 触发器,您还需要正确的时机。使用的最佳时机是刚刚创建 _space 时,即 box.ctl.on_schema_init() 触发器。 https://www.tarantool.io/en/doc/2.1/book/box/box_ctl/#lua-function.box.ctl.on_schema_init

于 2019-06-25T07:48:35.957 回答
0

关于2)我遇到了问题。在我的情况下,启用“在创建空间时”触发会导致 read_only 错误。原因是:触发器在从 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

在那种情况下,我只需要在读取 WAL 时启用触发器。在复制同步开始之前(否则我可能会遇到冲突或统计数据松散)。我发现是做这件事的正确时机。在 box.info.status 从“正在加载”更改后,我启用了触发器。像这样:

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

因此before_replace()触发器将myspace在初始 WAL 读取后的第一次提交时执行并启用。

也许有可能在 box.info.status 改变时触发?这可以使代码更干净。但我不知道这是否可能。

于 2019-08-07T13:50:49.093 回答