Skip to content

Commit 49905c9

Browse files
committed
config: don't bootstrap database in isolated mode
In the isolated mode an instance refuses to fetch any data from other replicaset members. An attempt to bootstrap a database on such an instance would produce its own database (for example, it assigns its own replicaset UUID). Unlikely a user wants to place two instances into the same replicaset in the configuration and get two different databases. Part of #10796 NO_DOC=tarantool/doc#4632 NO_CHANGELOG=added together with the configuration option
1 parent e5aea49 commit 49905c9

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed

src/box/lua/config/configdata.lua

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -775,6 +775,35 @@ local function validate_misplacing(cconfig)
775775
end
776776
end
777777

778+
-- Check startup conditions.
779+
local function validate_startup(instance_name, iconfig_def)
780+
local is_startup = type(box.cfg) == 'function'
781+
if not is_startup then
782+
return
783+
end
784+
785+
local no_snap = snapshot.get_path(iconfig_def) == nil
786+
local isolated = instance_config:get(iconfig_def, 'isolated')
787+
788+
-- Forbid startup without a local snapshot in the isolated
789+
-- mode.
790+
if no_snap and isolated then
791+
error(('Startup failure.\nThe isolated mode is enabled and the ' ..
792+
'instance %q has no local snapshot. An attempt to bootstrap ' ..
793+
'the instance would lead to the split-brain situation.'):format(
794+
instance_name), 0)
795+
end
796+
797+
-- TODO: There is a situation, which looks similar, but we
798+
-- don't report an error in the case. It is a startup without
799+
-- a local snapshot with replication.peers configured as an
800+
-- empty list if there are other instances in the replicaset.
801+
-- Are there cases, when it is OK? Maybe if the instance is
802+
-- assigned as a bootstrap leader? Now we pass it over, but
803+
-- maybe it worth to revisit it later and report an error in
804+
-- some definitely/likely erroreous cases.
805+
end
806+
778807
local function new(iconfig, cconfig, instance_name)
779808
-- Find myself in a cluster config, determine peers in the same
780809
-- replicaset.
@@ -921,6 +950,9 @@ local function new(iconfig, cconfig, instance_name)
921950
}, iconfig_def)
922951
end
923952

953+
-- A couple of checks that are only performed on startup.
954+
validate_startup(instance_name, iconfig_def)
955+
924956
return setmetatable({
925957
_iconfig = iconfig,
926958
_iconfig_def = iconfig_def,
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
local yaml = require('yaml')
2+
local t = require('luatest')
3+
local cbuilder = require('luatest.cbuilder')
4+
local treegen = require('luatest.treegen')
5+
local justrun = require('luatest.justrun')
6+
local cluster = require('test.config-luatest.cluster')
7+
local it = require('test.interactive_tarantool')
8+
9+
local g = t.group()
10+
11+
g.before_all(cluster.init)
12+
g.after_each(cluster.drop)
13+
g.after_all(cluster.clean)
14+
15+
g.after_each(function(g)
16+
if g.it ~= nil then
17+
g.it:close()
18+
end
19+
end)
20+
21+
-- Verify that an instance can't start in the isolated mode if
22+
-- there is no local snapshot.
23+
g.test_startup_no_snap = function()
24+
local config = cbuilder:new()
25+
:set_replicaset_option('replication.failover', 'manual')
26+
:set_replicaset_option('leader', 'i-001')
27+
:add_instance('i-001', {})
28+
:add_instance('i-002', {})
29+
:add_instance('i-003', {isolated = true})
30+
:config()
31+
32+
-- Write config to a temporary directory.
33+
local dir = treegen.prepare_directory({}, {})
34+
local config_file = treegen.write_file(dir, 'config.yaml',
35+
yaml.encode(config))
36+
37+
-- Run tarantool instance that is expected to exit
38+
-- immediately.
39+
local env = {}
40+
local args = {'--name', 'i-003', '--config', config_file}
41+
local opts = {nojson = true, stderr = true}
42+
local res = justrun.tarantool(dir, env, args, opts)
43+
44+
-- Verify the exit code and the error reported to stderr.
45+
local exp_err = 'Startup failure.\n' ..
46+
'The isolated mode is enabled and the instance "i-003" has no local ' ..
47+
'snapshot. An attempt to bootstrap the instance would lead to the ' ..
48+
'split-brain situation.'
49+
t.assert_covers(res, {
50+
exit_code = 1,
51+
stderr = ('LuajitError: %s\nfatal error, exiting the event loop')
52+
:format(exp_err),
53+
})
54+
end
55+
56+
-- The opposite to the previous test case: verify that an instance
57+
-- can start in the isolated mode if there is a local snapshot.
58+
g.test_startup_with_snap = function(g)
59+
local config = cbuilder:new()
60+
:set_replicaset_option('replication.failover', 'manual')
61+
:set_replicaset_option('leader', 'i-001')
62+
:add_instance('i-001', {})
63+
:add_instance('i-002', {})
64+
:add_instance('i-003', {})
65+
:config()
66+
67+
local cluster = cluster.new(g, config)
68+
cluster:start()
69+
70+
-- Stop i-003. It leaves a local snapshot.
71+
cluster['i-003']:stop()
72+
73+
-- Mark i-003 as isolated in the configuration, write it to
74+
-- the file.
75+
local config_2 = cbuilder:new(config)
76+
:set_instance_option('i-003', 'isolated', true)
77+
:config()
78+
cluster:sync(config_2)
79+
80+
-- Start the instance again from the local snapshot in the
81+
-- isolated mode.
82+
cluster['i-003']:start()
83+
84+
-- Use the console connection, because an instance in the
85+
-- isolated mode doesn't accept iproto requests.
86+
g.it = it.connect(cluster['i-003'])
87+
88+
-- Verify that the instance is started in the isolated mode.
89+
g.it:roundtrip("require('config'):get('isolated')", true)
90+
91+
-- Verify that the instance loaded the database.
92+
g.it:roundtrip("box.space._schema:get({'replicaset_name'})[2]",
93+
'replicaset-001')
94+
end

0 commit comments

Comments
 (0)