diff --git a/queue/init.lua b/queue/init.lua index fff428c..beeae31 100644 --- a/queue/init.lua +++ b/queue/init.lua @@ -24,13 +24,54 @@ end queue = setmetatable({ driver = core_drivers, register_driver = register_driver, -}, { __index = function() print(debug.traceback()) error("Please run box.cfg{} first") end }) +}, { __index = function() + print(debug.traceback()) + error('Please configure box.cfg{} in read/write mode first') + end +}) + +-- Used to store the original methods +local orig_cfg = nil +local orig_call = nil + +local wrapper_impl + +local function cfg_wrapper(...) + box.cfg = orig_cfg + return wrapper_impl(...) +end -if rawget(box, 'space') == nil then - local orig_cfg = box.cfg - box.cfg = function(...) - local result = { orig_cfg(...) } +local function cfg_call_wrapper(cfg, ...) + local cfg_mt = getmetatable(box.cfg) + cfg_mt.__call = orig_call + return wrapper_impl(...) +end +local function wrap_box_cfg() + if type(box.cfg) == 'function' then + -- box.cfg before the first box.cfg call + orig_cfg = box.cfg + box.cfg = cfg_wrapper + elseif type(box.cfg) == 'table' then + -- box.cfg after the first box.cfg call + local cfg_mt = getmetatable(box.cfg) + orig_call = cfg_mt.__call + cfg_mt.__call = cfg_call_wrapper + else + error('The box.cfg type is unexpected: ' .. type(box.cfg)) + end +end + +function wrapper_impl(...) + local result = { pcall(box.cfg,...) } + if result[1] then + table.remove(result, 1) + else + wrap_box_cfg() + error(result[2]) + end + + if box.info.ro == false then local abstract = require 'queue.abstract' for name, val in pairs(abstract) do rawset(queue, name, val) @@ -38,14 +79,30 @@ if rawget(box, 'space') == nil then abstract.driver = queue.driver setmetatable(queue, getmetatable(abstract)) queue.start() + else + -- Delay a start until the box will be configured + -- with read_only = false + wrap_box_cfg() + end + return unpack(result) +end - return unpack(result) +--- Implementation of the “lazy start” procedure. +-- 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. +local function queue_init() + if rawget(box, 'space') ~= nil and box.info.ro == false then + -- The box was configured with read_only = false + queue = require('queue.abstract') + queue.register_driver = register_driver + queue.driver = core_drivers + queue.start() + else + wrap_box_cfg() end -else - queue = require 'queue.abstract' - queue.register_driver = register_driver - queue.driver = core_drivers - queue.start() end +queue_init() + return queue diff --git a/t/000-init.t b/t/000-init.t index d988671..ee7b9e2 100755 --- a/t/000-init.t +++ b/t/000-init.t @@ -15,7 +15,7 @@ test:test('access to queue until box.cfg is started', function(test) local s, e = pcall(function() return queue.tube end) test:ok(not s, 'exception was generated') - test:ok(string.match(e, 'Please run box.cfg') ~= nil, 'Exception text') + test:ok(string.match(e, 'Please configure box.cfg') ~= nil, 'Exception text') end) local state = require('queue.abstract.state') diff --git a/t/150-lazy-start.t b/t/150-lazy-start.t new file mode 100755 index 0000000..1d27dc4 --- /dev/null +++ b/t/150-lazy-start.t @@ -0,0 +1,33 @@ +#!/usr/bin/env tarantool +local tap = require('tap') +local tnt = require('t.tnt') + +local test = tap.test('test driver register') +test:plan(3) + +local function check_lazy_start() + -- Needed for bootstrap + tnt.cfg{} + + tnt.cfg{read_only = true} + local queue = require('queue') + + local err_msg = 'Please configure box.cfg{} in read/write mode first' + local res, err = pcall(function() queue.stats() end) + local check = not res and string.match(err,err_msg) ~= nil + test:ok(check, 'check queue delayed start') + + tnt.cfg({read_only = true}) + res, err = pcall(function() queue.stats() end) + check = not res and string.match(err, err_msg) ~= nil + test:ok(check, 'check box reconfiguration with read_only = true') + + tnt.cfg({read_only = false}) + res = pcall(function() queue.stats() end) + test:ok(res, 'queue has been started') +end + +check_lazy_start() + +tnt.finish() +os.exit(test:check() and 0 or 1)