Skip to content

Commit 163c7d4

Browse files
committed
cluster: expel instances removed from config in cluster.sync
Currently, cluster:sync() leaves instances removed from the config in the instance list. This makes cluster:reload() fail because it tries to reload the config for all instances including those that are not in the config anymore. To fix that, let's make cluster:sync() move instances that were removed from the config to the special expelled instance list so that they become inaccessible via cluster but are still dropped by cluster:drop(). Also, let's add the new boolean option start_stop for cluster:sync(). If set, cluster:sync() will stop old servers and start new ones. Closes #423
1 parent e03b037 commit 163c7d4

File tree

3 files changed

+110
-12
lines changed

3 files changed

+110
-12
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@
44

55
- Fixed a bug when `server:grep_log()` failed to find a string logged in
66
`server:exec()` called immediately before it (gh-421).
7+
- Fixed a bug when it wasn't possible to reload the cluster config with
8+
`cluster:reload()` after removing an instance with `cluster:sync()`.
9+
Also added an option to `cluster:sync()` to start/stop added/removed
10+
instances (gh-423).
711

812
## 1.1.0
913

luatest/cluster.lua

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,12 @@ function Cluster:drop()
187187
for _, iserver in ipairs(self._servers or {}) do
188188
iserver:drop()
189189
end
190+
for _, iserver in ipairs(self._expelled_servers or {}) do
191+
iserver:drop()
192+
end
190193
self._servers = nil
191194
self._server_map = nil
195+
self._expelled_servers = nil
192196
end
193197

194198
--- Sync the cluster object with the new config.
@@ -197,25 +201,50 @@ end
197201
--
198202
-- * Write the new config into the config file.
199203
-- * Update the internal list of instances.
204+
-- * Optionally starts instances added to the config and stops instances
205+
-- removed from the config.
200206
--
201207
-- @tab config New config.
202-
function Cluster:sync(config)
208+
-- @tab[opt] opts Options.
209+
-- @bool[opt] opts.start_stop Start/stop added/removed servers (default: false).
210+
function Cluster:sync(config, opts)
203211
assert(type(config) == 'table')
204212

213+
local start_stop = false
214+
if opts ~= nil and opts.start_stop ~= nil then
215+
start_stop = opts.start_stop
216+
end
217+
205218
local instance_names = instance_names_from_config(config)
206219

207220
treegen.write_file(self._dir, self._config_file_rel, yaml.encode(config))
208221

209-
for i, name in ipairs(instance_names) do
210-
if self._server_map[name] == nil then
211-
local iserver = server:new(fun.chain(self._server_opts, {
222+
local server_map = self._server_map
223+
self._server_map = {}
224+
self._servers = {}
225+
226+
for _, name in ipairs(instance_names) do
227+
local iserver = server_map[name]
228+
if iserver == nil then
229+
iserver = server:new(fun.chain(self._server_opts, {
212230
alias = name,
213231
}):tomap())
214-
table.insert(self._servers, i, iserver)
215-
self._server_map[name] = iserver
232+
if start_stop then
233+
iserver:start()
234+
end
235+
else
236+
server_map[name] = nil
216237
end
238+
self._server_map[name] = iserver
239+
table.insert(self._servers, iserver)
217240
end
218241

242+
for _, iserver in pairs(server_map) do
243+
table.insert(self._expelled_servers, iserver)
244+
if start_stop then
245+
iserver:stop()
246+
end
247+
end
219248
end
220249

221250
--- Reload configuration on all the instances.
@@ -297,6 +326,7 @@ function Cluster:new(config, server_opts, opts)
297326
-- Store a cluster object in 'g'.
298327
self._servers = servers
299328
self._server_map = server_map
329+
self._expelled_servers = {}
300330
self._dir = dir
301331
self._config_file_rel = config_file_rel
302332
self._server_opts = server_opts

test/cluster_test.lua

Lines changed: 70 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -142,9 +142,6 @@ g.test_sync = function()
142142
c:start()
143143
assert_instance_running(c, 'i-001')
144144

145-
c:stop()
146-
assert_instance_stopped(c, 'i-001')
147-
148145
local config2 = cbuilder:new()
149146
:use_group('g-001')
150147
:use_replicaset('r-001')
@@ -156,18 +153,85 @@ g.test_sync = function()
156153

157154
:config()
158155

156+
local server1 = c['i-001']
157+
159158
c:sync(config2)
160159

161-
t.assert_equals(c:size(), 3)
160+
-- Check that the server that was removed from the config is expelled
161+
-- from the cluster but not stopped.
162+
t.assert_equals(c:size(), 2)
163+
t.assert_is(c['i-001'], nil)
164+
t.assert_is_not(server1.process, nil)
162165

163-
c:start_instance('i-002')
164-
c:start_instance('i-003')
166+
-- Check that the new servers are not started.
167+
assert_instance_stopped(c, 'i-002')
168+
assert_instance_stopped(c, 'i-003')
169+
170+
c:start()
165171
assert_instance_running(c, 'i-002')
166172
assert_instance_running(c, 'i-003')
167173

174+
-- Check config reload works after sync.
175+
c:reload()
176+
168177
c:stop()
169178
assert_instance_stopped(c, 'i-002')
170179
assert_instance_stopped(c, 'i-003')
180+
181+
-- Starting/stopping the cluster shouldn't affect the expelled server.
182+
-- However, dropping the cluster should also drop the expelled server.
183+
t.assert_is_not(server1.process, nil)
184+
c:drop()
185+
t.assert_is(server1.process, nil)
186+
end
187+
188+
g.test_sync_start_stop = function()
189+
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
190+
[[Declarative configuration works on Tarantool 3.0.0+.
191+
See tarantool/tarantool@13149d65bc9d for details]])
192+
193+
t.assert_equals(g._cluster, nil)
194+
195+
local config = cbuilder:new()
196+
:use_group('g-001')
197+
:use_replicaset('r-001')
198+
:add_instance('i-001', {})
199+
:config()
200+
201+
local c = cluster:new(config, server_opts)
202+
203+
t.assert_equals(c:size(), 1)
204+
205+
c:start()
206+
assert_instance_running(c, 'i-001')
207+
208+
local config2 = cbuilder:new()
209+
:use_group('g-001')
210+
:use_replicaset('r-001')
211+
:add_instance('i-002', {})
212+
213+
:use_group('g-002')
214+
:use_replicaset('r-002')
215+
:add_instance('i-003', {})
216+
217+
:config()
218+
219+
local server1 = c['i-001']
220+
221+
c:sync(config2, {start_stop = true})
222+
223+
-- Check that the server that was removed from the config is expelled
224+
-- from the cluster and stopped.
225+
t.assert_equals(c:size(), 2)
226+
t.assert_is(c['i-001'], nil)
227+
t.assert_is(server1.process, nil)
228+
229+
-- Check that the new servers are started.
230+
assert_instance_running(c, 'i-002')
231+
assert_instance_running(c, 'i-003')
232+
233+
-- Check config reload works after sync.
234+
c:reload()
171235
end
172236

173237
g.test_reload = function()

0 commit comments

Comments
 (0)