Skip to content

Commit fd2f14b

Browse files
storage: support async bootstrap
Support asynchronous bootstrap for crud storages. The main motivation is compatibility with Tarantool 3 instances, which start in read-only mode. We do not support async bootstrap for crud routers since they do not change `box`, thus may start on read-only instances without any issues. Part of #412 Part of #415
1 parent 482ea31 commit fd2f14b

File tree

7 files changed

+148
-5
lines changed

7 files changed

+148
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
77

88
## Unreleased
99

10+
### Added
11+
* Asynchronous bootstrap support for storages (#412).
12+
1013
### Changed
1114
* Explicitly forbid datetime interval conditions (#373).
1215

README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,11 @@ across the cluster. The storage-side functions have the same access
9090
as a user calling `crud.init_storage()`. Therefore, if `crud` do not have
9191
enough access to modify some space, then you need to give access to the user.
9292

93+
You can call `crud.init_storage{async = true}` to bootstrap procedures grants
94+
asynchronously. It is useful in case your application master instances may
95+
start in ro mode (for example, if you use Tarantool 3.x). By default,
96+
synchronous bootstrap is used.
97+
9398
All VShard routers should call `crud.init_router()` after `vshard.router.cfg()`
9499
(or enable the `crud-router` role for Cartridge) to make `crud` functions
95100
callable via `net.box`. If a user is allowed to execute `crud` functions on

crud/common/stash.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,9 @@ local stash = {}
2323
-- @tfield string select_module_compat_info
2424
-- Stash for select compatability version registry.
2525
--
26+
-- @tfield string storage_init
27+
-- Stash for storage initializing tools.
28+
--
2629
stash.name = {
2730
cfg = '__crud_cfg',
2831
stats_internal = '__crud_stats_internal',
@@ -31,6 +34,7 @@ stash.name = {
3134
ddl_triggers = '__crud_ddl_spaces_triggers',
3235
select_module_compat_info = '__select_module_compat_info',
3336
storage_readview = '__crud_storage_readview',
37+
storage_init = '__crud_storage_init',
3438
}
3539

3640
--- Setup Tarantool Cartridge reload.

crud/common/utils.lua

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -683,6 +683,10 @@ local function determine_enabled_features()
683683

684684
enabled_tarantool_features.netbox_skip_header_option = is_version_ge(major, minor, patch, suffix,
685685
2, 2, 0, nil)
686+
687+
-- https://github.com/tarantool/tarantool/commit/11f2d999a92e45ee41b8c8d0014d8a09290fef7b
688+
enabled_tarantool_features.box_watch = is_version_ge(major, minor, patch, suffix,
689+
2, 10, 0, 'beta2')
686690
end
687691

688692
determine_enabled_features()

crud/storage.lua

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1+
local checks = require('checks')
2+
13
local dev_checks = require('crud.common.dev_checks')
4+
local stash = require('crud.common.stash')
25
local utils = require('crud.common.utils')
36

47
local sharding_metadata = require('crud.common.sharding.sharding_metadata')
@@ -21,6 +24,8 @@ local storage_info = require('crud.storage_info')
2124

2225
local storage = {}
2326

27+
local internal_stash = stash.get(stash.name.storage_init)
28+
2429
local function init_local_part(_, name, func)
2530
rawset(_G[utils.STORAGE_NAMESPACE], name, func)
2631
end
@@ -77,11 +82,7 @@ local modules_with_storage_api = {
7782
storage_info,
7883
}
7984

80-
function storage.init()
81-
if type(box.cfg) ~= 'table' then
82-
error('box.cfg() must be called first')
83-
end
84-
85+
local function init_impl()
8586
rawset(_G, utils.STORAGE_NAMESPACE, {})
8687

8788
-- User is required only for persistent part of the init.
@@ -98,7 +99,40 @@ function storage.init()
9899
end
99100
end
100101

102+
function storage.init(opts)
103+
checks({async = '?boolean'})
104+
105+
opts = opts or {}
106+
107+
if opts.async == nil then
108+
opts.async = false
109+
end
110+
111+
if type(box.cfg) ~= 'table' then
112+
error('box.cfg() must be called first')
113+
end
114+
115+
if internal_stash.watcher ~= nil then
116+
internal_stash.watcher:unregister()
117+
internal_stash.watcher = nil
118+
end
119+
120+
if opts.async then
121+
assert(utils.tarantool_supports_box_watch(),
122+
'async start is supported only for Tarantool versions with box.watch support')
123+
124+
internal_stash.watcher = box.watch('box.status', init_impl)
125+
else
126+
init_impl()
127+
end
128+
end
129+
101130
function storage.stop()
131+
if internal_stash.watcher ~= nil then
132+
internal_stash.watcher:unregister()
133+
internal_stash.watcher = nil
134+
end
135+
102136
rawset(_G, utils.STORAGE_NAMESPACE, nil)
103137
end
104138

test/helper.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1254,4 +1254,16 @@ function helpers.wait_schema_init()
12541254
return helpers.wait_func_flag(helpers.SCHEMA_READY_FLAG)
12551255
end
12561256

1257+
function helpers.is_box_watch_supported()
1258+
return crud_utils.tarantool_supports_box_watch()
1259+
end
1260+
1261+
function helpers.skip_if_box_watch_unsupported()
1262+
t.skip_if(not helpers.is_box_watch_supported(), 'box.watch is not supported')
1263+
end
1264+
1265+
function helpers.skip_if_box_watch_supported()
1266+
t.skip_if(helpers.is_box_watch_supported(), 'box.watch is supported')
1267+
end
1268+
12571269
return helpers
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
local t = require('luatest')
2+
3+
local helpers = require('test.helper')
4+
local vtest = require('test.vshard_helpers.vtest')
5+
6+
local g = t.group('async_bootstrap')
7+
8+
local function prepare_clean_cluster(cg)
9+
local cfg = {
10+
sharding = {
11+
['s-1'] = {
12+
replicas = {
13+
['s1-master'] = {
14+
instance_uuid = helpers.uuid('b', 1),
15+
master = true,
16+
},
17+
},
18+
},
19+
},
20+
bucket_count = 3000,
21+
storage_entrypoint = nil,
22+
router_entrypoint = nil,
23+
all_entrypoint = nil,
24+
crud_init = false,
25+
}
26+
27+
cg.cfg = vtest.config_new(cfg)
28+
vtest.cluster_new(cg, cg.cfg)
29+
end
30+
31+
32+
g.test_async_storage_bootstrap = function(cg)
33+
helpers.skip_if_box_watch_unsupported()
34+
35+
-- Prepare a clean vshard cluster with 1 router and 1 storage.
36+
prepare_clean_cluster(cg)
37+
38+
-- Sync bootstrap router.
39+
cg.cluster:server('router'):exec(function()
40+
require('crud').init_router()
41+
end)
42+
43+
-- Async bootstrap storage.
44+
cg.cluster:server('s1-master'):exec(function()
45+
require('crud').init_storage{async = true}
46+
end)
47+
48+
-- Assert storage is ready after some time.
49+
cg.router = cg.cluster:server('router')
50+
helpers.wait_crud_is_ready_on_cluster(cg, {backend = helpers.backend.VSHARD})
51+
end
52+
53+
g.after_test('test_async_storage_bootstrap', function(cg)
54+
if cg.cluster ~= nil then
55+
cg.cluster:drop()
56+
end
57+
end)
58+
59+
60+
g.test_async_storage_bootstrap_unsupported = function(cg)
61+
helpers.skip_if_box_watch_supported()
62+
63+
-- Prepare a clean vshard cluster with 1 router and 1 storage.
64+
prepare_clean_cluster(cg)
65+
66+
-- Async bootstrap storage (fails).
67+
cg.cluster:server('s1-master'):exec(function()
68+
t.assert_error_msg_contains(
69+
'async start is supported only for Tarantool versions with box.watch support',
70+
function()
71+
require('crud').init_storage{async = true}
72+
end
73+
)
74+
end)
75+
end
76+
77+
g.after_test('test_async_storage_bootstrap_unsupported', function(cg)
78+
if cg.cluster ~= nil then
79+
cg.cluster:drop()
80+
end
81+
end)

0 commit comments

Comments
 (0)