Skip to content

Commit

Permalink
config: forbid a replicaset of only anon replicas
Browse files Browse the repository at this point in the history
A replicaset that contains only anonymous replicas can't be
bootstrapped, because all the instances must in read-only mode.

Part of tarantool#9432

NO_DOC=The documentation request is in the last commit of the series.
  • Loading branch information
Totktonada committed Dec 5, 2023
1 parent 7d820fe commit b66e9aa
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 3 deletions.
3 changes: 0 additions & 3 deletions changelogs/unreleased/config-anonymous-replica.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@

There are caveats that are not resolved yet:

* An attempt to configure a replicaset where all instances are anonymous
replicas should lead to an error on config validation, before configuration
applying.
* An attempt to configure an anonymous replica in read-write mode (using
`database.mode` or `<replicaset>.leader`) should lead to an error on config
validation, before configuration applying.
Expand Down
4 changes: 4 additions & 0 deletions src/box/lua/config/applier/box_cfg.lua
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,10 @@ local function apply(config)
--
-- * 1 instance: read-write.
-- * >1 instances: read-only.
--
-- NB: configdata.lua verifies that there is at least one
-- non-anonymous instance. So, an anonymous replica is
-- read-only by default.
local mode = configdata:get('database.mode', {use_default = true})
if mode == 'ro' then
box_cfg.read_only = true
Expand Down
35 changes: 35 additions & 0 deletions src/box/lua/config/configdata.lua
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,37 @@ local function validate_failover(found, peers, failover, leader)
end
end

-- Verify that the given replicaset contains at least one
-- non-anonymous replica.
--
-- This check doesn't verify the whole cluster config, only the
-- given replicaset.
local function validate_anon(found, peers)
-- failover: <any>
--
-- A replicaset can't consist of only anonymous replicas.
assert(next(peers) ~= nil)
local found_non_anon = false
for _, peer in pairs(peers) do
local is_anon =
instance_config:get(peer.iconfig_def, 'replication.anon')
if not is_anon then
found_non_anon = true
break
end
end
if not found_non_anon then
error(('All the instances of replicaset %q of group %q are ' ..
'configured as anonymous replicas; it effectively means that ' ..
'the whole replicaset is read-only; moreover, it means that ' ..
'default replication.peers construction logic will create ' ..
'empty upstream list and each instance is de-facto isolated: ' ..
'neither is connected to any other; this configuration is ' ..
'forbidden, because it looks like there is no meaningful ' ..
'use case'):format(found.replicaset_name, found.group_name), 0)
end
end

local function new(iconfig, cconfig, instance_name)
-- Find myself in a cluster config, determine peers in the same
-- replicaset.
Expand Down Expand Up @@ -547,6 +578,10 @@ local function new(iconfig, cconfig, instance_name)
end
end

-- Verify that there is at least one non-anonymous replica in
-- the given replicaset.
validate_anon(found, peers)

-- Verify "replication.failover" = "supervised" strategy
-- prerequisites.
local bootstrap_leader_name
Expand Down
38 changes: 38 additions & 0 deletions test/config-luatest/anonymous_replica_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ g.before_all(replicaset.init)
g.after_each(replicaset.drop)
g.after_all(replicaset.clean)

-- Ease writing of a long error message in a code.
local function toline(s)
return s:gsub('\n', ''):gsub(' +', ' '):strip()
end

-- Verify that an anonymous replica can be started and joined to
-- a replicaset.
--
Expand Down Expand Up @@ -157,3 +162,36 @@ g.test_supervised_mode_bootstrap_leader_not_anon = function(g)
t.assert_equals(box.info.id, 1)
end)
end

-- Verify that a replicaset with all the instances configured as
-- anonymous replicas refuse to start with a meaningful error
-- message.
g.test_all_anonymous = function(g)
local config = cbuilder.new()
:add_instance('instance-001', {
replication = {
anon = true,
},
})
:add_instance('instance-002', {
replication = {
anon = true,
},
})
:add_instance('instance-003', {
replication = {
anon = true,
},
})
:config()

replicaset.startup_error(g, config, toline([[
All the instances of replicaset "replicaset-001" of group "group-001"
are configured as anonymous replicas; it effectively means that the
whole replicaset is read-only; moreover, it means that default
replication.peers construction logic will create empty upstream list
and each instance is de-facto isolated: neither is connected to any
other; this configuration is forbidden, because it looks like there
is no meaningful use case
]]))
end

0 comments on commit b66e9aa

Please sign in to comment.