Skip to content

Commit

Permalink
config: automatically set names on schema upgrade
Browse files Browse the repository at this point in the history
This commit adds a trigger, which sets names via box.cfg, when
dd_version_id, seen from box.schema event, is changed to the
version >= 3.0.0 from version < 3.0.0.

On master instances we set replicaset_name and instance_name.
On replicas - only instance_name, they'll automatically register
themselves in _cluster space by making IPROTO_REGISTER to master.
This behavior allows live upgrade to Tarantool 3.0.0.

Closes tarantool#8978

NO_DOC=tarantool/doc#3661
  • Loading branch information
Serpentian committed Sep 27, 2023
1 parent f58c266 commit 4028364
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 0 deletions.
4 changes: 4 additions & 0 deletions changelogs/unreleased/config-automatic-names.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## feature/config

* If Tarantool is congired via yaml file or etcd, then
instance/replicaset/cluster names will be automatically set on schema upgrade.
47 changes: 47 additions & 0 deletions src/box/lua/config/applier/box_cfg.lua
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
local fiber = require('fiber')
local fio = require('fio')
local log = require('internal.config.utils.log')
local instance_config = require('internal.config.instance_config')
local mkversion = require('internal.mkversion')

local function peer_uri(configdata, peer_name)
local iconfig = configdata._peers[peer_name].iconfig_def
Expand Down Expand Up @@ -117,6 +119,47 @@ local function has_snapshot(configdata)
return #fio.glob(pattern) > 0
end

--
-- If schema version is less than 3.0.0, set box.watch trigger, which
-- automatically configures names as soon as schema is upgraded to
-- the version >= 3.0.0
--
local function apply_names_on_upgrade(names, is_rw)
local version = mkversion.get()
local version_3 = mkversion(3, 0, 0)
if version >= version_3 then
return
end

box.watch('box.schema', function(_, value)
local id = value.dd_version_id
-- dd_version_id is not updated in most cases.
if id == version.id then
return
end

if version < version_3 and mkversion.from_id(id) >= version_3 then
-- Indefinitely retry setting names, schema was already updated,
-- we must set names before restart. Otherwise, it will be
-- impossible to start instance again with yaml config.
-- Manual intervention will be required: configure without names,
-- set names with box.cfg.
while true do
local ok, err = pcall(box.cfg, {
instance_name = names.instance_name,
replicaset_name = is_rw and names.replicaset_name or nil,
})
if ok then
break
end

log.info('Failed to set names: %s. Retrying...', err)
fiber.sleep(1)
end
end
end)
end

-- Returns nothing or {needs_retry = true}.
local function apply(config)
local configdata = config._configdata
Expand Down Expand Up @@ -395,6 +438,7 @@ local function apply(config)

log.debug('box_cfg.apply (startup): %s', box_cfg)
box.cfg(box_cfg)
apply_names_on_upgrade(names, configured_as_rw)

-- NB: needs_retry should be true when force_read_only is
-- true. It is so by construction.
Expand All @@ -404,6 +448,9 @@ local function apply(config)
-- If it is reload, just apply the new configuration.
log.debug('box_cfg.apply: %s', box_cfg)
box.cfg(box_cfg)
if is_startup then
apply_names_on_upgrade(names, not box_cfg.read_only)
end
end

return {
Expand Down
Binary file not shown.
16 changes: 16 additions & 0 deletions test/box-luatest/upgrade/2.11.0/replicaset/instance-001/gen.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
box.cfg{
replicaset_uuid = 'cbf06940-0790-498b-948d-042b62cf3d29',
instance_uuid = '8a274925-a26d-47fc-9e1b-af88ce939412',
replication = {3301, 3302},
listen = 3301,
}

box.schema.user.grant('guest', 'super')
box.schema.create_space('test_space')
while not box.info.replication[2] or
not box.info.replication[2].downstream or
box.info.replication[2].downstream.status ~= 'follow' do
require('fiber').yield(0.1)
end
box.snapshot()
os.exit(0)
Binary file not shown.
10 changes: 10 additions & 0 deletions test/box-luatest/upgrade/2.11.0/replicaset/instance-002/gen.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
box.cfg{
replicaset_uuid = 'cbf06940-0790-498b-948d-042b62cf3d29',
instance_uuid = '3de2e3e1-9ebe-4d0d-abb1-26d301b84633',
replication = {3301, 3302},
read_only = true,
listen = 3302,
}

box.snapshot()
os.exit(0)
108 changes: 108 additions & 0 deletions test/config-luatest/upgrade_test.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
local t = require('luatest')
local server = require('test.luatest_helpers.server')
local treegen = require('test.treegen')

local g = t.group('config_upgrade_test')

g.before_all(function()
treegen.init(g)
local dir = treegen.prepare_directory(g, {}, {})
local config = [[
credentials:
users:
guest:
roles: [super]
iproto:
listen: 'unix/:./{{ instance_name }}.iproto'
wal:
dir: %s
snapshot:
dir: %s
groups:
group-001:
replicasets:
replicaset-001:
database:
replicaset_uuid: 'cbf06940-0790-498b-948d-042b62cf3d29'
instances:
instance-001:
database:
mode: rw
instance_uuid: '8a274925-a26d-47fc-9e1b-af88ce939412'
instance-002:
database:
mode: ro
instance_uuid: '3de2e3e1-9ebe-4d0d-abb1-26d301b84633'
]]

local datadir_prefix = 'test/box-luatest/upgrade/2.11.0/replicaset/'
-- Create dirs to generate config
g.server_1 = server:new({
-- Requires non-empty config
chdir = dir,
config_file = '',
alias = 'instance-001',
datadir = datadir_prefix .. 'instance-001',
})
g.server_2 = server:new({
chdir = dir,
config_file = '',
alias = 'instance-002',
datadir = datadir_prefix .. 'instance-002',
})

local config_1 = string.format(config, g.server_1.workdir,
g.server_1.workdir, g.server_1.workdir)
local config_2 = string.format(config, g.server_2.workdir,
g.server_2.workdir, g.server_2.workdir)

local config_file_1 = treegen.write_script(dir, 'config_1.yaml', config_1)
local config_file_2 = treegen.write_script(dir, 'config_2.yaml', config_2)

g.server_1.net_box_uri = 'unix/:' .. dir .. '/instance-001.iproto'
g.server_1.config_file = config_file_1
g.server_1:initialize()

g.server_2.net_box_uri = 'unix/:' .. dir .. '/instance-002.iproto'
g.server_2.config_file = config_file_2
g.server_2:initialize()

g.server_1:start({wait_until_ready = false})
g.server_2:start({wait_until_ready = false})
g.server_1:wait_until_ready()
g.server_2:wait_until_ready()
end)

g.after_all(function()
g.server_1:stop()
g.server_2:stop()
treegen.clean(g)
end)

local function assert_before_upgrade()
t.assert_equals(box.space._schema:get{'version'}, {'version', 2, 11, 0})
local info = box.info
t.assert_equals(info.name, nil)
t.assert_equals(info.replicaset.name, nil)
end

local function assert_after_upgrade(instance_name, replicaset_name)
local info = box.info
t.helpers.retrying({}, function()
t.assert_equals(box.space._schema:get{'version'}, {'version', 3, 0, 0})
t.assert_equals(info.name, instance_name)
t.assert_equals(info.replicaset.name, replicaset_name)
end)
end

g.test_upgrade = function()
g.server_1:exec(assert_before_upgrade)
g.server_2:exec(assert_before_upgrade)

g.server_1:exec(function() box.schema.upgrade() end)
g.server_2:wait_for_vclock_of(g.server_1)

local rs_name = 'replicaset-001'
g.server_1:exec(assert_after_upgrade, {g.server_1.alias, rs_name})
g.server_2:exec(assert_after_upgrade, {g.server_2.alias, rs_name})
end

0 comments on commit 4028364

Please sign in to comment.