Skip to content

Commit

Permalink
cfg: introduce identification_mode
Browse files Browse the repository at this point in the history
Part of tarantool#426

@TarantoolBot document
Title: vshard: config identification mode

The option `identification_mode` should be specified in the root of the
config. It can have one of those values:

* `'uuid_as_key'` - default. Means, that default uuid config
    identification is used. replica.name is allowed and should not be
    interpreted as `box.cfg.instance_name`. replica/replicaset.uuid is
    forbidden. The config should have the following format:
    {
        ['cbf06940-0790-498b-948d-042b62cf3d29'] = { -- replicaset tarantool#1
            replicas = {
                ['8a274925-a26d-47fc-9e1b-af88ce939412'] = {
                    name = 'storage_1_a',
                    ...
                },
                ...
            },
        },
        ...
    }

* `'name_as_key'`. Name identification is used, supported only by
    Tarantool >= 3.0.0. It's forbidden to specify replica.name in
    such format. UUIDs are optional and can be specified via
    replicaset/replica.uuid:
    {
        replicaset_1 = {
            uuid = 'cbf06940-0790-498b-948d-042b62cf3d29',
            replicas = {
                replica_1_a = {
                    uuid = '8a274925-a26d-47fc-9e1b-af88ce939412'
                    ...
                },
                ...
            }
        },
        ...
    }

    Note, that names, used as keys in config are passed to
    box.cfg.replicaset/instance_name for storage. In case of
    reconfiguration it's strictly validated, that both
    replicaset and instance name corresponds to the passed
    config. Vshard doesn't deal with changing or setting names,
    it must be done externally (using Tarantool's config module,
    for example).
  • Loading branch information
Serpentian committed Dec 4, 2023
1 parent 2fbb53f commit 908a672
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 30 deletions.
82 changes: 82 additions & 0 deletions test/unit-luatest/config_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -290,3 +290,85 @@ g.test_enum = function()
vcfg.check, config)
config.rebalancer_mode = nil
end

g.test_enum_identification_mode = function()
t.run_only_if(vutil.version_is_at_least(3, 0, 0, 'entrypoint', 0, 0))
local config = {
sharding = {
storage_1_uuid = {
replicas = {}
},
},
}
-- Test enum identification_mode.
for _, v in pairs({'uuid_as_key', 'name_as_key'}) do
config.identification_mode = v
t.assert(vcfg.check(config))
end
config.identification_mode = 'bad'
t.assert_error_msg_content_equals(
"Config identification mode must be enum " ..
"{'uuid_as_key', 'name_as_key', nil}",
vcfg.check, config)
config.identification_mode = nil
end

g.test_identification_mode_name_as_key = function()
t.run_only_if(vutil.version_is_at_least(3, 0, 0, 'entrypoint', 0, 0))
local storage_1_a = {
uuid = 'storage_1_a_uuid',
uri = 'storage:storage@127.0.0.1:3301',
}
local replicaset_1 = {
uuid = 'replicaset_1_uuid',
replicas = {
storage_1_a = storage_1_a,
},
}
local config = {
identification_mode = 'name_as_key',
sharding = {
replicaset_1 = replicaset_1,
},
}

-- UUID is optional.
storage_1_a.uuid = nil
replicaset_1.uuid = nil
t.assert(vcfg.check(config))

-- replica.name is forbidden.
storage_1_a.name = 'name'
t.assert_error_msg_content_equals(
'replica.name can be specified only when ' ..
'identification_mode = "uuid_as_key"', vcfg.check, config)
end

g.test_identification_mode_uuid_as_key = function()
local storage_1_a = {
name = 'storage_1_a',
uri = 'storage:storage@127.0.0.1:3301',
}
local replicaset_1 = {
replicas = {
storage_1_a_uuid = storage_1_a,
},
}
local config = {
identification_mode = 'uuid_as_key',
sharding = {
replicaset_1_uuid = replicaset_1,
},
}
t.assert(vcfg.check(config))

-- replica.name is optional.
storage_1_a.name = nil
t.assert(vcfg.check(config))

-- replicaset/replica.uuid is forbidden.
storage_1_a.uuid = 'uuid'
t.assert_error_msg_content_equals(
'uuid option can be specified only when ' ..
'identification_mode = "name_as_key"', vcfg.check, config)
end
10 changes: 5 additions & 5 deletions test/unit/config.result
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ cfg.sharding['rsid3'] = replicaset3
...
check(cfg)
---
- Duplicate uuid id3
- Duplicate id id3
...
cfg.sharding['rsid3'] = nil
---
Expand Down Expand Up @@ -463,9 +463,9 @@ lcfg.check(cfg)['sharding']
weight: 100000
replicas:
replica_uuid:
master: true
uri: 127.0.0.1
name: storage
master: true
...
replica.uri = 'user:password@localhost'
---
Expand All @@ -476,9 +476,9 @@ lcfg.check(cfg)['sharding']
weight: 100000
replicas:
replica_uuid:
master: true
uri: user:password@localhost
name: storage
master: true
...
replica.url = old_uri
---
Expand Down Expand Up @@ -660,13 +660,13 @@ replicaset.master = 'auto'
util.check_error(lcfg.check, cfg)
---
- Can not specify master nodes when master search is enabled, but found master flag
in replica uuid uuid
in replica id uuid
...
replica.master = false
---
...
util.check_error(lcfg.check, cfg)
---
- Can not specify master nodes when master search is enabled, but found master flag
in replica uuid uuid
in replica id uuid
...
101 changes: 76 additions & 25 deletions vshard/cfg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,20 @@ local function check_replica_master(master, ctx)
end
end

local function check_uuid(uuid, ctx)
if uuid and not ctx.is_named then
error('uuid option can be specified only when ' ..
'identification_mode = "name_as_key"')
end
end

local function check_replica_name(name, ctx)
if name and ctx.is_named then
error('replica.name can be specified only when ' ..
'identification_mode = "uuid_as_key"')
end
end

local function is_number(v)
return type(v) == 'number' and v == v
end
Expand Down Expand Up @@ -198,17 +212,28 @@ local replica_template = {
is_optional = true,
is_overriding_box = true,
},
name = {type = 'string', name = "Name", is_optional = true},
name = {
type = 'string',
name = "Name",
check = check_replica_name,
is_optional = true,
},
zone = {type = {'string', 'number'}, name = "Zone", is_optional = true},
master = {
type = 'boolean', name = "Master", is_optional = true,
check = check_replica_master
},
rebalancer = {type = 'boolean', name = 'Rebalancer flag', is_optional = true},
uuid = {
type = {'non-empty string'},
check = check_uuid,
name = 'UUID',
is_optional = true,
},
}

local function check_replicas(replicas)
local ctx = {master = false}
local function check_replicas(replicas, ctx)
ctx.master = false
for _, replica in pairs(replicas) do
validate_config(replica, replica_template, ctx)
end
Expand All @@ -226,6 +251,12 @@ local replicaset_template = {
enum = {'auto'},
},
rebalancer = {type = 'boolean', name = 'Rebalancer flag', is_optional = true},
uuid = {
type = {'non-empty string'},
check = check_uuid,
name = 'UUID',
is_optional = true,
},
}

--
Expand Down Expand Up @@ -257,17 +288,23 @@ local function cfg_check_weights(weights)
end
end

local function check_sharding(sharding)
local uuids = {}
local function check_sharding(sharding, ctx)
local ids = {}
local uris = {}
local names = {}
local is_all_weights_zero = true
local rebalancer_uuid
for replicaset_uuid, replicaset in pairs(sharding) do
if uuids[replicaset_uuid] then
error(string.format('Duplicate uuid %s', replicaset_uuid))
local rebalancer_id
if ctx.is_named then
-- It's allowed to have the same names for replicaset and replica.
ids.replicasets = {}
ids.replicas = {}
end
for replicaset_id, replicaset in pairs(sharding) do
local rs_ids = ctx.is_named and ids.replicasets or ids
if rs_ids[replicaset_id] then
error(string.format('Duplicate id %s', replicaset_id))
end
uuids[replicaset_uuid] = true
rs_ids[replicaset_id] = true
if type(replicaset) ~= 'table' then
error('Replicaset must be a table')
end
Expand All @@ -276,41 +313,42 @@ local function check_sharding(sharding)
error('Replicaset weight can not be Inf')
end
if replicaset.rebalancer then
if rebalancer_uuid then
if rebalancer_id then
error(('Found 2 rebalancer flags at %s and %s'):format(
rebalancer_uuid, replicaset_uuid))
rebalancer_id, replicaset_id))
end
rebalancer_uuid = replicaset_uuid
rebalancer_id = replicaset_id
end
validate_config(replicaset, replicaset_template)
validate_config(replicaset, replicaset_template, ctx)
local no_rebalancer = replicaset.rebalancer == false
local is_master_auto = replicaset.master == 'auto'
for replica_uuid, replica in pairs(replicaset.replicas) do
for replica_id, replica in pairs(replicaset.replicas) do
if uris[replica.uri] then
error(string.format('Duplicate uri %s', replica.uri))
end
uris[replica.uri] = true
if uuids[replica_uuid] then
error(string.format('Duplicate uuid %s', replica_uuid))
local replica_ids = ctx.is_named and ids.replicas or ids
if replica_ids[replica_id] then
error(string.format('Duplicate id %s', replica_id))
end
uuids[replica_uuid] = true
replica_ids[replica_id] = true
if is_master_auto and replica.master ~= nil then
error(string.format('Can not specify master nodes when '..
'master search is enabled, but found '..
'master flag in replica uuid %s',
replica_uuid))
'master flag in replica id %s',
replica_id))
end
if replica.rebalancer then
if rebalancer_uuid then
if rebalancer_id then
error(('Found 2 rebalancer flags at %s and %s'):format(
rebalancer_uuid, replica_uuid))
rebalancer_id, replica_id))
end
if no_rebalancer then
error(('Replicaset %s can\'t run the rebalancer, and yet '..
'it was explicitly assigned to its instance '..
'%s'):format(replicaset_uuid, replica_uuid))
'%s'):format(replicaset_id, replica_id))
end
rebalancer_uuid = replica_uuid
rebalancer_id = replica_id
end
-- Log warning in case replica.name duplicate is
-- found. Message appears once for each unique
Expand All @@ -333,6 +371,12 @@ local function check_sharding(sharding)
end
end

local function check_identification_mode(mode)
if mode == 'name_as_key' and not lutil.feature.persistent_names then
error('Name identification is supported only for Tarantool >= 3.0.0')
end
end

local cfg_template = {
sharding = {type = 'table', name = 'Sharding', check = check_sharding},
weights = {
Expand Down Expand Up @@ -407,6 +451,12 @@ local cfg_template = {
name = 'Scheduler bucket move quota', type = 'non-negative number',
is_optional = true, default = consts.DEFAULT_SCHED_MOVE_QUOTA
},
identification_mode = {
name = 'Config identification mode', type = 'enum',
is_optional = true, default = 'uuid_as_key',
enum = {'uuid_as_key', 'name_as_key'},
check = check_identification_mode,
},
}

--
Expand Down Expand Up @@ -458,7 +508,8 @@ local function cfg_check(shard_cfg, old_cfg)
error('Сonfig must be map of options')
end
shard_cfg = table.deepcopy(shard_cfg)
validate_config(shard_cfg, cfg_template)
local ctx = {is_named = shard_cfg.identification_mode == 'name_as_key'}
validate_config(shard_cfg, cfg_template, ctx)
if not old_cfg then
return shard_cfg
end
Expand Down
1 change: 1 addition & 0 deletions vshard/util.lua
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,7 @@ local feature = {
local ok, res = pcall(function() return box.info end)
return ok and res.replicaset ~= nil
end)(),
persistent_names = version_is_at_least(3, 0, 0, 'entrypoint', 0, 0),
}

local schema_version = function()
Expand Down

0 comments on commit 908a672

Please sign in to comment.