Skip to content

Commit d39ab75

Browse files
a1div0vakhov
authored andcommitted
[TNTP-2177] Role-based model support in CRUD for single operations is provided
(cherry picked from commit 800d614aab4b49a7d2115d27d0836483dc9429f4)
1 parent 0f62c1e commit d39ab75

27 files changed

+863
-40
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
66
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
77

88
## [Unreleased]
9+
* Обеспечена поддержка ролевой модели в CRUD (https://jira.vk.team/browse/TNTP-2177)
910

1011
### Fixed
1112
* `crud.schema` no longer returns TCF system space `_cdc_state`.

Makefile

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
SHELL := /bin/bash
2+
3+
S3_TARANTOOL_SDK_3_PATH := s3://packages/enterprise/release/linux/x86_64/3.3/tarantool-enterprise-sdk-gc64-3.3.1-0-r55.linux.x86_64.tar.gz
4+
S3_TARANTOOL_SDK_2_PATH := s3://packages/enterprise/release/linux/x86_64/2.11/tarantool-enterprise-sdk-gc64-2.11.6-0-r677.linux.x86_64.tar.gz
5+
S3_ENDPOINT_URL := $(if $(S3_ENDPOINT_URL),$(S3_ENDPOINT_URL),https://hb.vkcs.cloud)
6+
7+
SDK_TEST := $(if $(SDK_TEST),$(SDK_TEST),sdk-3)
8+
9+
.rocks: sdk
10+
# в sdk-2 есть все нужные роки, в sdk-3 нет
11+
source ./sdk-2/env.sh && \
12+
tt rocks install luacheck 0.26.0 --only-server=sdk-2/rocks && \
13+
tt rocks install luacov 0.13.0 --only-server=sdk-2/rocks && \
14+
tt rocks install luacov-reporters 0.1.0 --only-server=sdk-2/rocks && \
15+
tt rocks install metrics 1.1.0 --only-server=sdk-2/rocks && \
16+
tt rocks install ddl-ee 1.8.0 --only-server=sdk-2/rocks && \
17+
tt rocks install cartridge 2.15.1 --only-server=sdk-2/rocks && \
18+
tt rocks install migrations-ee 1.3.1 --only-server=sdk-2/rocks && \
19+
tt rocks make
20+
21+
sdk-2:
22+
aws --endpoint-url "$(S3_ENDPOINT_URL)" s3 cp "$(S3_TARANTOOL_SDK_2_PATH)" .
23+
mkdir sdk-2 && tar -xvzf tarantool-enterprise-*.tar.gz -C ./sdk-2 --strip-components=1 && rm tarantool-enterprise-*.tar.gz
24+
25+
sdk-3:
26+
aws --endpoint-url "$(S3_ENDPOINT_URL)" s3 cp "$(S3_TARANTOOL_SDK_3_PATH)" .
27+
mkdir sdk-3 && tar -xvzf tarantool-enterprise-*.tar.gz -C ./sdk-3 --strip-components=1 && rm tarantool-enterprise-*.tar.gz
28+
29+
sdk: sdk-2 sdk-3
30+
# в sdk-3 нет luatest
31+
source sdk-3/env.sh && \
32+
cp sdk-2/rocks/luatest-1.0.1-1.all.rock sdk-3/rocks/ && \
33+
chmod 644 sdk-3/rocks/* && \
34+
tt rocks make_manifest sdk-3/rocks
35+
36+
lint: .rocks
37+
source sdk-2/env.sh && .rocks/bin/luacheck .
38+
39+
.PHONY: test
40+
test:
41+
@echo "RUN TESTS WITH $(SDK_TEST)"
42+
# luatest будет свой для каждого sdk
43+
source $(SDK_TEST)/env.sh && \
44+
tt rocks install luatest 1.0.1 --only-server=$(SDK_TEST)/rocks && \
45+
.rocks/bin/luatest -v --coverage test/
46+
47+
coverage:
48+
source sdk-2/env.sh && ./.rocks/bin/luacov -r summary && cat luacov.report.out

crud/cfg.lua

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ local function __call(self, opts)
157157
})
158158
-- Value validation would be performed in stats checks, if required.
159159

160-
opts = table.deepcopy(opts) or {}
160+
opts = table.deepcopy(opts or {})
161161
-- opts from Cartridge clusterwide configuration is read-only,
162162
-- but we want to work with copy anyway.
163163
setmetatable(opts, {})

crud/common/call.lua

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
local errors = require('errors')
22

3+
local call_cache = require('crud.common.call_cache')
34
local dev_checks = require('crud.common.dev_checks')
45
local utils = require('crud.common.utils')
56
local sharding_utils = require('crud.common.sharding.utils')
@@ -11,8 +12,18 @@ local BasePostprocessor = require('crud.common.map_call_cases.base_postprocessor
1112

1213
local CallError = errors.new_class('CallError')
1314

15+
local CALL_FUNC_NAME = 'call_on_storage'
16+
local CRUD_CALL_FUNC_NAME = utils.get_storage_call(CALL_FUNC_NAME)
17+
18+
1419
local call = {}
1520

21+
local function call_on_storage(run_as_user, func_name, ...)
22+
return box.session.su(run_as_user, call_cache.func_name_to_func(func_name), ...)
23+
end
24+
25+
call.storage_api = {[CALL_FUNC_NAME] = call_on_storage}
26+
1627
function call.get_vshard_call_name(mode, prefer_replica, balance)
1728
dev_checks('string', '?boolean', '?boolean')
1829

@@ -71,11 +82,12 @@ local function wrap_vshard_err(vshard_router, err, func_name, replicaset_id, buc
7182
))
7283
end
7384

74-
local function retry_call_with_master_discovery(replicaset, method, ...)
85+
local function retry_call_with_master_discovery(replicaset, method, func_name, func_args, call_opts)
86+
local func_args_ext = utils.append_array({ box.session.effective_user(), func_name }, func_args)
87+
7588
-- In case cluster was just bootstrapped with auto master discovery,
7689
-- replicaset may miss master.
77-
78-
local resp, err = replicaset[method](replicaset, ...)
90+
local resp, err = replicaset[method](replicaset, CRUD_CALL_FUNC_NAME, func_args_ext, call_opts)
7991

8092
if err == nil then
8193
return resp, err
@@ -87,7 +99,7 @@ local function retry_call_with_master_discovery(replicaset, method, ...)
8799

88100
-- Retry only once: should be enough for initial discovery,
89101
-- otherwise force user fix up cluster bootstrap.
90-
return replicaset[method](replicaset, ...)
102+
return replicaset[method](replicaset, CRUD_CALL_FUNC_NAME, func_args_ext, call_opts)
91103
end
92104

93105
function call.map(vshard_router, func_name, func_args, opts)

crud/common/call_cache.lua

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
local func_name_to_func_cache = {}
2+
3+
local function func_name_to_func(func_name)
4+
if func_name_to_func_cache[func_name] then
5+
return func_name_to_func_cache[func_name]
6+
end
7+
8+
local current = _G
9+
for part in string.gmatch(func_name, "[^%.]+") do
10+
current = rawget(current, part)
11+
if current == nil then
12+
error(("Function '%s' is not registered"):format(func_name))
13+
end
14+
end
15+
16+
if type(current) ~= "function" then
17+
error(func_name .. " is not a function")
18+
end
19+
20+
func_name_to_func_cache[func_name] = current
21+
return current
22+
end
23+
24+
local function reset()
25+
func_name_to_func_cache = {}
26+
end
27+
28+
return {
29+
func_name_to_func = func_name_to_func,
30+
reset = reset,
31+
}

crud/common/schema.lua

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -217,15 +217,16 @@ function schema.wrap_func_result(space, func, args, opts)
217217
opts = opts or {}
218218

219219
local ok, func_res = pcall(func, unpack(args))
220-
if not ok then
221-
result.err = func_res
222-
if opts.add_space_schema_hash then
223-
result.space_schema_hash = get_space_schema_hash(space)
224-
end
225-
else
220+
if ok then
226221
if opts.noreturn ~= true then
227222
result.res = filter_tuple_fields(func_res, opts.field_names)
228223
end
224+
else
225+
result.err = func_res
226+
if opts.add_space_schema_hash then
227+
local _, space_schema_hash = pcall(get_space_schema_hash, space)
228+
result.space_schema_hash = space_schema_hash
229+
end
229230
end
230231

231232
if opts.fetch_latest_metadata == true then

crud/common/utils.lua

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1166,18 +1166,22 @@ function utils.update_storage_call_error_description(err, func_name, replicaset_
11661166
return nil
11671167
end
11681168

1169-
if (err.type == 'ClientError' or err.type == 'AccessDeniedError')
1169+
if (err.type == 'ClientError' or err.type == 'AccessDeniedError' or err.type == 'LuajitError')
11701170
and type(err.message) == 'string' then
11711171
local not_defined_str = string.format("Procedure '%s' is not defined", func_name)
1172+
local not_registered_str = string.format("Function '%s' is not registered", func_name)
11721173
local access_denied_str = string.format("Execute access to function '%s' is denied", func_name)
1173-
if err.message == not_defined_str or err.message:startswith(access_denied_str) then
1174+
if err.message == not_defined_str or err.message:startswith(access_denied_str)
1175+
or err.message:find(not_registered_str)
1176+
or err.message == "Procedure '_crud.call_on_storage' is not defined"
1177+
or err.message:startswith("Execute access to function '_crud.call_on_storage' is denied") then
11741178
if func_name:startswith('_crud.') then
1175-
err = NotInitializedError:new("Function %s is not registered: " ..
1179+
err = NotInitializedError:new("Function '%s' is not registered: " ..
11761180
"crud isn't initialized on replicaset %q or crud module versions mismatch " ..
11771181
"between router and storage",
11781182
func_name, replicaset_id or "Unknown")
11791183
else
1180-
err = NotInitializedError:new("Function %s is not registered", func_name)
1184+
err = NotInitializedError:new("Function '%s' is not registered", func_name)
11811185
end
11821186
end
11831187
end
@@ -1324,4 +1328,14 @@ for k, v in pairs(require('crud.common.vshard_utils')) do
13241328
utils[k] = v
13251329
end
13261330

1331+
function utils.append_array(array_src, array_dst)
1332+
if not array_dst then
1333+
return array_src
1334+
end
1335+
1336+
table.move(array_dst, 1, #array_dst, #array_src + 1, array_src)
1337+
1338+
return array_src
1339+
end
1340+
13271341
return utils

crud/count.lua

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,21 +51,21 @@ local function count_on_storage(space_name, index_id, conditions, opts)
5151
end
5252

5353
local value = opts.scan_value
54-
55-
local filter_func, err = filters.gen_func(space, index, conditions, {
56-
tarantool_iter = opts.tarantool_iter,
57-
scan_condition_num = opts.scan_condition_num,
58-
})
59-
if err ~= nil then
60-
return nil, CountError:new("Failed to generate tuples filter: %s", err)
61-
end
62-
54+
local filter_func
6355
local tuples_count = 0
6456
local looked_up_tuples = 0
6557

6658
for _, tuple in index:pairs(value, {iterator = opts.tarantool_iter}) do
6759
if tuple == nil then
6860
break
61+
elseif not filter_func then
62+
filter_func, err = filters.gen_func(space, index, conditions, {
63+
tarantool_iter = opts.tarantool_iter,
64+
scan_condition_num = opts.scan_condition_num,
65+
})
66+
if err ~= nil then
67+
return nil, CountError:new("Failed to generate tuples filter: %s", err)
68+
end
6969
end
7070

7171
looked_up_tuples = looked_up_tuples + 1

crud/len.lua

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
local checks = require('checks')
22
local errors = require('errors')
33

4+
local call = require('crud.common.call')
45
local utils = require('crud.common.utils')
56
local dev_checks = require('crud.common.dev_checks')
67

@@ -60,7 +61,10 @@ function len.call(space_name, opts)
6061
return nil, LenError:new("Space %q doesn't exist", space_name)
6162
end
6263

63-
local results, err = vshard_router:map_callrw(CRUD_LEN_FUNC_NAME, {space_name}, opts)
64+
local results, err = call.map(vshard_router, CRUD_LEN_FUNC_NAME, {space_name}, {
65+
mode = 'write',
66+
timeout = opts.timeout,
67+
})
6468

6569
if err ~= nil then
6670
return nil, LenError:new("Failed to call len on storage-side: %s", err)

crud/readview.lua

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ local fiber = require('fiber')
22
local checks = require('checks')
33
local errors = require('errors')
44

5+
local call = require('crud.common.call')
56
local const = require('crud.common.const')
67
local stash = require('crud.common.stash')
78
local utils = require('crud.common.utils')
@@ -334,8 +335,15 @@ function Readview_obj.create(vshard_router, opts)
334335
setmetatable(readview, Readview_obj)
335336

336337
readview._name = opts.name
337-
local results, err, err_id = vshard_router:map_callrw(CRUD_OPEN_FUNC_NAME,
338-
{readview._name}, {timeout = opts.timeout})
338+
local results, err, err_id = call.map(
339+
vshard_router,
340+
CRUD_OPEN_FUNC_NAME,
341+
{readview._name},
342+
{
343+
mode = "write",
344+
timeout = opts.timeout,
345+
}
346+
)
339347
if err ~= nil then
340348
return nil, ReadviewError:new(
341349
"Failed to call readview_open_on_storage on storage-side: storage id: %s err: %s",

0 commit comments

Comments
 (0)