Skip to content

Commit bea9a83

Browse files
authored
Expose the crud module in global scope (#58)
* Get rid of performing map-reduce for single-replicaset operations (introduced `call.rw_single`) * Added `crud-router` Cartridge role * CRUD-router functions are exposed to the global scope, so it's possible to call crud-operations via `net.box.call` * `crud.init` is removed in favor to `crud.init_storage` and `crud.init_router`
1 parent aaf4106 commit bea9a83

27 files changed

+313
-877
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,6 @@ build.luarocks/
33
myapp/
44
**.snap
55
**.xlog
6+
.tarantool.cookie
7+
CMakeCache.txt
8+
CMakeFiles/

CHANGELOG.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,20 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88
## [Unreleased]
99

1010
### Fixed
11+
1112
* Select by primary index name
12-
* Fix error handling select with invalid type value
13+
* Fix error handling select with invalid type value
14+
* Get rid of performing map-reduce for single-replicaset operations
15+
16+
### Added
17+
18+
* `crud-router` Cartridge role
19+
20+
### Changed
21+
22+
* CRUD-router functions are exposed to the global scope, so it's possible to call
23+
crud-operations via `net.box.call`
24+
* `crud.init` is removed in favor to `crud.init_storage` and `crud.init_router`
1325

1426
## [0.2.0] - 2020-10-07
1527

README.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@ It also provides the `crud-storage` role for
77
## API
88

99
The CRUD operations should be called from router.
10-
All storage replica sets should call `crud.init()`
10+
All storage replica sets should call `crud.init_storage()`
1111
(or enable the `crud-storage` role)
1212
first to initialize storage-side functions that are used to manipulate data
1313
across the cluster.
14+
All routers should call `crud.init_router()` (or enable the `crud-router` role)
15+
to make `crud` functions callable via `net.box`.
1416

1517
**Notes:**
1618

@@ -318,12 +320,17 @@ for _, object in crud.pairs('customers', {{'<=', 'age', 35}}, {use_tomap = true}
318320
end
319321
```
320322

321-
## Cartridge role
323+
## Cartridge roles
322324

323325
`cartridge.roles.crud-storage` is a Tarantool Cartridge role that depends on the
324326
`vshard-storage` role, but also initializes functions that
325327
are used on the storage side to perform CRUD operations.
326328

329+
`cartridge.roles.crud-router` is a role that depends on the
330+
`vshard-router` role, but also exposes public `crud` functions in the global
331+
scope, so that you can call them via `net.box`.
332+
333+
327334
### Usage
328335

329336
1. Add `crud` to dependencies in the project rockspec.
@@ -373,8 +380,17 @@ return {
373380
}
374381
```
375382

383+
```lua
384+
-- app.roles.customers-router.lua
385+
local cartridge = require('cartridge')
386+
return {
387+
role_name = 'customers-router',
388+
dependencies = {'cartridge.roles.crud-router'},
389+
}
390+
```
391+
376392
3. Start the application and create `customers-storage` and
377-
`vshard-router` replica sets.
393+
`customers-router` replica sets.
378394

379395
4. Don't forget to bootstrap vshard.
380396

cartridge/roles/crud-router.lua

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
local crud = require('crud')
2+
3+
-- removes routes that changed in config and adds new routes
4+
local function init()
5+
crud.init_router()
6+
end
7+
8+
return {
9+
role_name = 'crud-router',
10+
init = init,
11+
dependencies = {'cartridge.roles.vshard-router'}
12+
}

cartridge/roles/crud-storage.lua

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
local crud = require('crud')
22

3-
-- removes routes that changed in config and adds new routes
43
local function init()
5-
crud.init()
4+
crud.init_storage()
65
end
76

87
return {

crud.lua

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
--
33
-- @module crud
44

5-
local registry = require('crud.common.registry')
6-
local call = require('crud.common.call')
75
local insert = require('crud.insert')
86
local replace = require('crud.replace')
97
local get = require('crud.get')
@@ -15,13 +13,6 @@ local utils = require('crud.common.utils')
1513

1614
local crud = {}
1715

18-
--- Functions registry
19-
-- @section registry
20-
21-
-- @refer registry.add
22-
-- @function register
23-
crud.register = registry.add
24-
2516
--- CRUD operations.
2617
-- @section crud
2718

@@ -80,8 +71,11 @@ crud.unflatten_rows = utils.unflatten_rows
8071
--
8172
-- @function init
8273
--
83-
function crud.init()
84-
call.init()
74+
function crud.init_storage()
75+
if rawget(_G, '_crud') == nil then
76+
rawset(_G, '_crud', {})
77+
end
78+
8579
insert.init()
8680
get.init()
8781
replace.init()
@@ -91,4 +85,8 @@ function crud.init()
9185
select.init()
9286
end
9387

88+
function crud.init_router()
89+
rawset(_G, 'crud', crud)
90+
end
91+
9492
return crud

crud/common/call.lua

Lines changed: 44 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,25 @@ local vshard = require('vshard')
33
local errors = require('errors')
44

55
local dev_checks = require('crud.common.dev_checks')
6-
local registry = require('crud.common.registry')
76
local utils = require('crud.common.utils')
87

98
local CallError = errors.new_class('Call')
109
local NotInitializedError = errors.new_class('NotInitialized')
1110

1211
local call = {}
1312

14-
local CALL_FUNC_NAME = '__call'
15-
1613
local DEFAULT_VSHARD_CALL_TIMEOUT = 2
1714

18-
--- Initializes call on node
19-
--
20-
-- Wrapper function is registered to call functions remotely.
21-
--
22-
-- @function init
23-
--
24-
function call.init()
25-
local function call(opts)
26-
dev_checks({
27-
func_name = 'string',
28-
func_args = '?table',
29-
})
30-
31-
local func = registry.get(opts.func_name) or rawget(_G, opts.func_name)
32-
33-
if type(func) ~= 'function' then
34-
return nil, CallError:new('Function %s is not registered', opts.func_name)
35-
end
36-
37-
return func(unpack(opts.func_args or {}))
38-
end
39-
40-
-- register global function
41-
rawset(_G, CALL_FUNC_NAME, call)
42-
end
43-
4415
local function call_on_replicaset(replicaset, channel, vshard_call, func_name, func_args, opts)
45-
local elect_call_arg = {
46-
func_name = func_name,
47-
func_args = func_args,
48-
}
49-
5016
-- replicaset:<vshard_call>(func_name,...)
51-
local func_ret, err = replicaset[vshard_call](replicaset, CALL_FUNC_NAME, {elect_call_arg}, opts)
17+
local func_ret, err = replicaset[vshard_call](replicaset, func_name, func_args, opts)
5218
if type(err) == 'table' and err.type == 'ClientError' and type(err.message) == 'string' then
53-
if err.message == string.format("Procedure '%s' is not defined", CALL_FUNC_NAME) then
54-
err = NotInitializedError:new("crud isn't initialized on replicaset")
19+
if err.message == string.format("Procedure '%s' is not defined", func_name) then
20+
if func_name:startswith('_crud.') then
21+
err = NotInitializedError:new("crud isn't initialized on replicaset")
22+
else
23+
err = NotInitializedError:new("Function %s is not registered", func_name)
24+
end
5525
end
5626
end
5727

@@ -165,4 +135,41 @@ function call.ro(func_name, func_args, opts)
165135
return call_impl('callro', func_name, func_args, opts)
166136
end
167137

138+
--- Calls specified function on a node according to bucket_id.
139+
--
140+
-- Exactly mimics the contract of vshard.router.callrw, but adds
141+
-- better error hangling
142+
--
143+
-- @function rw_single
144+
--
145+
function call.rw_single(bucket_id, func_name, func_args, options)
146+
local res, err = vshard.router.callrw(bucket_id, func_name, func_args, options)
147+
148+
-- This is a workaround, until vshard supports telling us where the error happened
149+
if err ~= nil then
150+
if type(err) == 'table' and err.type == 'ClientError' and type(err.message) == 'string' then
151+
if err.message == string.format("Procedure '%s' is not defined", func_name) then
152+
err = NotInitializedError:new("crud isn't initialized on replicaset")
153+
end
154+
end
155+
156+
local replicaset, _ = vshard.router.route(bucket_id)
157+
if replicaset == nil then
158+
return nil, CallError:new(
159+
"Function returned an error, but we couldn't figure out the replicaset: %s", err
160+
)
161+
end
162+
163+
return nil, CallError:new(utils.format_replicaset_error(
164+
replicaset.uuid, "Function returned an error: %s", err
165+
))
166+
end
167+
168+
if res == box.NULL then
169+
return nil
170+
end
171+
172+
return res
173+
end
174+
168175
return call

crud/common/registry.lua

Lines changed: 0 additions & 42 deletions
This file was deleted.

crud/delete.lua

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,16 @@ local errors = require('errors')
33
local vshard = require('vshard')
44

55
local call = require('crud.common.call')
6-
local registry = require('crud.common.registry')
76
local utils = require('crud.common.utils')
87
local dev_checks = require('crud.common.dev_checks')
98

109
local DeleteError = errors.new_class('Delete', {capture_stack = false})
1110

1211
local delete = {}
1312

14-
local DELETE_FUNC_NAME = '__delete'
13+
local DELETE_FUNC_NAME = '_crud.delete_on_storage'
1514

16-
local function call_delete_on_storage(space_name, key)
15+
local function delete_on_storage(space_name, key)
1716
dev_checks('string', '?')
1817

1918
local space = box.space[space_name]
@@ -26,9 +25,7 @@ local function call_delete_on_storage(space_name, key)
2625
end
2726

2827
function delete.init()
29-
registry.add({
30-
[DELETE_FUNC_NAME] = call_delete_on_storage,
31-
})
28+
_G._crud.delete_on_storage = delete_on_storage
3229
end
3330

3431
--- Deletes tuple from the specified space by key
@@ -66,24 +63,18 @@ function delete.call(space_name, key, opts)
6663
end
6764

6865
local bucket_id = vshard.router.bucket_id_strcrc32(key)
69-
local replicaset, err = vshard.router.route(bucket_id)
70-
if replicaset == nil then
71-
return nil, DeleteError:new("Failed to get replicaset for bucket_id %s: %s", bucket_id, err.err)
72-
end
73-
74-
local results, err = call.rw(DELETE_FUNC_NAME, {space_name, key}, {
75-
replicasets = {replicaset},
76-
timeout = opts.timeout,
77-
})
66+
local result, err = call.rw_single(
67+
bucket_id, DELETE_FUNC_NAME,
68+
{space_name, key}, {timeout = opts.timeout}
69+
)
7870

7971
if err ~= nil then
8072
return nil, DeleteError:new("Failed to delete: %s", err)
8173
end
8274

83-
local tuple = results[replicaset.uuid]
8475
return {
8576
metadata = table.copy(space:format()),
86-
rows = {tuple},
77+
rows = {result},
8778
}
8879
end
8980

0 commit comments

Comments
 (0)