Skip to content

Commit e4986c5

Browse files
committed
Add the "lazy start" technique to initialization stage
Previously, if an instance was started in read_only mode, it could throw an error when initializing the queue module, like this: "Can't modify data because this instance is in read-only mode". To avoid such behavior the "lazy start" technique has been added: The queue module is loaded immediately if the instance was configured with read_only = false. Otherwise, a start is delayed until the instance will be configured with read_only = false. Closes #122
1 parent b38b46d commit e4986c5

File tree

3 files changed

+104
-18
lines changed

3 files changed

+104
-18
lines changed

queue/init.lua

+70-17
Original file line numberDiff line numberDiff line change
@@ -24,29 +24,82 @@ end
2424
queue = setmetatable({
2525
driver = core_drivers,
2626
register_driver = register_driver,
27-
}, { __index = function() print(debug.traceback()) error("Please run box.cfg{} first") end })
27+
}, { __index = function()
28+
print(debug.traceback())
29+
error('Please configure box.cfg{} in read/write mode first')
30+
end
31+
})
2832

29-
local function queue_init()
30-
if rawget(box, 'space') == nil then
31-
local orig_cfg = box.cfg
32-
box.cfg = function(...)
33-
local result = { orig_cfg(...) }
34-
35-
local abstract = require 'queue.abstract'
36-
for name, val in pairs(abstract) do
37-
rawset(queue, name, val)
38-
end
39-
abstract.driver = queue.driver
40-
setmetatable(queue, getmetatable(abstract))
41-
queue.start()
42-
43-
return unpack(result)
33+
-- Used to store the original methods
34+
local orig_cfg = nil
35+
local orig_call = nil
36+
37+
local wrapper_impl
38+
39+
local function cfg_wrapper(...)
40+
box.cfg = orig_cfg
41+
return wrapper_impl(...)
42+
end
43+
44+
local function cfg_call_wrapper(cfg, ...)
45+
local cfg_mt = getmetatable(box.cfg)
46+
cfg_mt.__call = orig_call
47+
return wrapper_impl(...)
48+
end
49+
50+
local function wrap_box_cfg()
51+
if type(box.cfg) == 'function' then
52+
-- box.cfg before the first box.cfg call
53+
orig_cfg = box.cfg
54+
box.cfg = cfg_wrapper
55+
elseif type(box.cfg) == 'table' then
56+
-- box.cfg after the first box.cfg call
57+
local cfg_mt = getmetatable(box.cfg)
58+
orig_call = cfg_mt.__call
59+
cfg_mt.__call = cfg_call_wrapper
60+
else
61+
error('The box.cfg type is unexpected: ' .. type(box.cfg))
62+
end
63+
end
64+
65+
function wrapper_impl(...)
66+
local result = { pcall(box.cfg,...) }
67+
if result[1] then
68+
table.remove(result, 1)
69+
else
70+
wrap_box_cfg()
71+
error(result[2])
72+
end
73+
74+
if box.info.ro == false then
75+
local abstract = require 'queue.abstract'
76+
for name, val in pairs(abstract) do
77+
rawset(queue, name, val)
4478
end
79+
abstract.driver = queue.driver
80+
setmetatable(queue, getmetatable(abstract))
81+
queue.start()
4582
else
46-
queue = require 'queue.abstract'
83+
-- Delay a start until the box will be configured
84+
-- with read_only = false
85+
wrap_box_cfg()
86+
end
87+
return unpack(result)
88+
end
89+
90+
--- Implementation of the “lazy start” procedure.
91+
-- The queue module is loaded immediately if the instance was
92+
-- configured with read_only = false. Otherwise, a start is
93+
-- delayed until the instance will be configured with read_only = false.
94+
local function queue_init()
95+
if rawget(box, 'space') ~= nil and box.info.ro == false then
96+
-- The box was configured with read_only = false
97+
queue = require('queue.abstract')
4798
queue.register_driver = register_driver
4899
queue.driver = core_drivers
49100
queue.start()
101+
else
102+
wrap_box_cfg()
50103
end
51104
end
52105

t/000-init.t

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ test:test('access to queue until box.cfg is started', function(test)
1515

1616
local s, e = pcall(function() return queue.tube end)
1717
test:ok(not s, 'exception was generated')
18-
test:ok(string.match(e, 'Please run box.cfg') ~= nil, 'Exception text')
18+
test:ok(string.match(e, 'Please configure box.cfg') ~= nil, 'Exception text')
1919
end)
2020

2121
local state = require('queue.abstract.state')

t/150-lazy-start.t

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env tarantool
2+
local tap = require('tap')
3+
local tnt = require('t.tnt')
4+
5+
local test = tap.test('test driver register')
6+
test:plan(3)
7+
8+
local function check_lazy_start()
9+
-- Needed for bootstrap
10+
tnt.cfg{}
11+
12+
tnt.cfg{read_only = true}
13+
local queue = require('queue')
14+
15+
local err_msg = 'Please configure box.cfg{} in read/write mode first'
16+
local res, err = pcall(function() queue.stats() end)
17+
local check = not res and string.match(err,err_msg) ~= nil
18+
test:ok(check, 'check queue delayed start')
19+
20+
tnt.cfg({read_only = true})
21+
res, err = pcall(function() queue.stats() end)
22+
check = not res and string.match(err, err_msg) ~= nil
23+
test:ok(check, 'check box reconfiguration with read_only = true')
24+
25+
tnt.cfg({read_only = false})
26+
res = pcall(function() queue.stats() end)
27+
test:ok(res, 'queue has been started')
28+
end
29+
30+
check_lazy_start()
31+
32+
tnt.finish()
33+
os.exit(test:check() and 0 or 1)

0 commit comments

Comments
 (0)