Skip to content

Commit

Permalink
roles: introduce router configuration
Browse files Browse the repository at this point in the history
This patch introduces `roles.crud-router` role configuration through
`roles_cfg`, similar to existing Cartridge clusterwide configuration
support. For now, storages don't have any configuration, so they remain
unchanged.

After this patch, Tarantool 3 roles have all features supported in
Cartridge roles.

Closes #415
  • Loading branch information
DifferentialOrange committed Mar 29, 2024
1 parent f0f2560 commit eb14f0e
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 25 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
### Added
* Asynchronous bootstrap support for storages (#412).
* Tarantool 3 roles for setting up crud routers and storages (#415).
* Ability to configure crud through Tarantool 3 roles configuration (#415).

### Changed
* Explicitly forbid datetime interval conditions (#373).
Expand Down
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2102,6 +2102,21 @@ issues.
5. Start the application cluster. You can check whether asynchronous bootstrap
had finished through `crud.storage_info()` calls on router.

6. Configure the statistics with roles configuration
(see `crud.cfg` options in [statistics](#statistics) section):
```yaml
roles:
- roles.crud-router
roles_cfg:
roles.crud-router:
stats: true
stats_driver: metrics
stats_quantiles: false
stats_quantile_tolerated_error: 0.001
stats_quantile_age_buckets_count: 5
stats_quantile_max_age_time: 180
```

Now your cluster contains storages that are configured to be used for
CRUD-operations.
You can simply call CRUD functions on the router to insert, select, and update
Expand Down
61 changes: 59 additions & 2 deletions roles/crud-router.lua
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ local errors = require('errors')
local crud = require('crud')
local common_role_utils = require('crud.common.roles')
local common_utils = require('crud.common.utils')
local stats = require('crud.stats')

local TarantoolRoleConfigurationError = errors.new_class('TarantoolRoleConfigurationError')

Expand All @@ -12,15 +13,71 @@ TarantoolRoleConfigurationError:assert(
('Tarantool 3 role is not supported for Tarantool %s, use 3.0.2 or newer'):format(tarantool_version)
)

local function validate()

local function validate_enabled_on_sharding_router()
TarantoolRoleConfigurationError:assert(
common_role_utils.is_sharding_role_enabled('router'),
'Instance must be a sharding router to enable roles.crud-router'
)
end

local function apply()
local cfg_types = {
stats = 'boolean',
stats_driver = 'string',
stats_quantiles = 'boolean',
stats_quantile_tolerated_error = 'number',
stats_quantile_age_buckets_count = 'number',
stats_quantile_max_age_time = 'number',
}

local cfg_values = {
stats_driver = function(value)
TarantoolRoleConfigurationError:assert(
stats.is_driver_supported(value),
'Invalid "stats_driver" field value: %q is not supported',
value
)
end,
}

local function validate_roles_cfg(roles_cfg)
if roles_cfg == nil then
return
end

TarantoolRoleConfigurationError:assert(
type(roles_cfg) == 'table',
'roles_cfg must be a table'
)

for name, value in pairs(roles_cfg) do
TarantoolRoleConfigurationError:assert(
cfg_types[name] ~= nil,
'Unknown field %q', name
)

TarantoolRoleConfigurationError:assert(
type(value) == cfg_types[name],
'Invalid %q field type: expected %s, got %s',
name, cfg_types[name], type(value)
)

if cfg_values[name] ~= nil then
cfg_values[name](value)
end
end
end

local function validate(roles_cfg)
validate_enabled_on_sharding_router()

validate_roles_cfg(roles_cfg)
end

local function apply(roles_cfg)
crud.init_router()

crud.cfg(roles_cfg)
end

local function stop()
Expand Down
4 changes: 4 additions & 0 deletions test/helper.lua
Original file line number Diff line number Diff line change
Expand Up @@ -1468,4 +1468,8 @@ function helpers.skip_if_tarantool3_crud_roles_unsupported()
("Tarantool %s does not support crud roles"):format(version))
end

function helpers.skip_not_config_backend(backend)
t.skip_if(backend ~= helpers.backend.CONFIG, "The test is for Tarantool 3 with config only")
end

return helpers
51 changes: 39 additions & 12 deletions test/integration/cfg_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -180,32 +180,59 @@ end

local role_cfg_error_cases = {
wrong_section_type = {
args = {crud = 'enabled'},
err = 'Configuration \\\"crud\\\" section must be a table',
args = 'enabled',
err_cartridge = 'Configuration \\\"crud\\\" section must be a table',
err_tarantool3 = 'Wrong config for role roles.crud-router: TarantoolRoleConfigurationError: '..
'roles_cfg must be a table',
},
wrong_structure = {
args = {crud = {crud = {stats = true}}},
err = '\\\"crud\\\" section is already presented as a name of \\\"crud.yml\\\", ' ..
'do not use it as a top-level section name',
args = {crud = {stats = true}},
err_cartridge = '\\\"crud\\\" section is already presented as a name of \\\"crud.yml\\\", ' ..
'do not use it as a top-level section name',
err_tarantool3 = 'Wrong config for role roles.crud-router: TarantoolRoleConfigurationError: '..
'Unknown field \"crud\"',
},
wrong_type = {
args = {crud = {stats = 'enabled'}},
err = 'Invalid crud configuration field \\\"stats\\\" type: expected boolean, got string',
args = {stats = 'enabled'},
err_cartridge = 'Invalid crud configuration field \\\"stats\\\" type: expected boolean, got string',
err_tarantool3 = 'Wrong config for role roles.crud-router: TarantoolRoleConfigurationError: '..
'Invalid \"stats\" field type: expected boolean, got string',
},
wrong_value = {
args = {crud = {stats_driver = 'prometheus'}},
err = 'Invalid crud configuration field \\\"stats_driver\\\" value: \\\"prometheus\\\" is not supported',
args = {stats_driver = 'prometheus'},
err_cartridge = 'Invalid crud configuration field \\\"stats_driver\\\" value: '..
'\\\"prometheus\\\" is not supported',
err_tarantool3 = 'Wrong config for role roles.crud-router: TarantoolRoleConfigurationError: '..
'Invalid \"stats_driver\" field value: \"prometheus\" is not supported',
}
}

for name, case in pairs(role_cfg_error_cases) do
group['test_role_cfg_' .. name] = function(g)
group['test_cartridge_role_cfg_' .. name] = function(g)
helpers.skip_not_cartridge_backend(g.params.backend)
local success, error = pcall(function()
g.router:upload_config(case.args)
g.router:upload_config({
crud = case.args,
})
end)

t.assert_equals(success, false)
t.assert_str_contains(error.response.body, case.err)
t.assert_str_contains(error.response.body, case.err_cartridge)
end

group['test_tarantool3_role_cfg_' .. name] = function(g)
helpers.skip_not_config_backend(g.params.backend)
local success, error = pcall(function()
local cfg = g.cluster:cfg()

cfg.groups['routers'].roles_cfg = {
['roles.crud-router'] = case.args,
}

g.cluster:reload_config(cfg)
end)

t.assert_equals(success, false)
t.assert_str_contains(tostring(error), case.err_tarantool3)
end
end
34 changes: 24 additions & 10 deletions test/integration/stats_test.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,19 @@ local matrix = helpers.backend_matrix({
{ way = 'call', args = { driver = 'metrics', quantiles = false }},
{ way = 'call', args = { driver = 'metrics', quantiles = true }},
})
table.insert(matrix, {backend = helpers.backend.CARTRIDGE,
way = 'role', args = { driver = 'local' },
})
table.insert(matrix, {backend = helpers.backend.CARTRIDGE,
way = 'role', args = { driver = 'metrics', quantiles = false },
})
table.insert(matrix, {backend = helpers.backend.CARTRIDGE,
way = 'role', args = { driver = 'metrics', quantiles = true },
})

for _, backend in ipairs({helpers.backend.CARTRIDGE, helpers.backend.CONFIG}) do
table.insert(matrix, {backend = backend,
way = 'role', args = { driver = 'local' },
})
table.insert(matrix, {backend = backend,
way = 'role', args = { driver = 'metrics', quantiles = false },
})
table.insert(matrix, {backend = backend,
way = 'role', args = { driver = 'metrics', quantiles = true },
})
end

local pgroup = t.group('stats_integration', matrix)

matrix = helpers.backend_matrix({
Expand Down Expand Up @@ -57,7 +61,17 @@ local call_cfg = function(g, way, cfg)
require('crud').cfg(...)
]], { cfg })
elseif way == 'role' then
g.router:upload_config{crud = cfg}
if g.params.backend == helpers.backend.CARTRIDGE then
g.router:upload_config{crud = cfg}
elseif g.params.backend == helpers.backend.CONFIG then
local cluster_cfg = g.cluster:cfg()

cluster_cfg.groups['routers'].roles_cfg = {
['roles.crud-router'] = cfg,
}

g.cluster:reload_config(cluster_cfg)
end
end
end

Expand Down
27 changes: 26 additions & 1 deletion test/tarantool3_helpers/cluster.lua
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,33 @@ function Cluster:cfg(new_config)
return table.deepcopy(self.config)
end

local function strip_all_entries(t, name)
if type(t) ~= 'table' then
return t
end

t[name] = nil

for k, v in pairs(t) do
t[k] = strip_all_entries(v, name)
end

return t
end

local function check_only_roles_cfg_changed_in_groups(old_groups, new_groups)
old_groups = table.deepcopy(old_groups)
new_groups = table.deepcopy(new_groups)

local old_groups_no_roles_cfg = strip_all_entries(old_groups, 'roles_cfg')
local new_groups_no_roles_cfg = strip_all_entries(new_groups, 'roles_cfg')

t.assert_equals(new_groups_no_roles_cfg, old_groups_no_roles_cfg,
'groups reload supports only roles_cfg reload')
end

function Cluster:reload_config(new_config)
t.assert_equals(new_config.groups, self.config.groups, 'groups reload is not supported yet')
check_only_roles_cfg_changed_in_groups(self.config.groups, new_config.groups)

for _, server in ipairs(self.servers) do
write_config(self.dirs[server], new_config)
Expand Down

0 comments on commit eb14f0e

Please sign in to comment.