Skip to content

Commit f732079

Browse files
committed
cluster: allow opt-out from global hooks
This patch lets tests create clusters that are not managed by the preloaded hooks, so you can keep multiple clusters alive between tests or reuse a single long-lived cluster across the whole test suite. Closes #414
1 parent 373fffc commit f732079

File tree

4 files changed

+152
-18
lines changed

4 files changed

+152
-18
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Changelog
22

3+
## Unreleased
4+
5+
- Added an option to create `Cluster` objects without global hook management,
6+
allowing tests to keep clusters alive between test runs (gh-414).
7+
38
## 1.2.1
49

510
- Fixed a bug when `Server:grep_log()` didn't consider the `reset` option.

README.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,39 @@ to server process.
471471
472472
``luatest.Process:start(path, args, env)`` provides low-level interface to run any other application.
473473

474+
``luatest.cluster`` runs a declarative configuration as a set of Tarantool instances.
475+
By default clusters are registered inside the current test group and cleaned up with
476+
preloaded hooks (``auto_cleanup = true``). When you need to reuse the same cluster across
477+
multiple tests or keep several clusters running at once, pass ``auto_cleanup = false`` and
478+
manage the lifecycle manually:
479+
480+
.. code-block:: Lua
481+
482+
local t = require('luatest')
483+
local cluster = require('luatest.cluster')
484+
local cbuilder = require('luatest.cbuilder')
485+
486+
local g = t.group('shared')
487+
488+
g.before_all(function()
489+
local config = cbuilder:new()
490+
:use_group('g-1')
491+
:use_replicaset('rs-1')
492+
:add_instance('instance-1', {})
493+
:config()
494+
495+
g.cluster = cluster:new(config, {}, {auto_cleanup = false})
496+
g.cluster:start()
497+
end)
498+
499+
g.after_all(function()
500+
g.cluster:drop()
501+
end)
502+
503+
g.test_reuses_cluster_between_cases = function()
504+
t.assert_not_equals(g.cluster['instance-1'].process, nil)
505+
end
506+
474507
There are several small helpers for common actions:
475508

476509
.. code-block:: Lua

luatest/cluster.lua

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,12 @@ local Cluster = require('luatest.class').new()
4747
-- Cluster uses custom __index implementation to support
4848
-- getting instances from it using `cluster['i-001']`.
4949
--
50-
-- Thus, we need to change the metatable of the class
51-
-- with a custom __index method.
52-
local mt = getmetatable(Cluster)
50+
-- The metamethod is set on the instance metatable so that multiple
51+
-- cluster objects can co-exist without clobbering shared state on the
52+
-- class table.
53+
local mt = Cluster.mt
5354
mt.__index = function(self, k)
54-
local method = rawget(mt, k)
55+
local method = Cluster[k]
5556
if method ~= nil then
5657
return method
5758
end
@@ -310,17 +311,31 @@ end
310311
-- @tab[opt] server_opts Extra options passed to server:new().
311312
-- @tab[opt] opts Cluster options.
312313
-- @string[opt] opts.dir Specific directory for the cluster.
314+
-- @bool[opt] opts.auto_cleanup Register the cluster in a test group and
315+
-- automatically drop it using hooks (default: true).
313316
-- @return table
314317
function Cluster:new(config, server_opts, opts)
315-
local g = cluster._group
316-
317318
assert(type(config) == 'table')
318319
assert(config._config == nil, "Please provide cbuilder:new():config()")
319-
assert(g._cluster == nil)
320+
321+
opts = opts or {}
322+
local auto_cleanup = opts.auto_cleanup
323+
324+
if auto_cleanup == nil then
325+
auto_cleanup = true
326+
end
327+
328+
assert(type(auto_cleanup) == 'boolean')
329+
330+
local g
331+
if auto_cleanup then
332+
g = cluster._group
333+
assert(g._cluster == nil)
334+
end
320335

321336
-- Prepare a temporary directory and write a configuration
322337
-- file.
323-
local dir = opts and opts.dir or treegen.prepare_directory({}, {})
338+
local dir = opts.dir or treegen.prepare_directory({}, {})
324339
local config_file_rel = 'config.yaml'
325340
local config_file = treegen.write_file(dir, config_file_rel,
326341
yaml.encode(config))
@@ -352,17 +367,20 @@ function Cluster:new(config, server_opts, opts)
352367
server_map[name] = iserver
353368
end
354369

355-
-- Store a cluster object in 'g'.
356-
self._servers = servers
357-
self._server_map = server_map
358-
self._expelled_servers = {}
359-
self._dir = dir
360-
self._config_file_rel = config_file_rel
361-
self._server_opts = server_opts
362-
363-
g._cluster = self
370+
local object = self:from({
371+
_servers = servers,
372+
_server_map = server_map,
373+
_expelled_servers = {},
374+
_dir = dir,
375+
_config_file_rel = config_file_rel,
376+
_server_opts = server_opts,
377+
})
378+
379+
if auto_cleanup then
380+
g._cluster = object
381+
end
364382

365-
return self
383+
return object
366384
end
367385

368386
-- }}} Replicaset management

test/cluster_test.lua

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,34 @@ g.test_start_instance = function()
122122
assert_instance_stopped(c, 'i-002')
123123
end
124124

125+
g.test_manual_lifecycle = function()
126+
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
127+
[[Declarative configuration works on Tarantool 3.0.0+.
128+
See tarantool/tarantool@13149d65bc9d for details]])
129+
130+
local config = cbuilder:new()
131+
:use_group('cluster')
132+
:use_replicaset('cluster-rs')
133+
:add_instance('cluster-1', {})
134+
:config()
135+
136+
local c1 = cluster:new(config, server_opts, {auto_cleanup = false})
137+
138+
t.assert_equals(g._cluster, nil)
139+
140+
c1:start()
141+
assert_instance_running(c1, 'cluster-1')
142+
c1:drop()
143+
144+
local c2 = cluster:new(config, server_opts, {auto_cleanup = false})
145+
146+
t.assert_equals(g._cluster, nil)
147+
148+
c2:start()
149+
assert_instance_running(c2, 'cluster-1')
150+
c2:drop()
151+
end
152+
125153
g.test_sync = function()
126154
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
127155
[[Declarative configuration works on Tarantool 3.0.0+.
@@ -318,3 +346,53 @@ g.test_startup_error = function()
318346

319347
cluster:startup_error(config, 'No such file')
320348
end
349+
350+
local g_persistent_clusters = t.group('persistent_clusters')
351+
352+
g_persistent_clusters.before_all(function()
353+
t.run_only_if(utils.version_current_ge_than(3, 0, 0),
354+
[[Declarative configuration works on Tarantool 3.0.0+.
355+
See tarantool/tarantool@13149d65bc9d for details]])
356+
357+
g_persistent_clusters.instances = {}
358+
g_persistent_clusters.pids = {}
359+
360+
for i = 1, 3 do
361+
local index = tostring(i)
362+
local instance = 'persistent-' .. index .. '-1'
363+
local config = cbuilder:new()
364+
:use_group('persistent-group-' .. index)
365+
:use_replicaset('persistent-rs-' .. index)
366+
:add_instance(instance, {})
367+
:config()
368+
369+
local c = cluster:new(config, server_opts, {auto_cleanup = false})
370+
c:start()
371+
assert_instance_running(c, instance)
372+
373+
g_persistent_clusters.instances[i] = {cluster = c, instance = instance}
374+
g_persistent_clusters.pids[i] = c[instance].process.pid
375+
end
376+
end)
377+
378+
g_persistent_clusters.after_all(function()
379+
for _, cdata in ipairs(g_persistent_clusters.instances or {}) do
380+
cdata.cluster:drop()
381+
end
382+
end)
383+
384+
g_persistent_clusters.test_clusters_survive_between_tests = function()
385+
for _, cdata in ipairs(g_persistent_clusters.instances) do
386+
assert_instance_running(cdata.cluster, cdata.instance)
387+
end
388+
end
389+
390+
g_persistent_clusters.test_clusters_keep_same_process = function()
391+
for i, cdata in ipairs(g_persistent_clusters.instances) do
392+
local server = cdata.cluster[cdata.instance]
393+
394+
t.assert_is_not(server.process, nil)
395+
t.assert_equals(server.process.pid, g_persistent_clusters.pids[i])
396+
assert_instance_running(cdata.cluster, cdata.instance)
397+
end
398+
end

0 commit comments

Comments
 (0)