Skip to content

Commit e898d0e

Browse files
committed
Add regression test for using DDL sharding key
CRUD allows to automatically calculate `bucket_id` based on primary key or one can specify `bucket_id` explicitly [1]. However it is often required to calculate `bucket_id` using sharding keys created by DDL schema. DDL module exposes space with sharding keys as a part of public API [2], so everyone is allowed to set and get sharding keys there without adding DDL module to dependencies. Patch allows to calculate `bucket_id` value automatically when sharding keys specified using DDL module or manually in `_ddl_sharding_key` space. 1. #46 2. https://github.com/tarantool/ddl#api 3. #46 (comment) Closes #166
1 parent 97a83ac commit e898d0e

File tree

3 files changed

+349
-0
lines changed

3 files changed

+349
-0
lines changed

CHANGELOG.md

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

2323
### Added
2424

25+
* Support calculating `bucket_id` based on `ddl.sharding_key`.
2526
* Added jsonpath indexes support for queries
2627
* `tuple-merger` module updated to 0.0.2
2728

test/entrypoint/srv_ddl.lua

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
#!/usr/bin/env tarantool
2+
3+
require('strict').on()
4+
_G.is_initialized = function() return false end
5+
6+
local log = require('log')
7+
local errors = require('errors')
8+
local cartridge = require('cartridge')
9+
local ddl = require('ddl')
10+
11+
package.preload['customers-storage'] = function()
12+
return {
13+
role_name = 'customers-storage',
14+
init = function()
15+
local engine = os.getenv('ENGINE') or 'memtx'
16+
local schema = {
17+
spaces = {
18+
customers = {
19+
engine = engine,
20+
is_local = true,
21+
temporary = false,
22+
format = {
23+
{name = 'id', is_nullable = false, type = 'unsigned'},
24+
{name = 'bucket_id', is_nullable = false, type = 'unsigned'},
25+
{name = 'name', is_nullable = false, type = 'string'},
26+
{name = 'age', is_nullable = false, type = 'number'},
27+
},
28+
indexes = {{
29+
name = 'id',
30+
type = 'TREE',
31+
unique = true,
32+
parts = {
33+
{path = 'id', is_nullable = false, type = 'unsigned'},
34+
},
35+
}, {
36+
name = 'bucket_id',
37+
type = 'TREE',
38+
unique = false,
39+
parts = {
40+
{path = 'bucket_id', is_nullable = false, type = 'unsigned'},
41+
}
42+
}},
43+
sharding_key = {'id', 'name'},
44+
},
45+
}
46+
}
47+
48+
rawset(_G, 'add_extra_field', function(name)
49+
local new_format = box.space.developers:format()
50+
table.insert(new_format, {name = name, type = 'any', is_nullable = true})
51+
box.space.developers:format(new_format)
52+
end)
53+
54+
if not box.cfg.read_only then
55+
local ok, err = ddl.set_schema(schema)
56+
if not ok then
57+
error(err)
58+
end
59+
end
60+
end,
61+
}
62+
end
63+
64+
local ok, err = errors.pcall('CartridgeCfgError', cartridge.cfg, {
65+
advertise_uri = 'localhost:3301',
66+
http_port = 8081,
67+
bucket_count = 3000,
68+
roles = {
69+
'customers-storage',
70+
'cartridge.roles.crud-router',
71+
'cartridge.roles.crud-storage',
72+
},
73+
})
74+
75+
if not ok then
76+
log.error('%s', err)
77+
os.exit(1)
78+
end
79+
80+
_G.is_initialized = cartridge.is_healthy
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
local fio = require('fio')
2+
3+
local t = require('luatest')
4+
local crud_utils = require('crud.common.utils')
5+
6+
local helpers = require('test.helper')
7+
8+
local ok = pcall(require, 'ddl')
9+
if not ok then
10+
t.skip('Lua module ddl is required to run test')
11+
end
12+
13+
local pgroup = helpers.pgroup.new('ddl_sharding_key', {
14+
engine = {'memtx', 'vinyl'},
15+
})
16+
17+
pgroup:set_before_all(function(g)
18+
g.cluster = helpers.Cluster:new({
19+
datadir = fio.tempdir(),
20+
server_command = helpers.entrypoint('srv_ddl'),
21+
use_vshard = true,
22+
replicasets = helpers.get_test_replicasets(),
23+
env = {
24+
['ENGINE'] = g.params.engine,
25+
},
26+
})
27+
g.cluster:start()
28+
local result, err = g.cluster.main_server.net_box:eval([[
29+
local ddl = require('ddl')
30+
31+
local ok, err = ddl.get_schema()
32+
return ok, err
33+
]])
34+
t.assert_equals(type(result), 'table')
35+
t.assert_equals(err, nil)
36+
37+
g.space_format = g.cluster.servers[2].net_box.space.customers:format()
38+
end)
39+
40+
41+
pgroup:set_after_all(function(g) helpers.stop_cluster(g.cluster) end)
42+
43+
pgroup:set_before_each(function(g)
44+
helpers.truncate_space_on_cluster(g.cluster, 'customers')
45+
end)
46+
47+
local function get_other_storage_bucket_id(g, key)
48+
local bucket_id = g.cluster.main_server.net_box:eval([[
49+
local vshard = require('vshard')
50+
51+
local key = ...
52+
return vshard.router.bucket_id_strcrc32(key)
53+
]], {key})
54+
55+
local res_bucket_id, err = helpers.get_other_storage_bucket_id(g.cluster, bucket_id)
56+
57+
t.assert(res_bucket_id ~= nil, err)
58+
return res_bucket_id
59+
end
60+
61+
local function check_get(g, space_name, id)
62+
local result, err = g.cluster.main_server.net_box:call('crud.get', {
63+
space_name, id,
64+
})
65+
66+
t.assert_equals(err, nil)
67+
t.assert(result ~= nil)
68+
end
69+
70+
local function check_get_with_bucket_id(g, space_name, id, bucket_id, tuple)
71+
local result, err = g.cluster.main_server.net_box:call('crud.get', {
72+
space_name, id,
73+
})
74+
75+
t.assert_equals(err, nil)
76+
t.assert(result ~= nil)
77+
t.assert_equals(#result.rows, 0)
78+
79+
-- get w/ right bucket_id
80+
local result, err = g.cluster.main_server.net_box:call('crud.get', {
81+
space_name, id, {bucket_id = bucket_id}
82+
})
83+
84+
t.assert_equals(err, nil)
85+
t.assert(result ~= nil)
86+
t.assert_equals(result.rows, {tuple})
87+
end
88+
89+
pgroup:add('test_update', function(g)
90+
local tuple = {2, box.NULL, 'Ivan', 20}
91+
92+
local update_operations = {
93+
{'+', 'age', 10},
94+
{'=', 'name', 'Leo Tolstoy'},
95+
}
96+
97+
-- insert tuple
98+
local result, err = g.cluster.main_server.net_box:call('crud.insert', {
99+
'customers', tuple
100+
})
101+
102+
t.assert_equals(err, nil)
103+
t.assert(result ~= nil)
104+
t.assert_equals(#result.rows, 1)
105+
106+
local result, err = g.cluster.main_server.net_box:call('crud.update', {
107+
'customers', tuple[1], update_operations,
108+
})
109+
110+
t.assert_equals(err, nil)
111+
t.assert(result ~= nil)
112+
t.assert_equals(#result.rows, 1)
113+
end)
114+
115+
pgroup:add('test_delete', function(g)
116+
local tuple = {2, box.NULL, 'Ivan', 20}
117+
118+
-- insert tuple
119+
local result, err = g.cluster.main_server.net_box:call('crud.insert', {
120+
'customers', tuple
121+
})
122+
123+
t.assert_equals(err, nil)
124+
t.assert(result ~= nil)
125+
t.assert_equals(#result.rows, 1)
126+
127+
local result, err = g.cluster.main_server.net_box:call('crud.delete', {
128+
'customers', tuple[1],
129+
})
130+
131+
t.assert_equals(err, nil)
132+
t.assert(result ~= nil)
133+
134+
local result, err = g.cluster.main_server.net_box:call('crud.get', {
135+
'customers', tuple[1]
136+
})
137+
138+
t.assert_equals(err, nil)
139+
t.assert(result ~= nil)
140+
t.assert_equals(#result.rows, 0)
141+
end)
142+
143+
pgroup:add('test_insert_object', function(g)
144+
local object = {id = 2, name = 'Ivan', age = 46}
145+
local bucket_id = get_other_storage_bucket_id(g, object.id)
146+
object.bucket_id = bucket_id
147+
148+
local tuple = crud_utils.flatten(object, g.space_format, bucket_id)
149+
150+
-- insert_object
151+
local result, err = g.cluster.main_server.net_box:call('crud.insert_object', {
152+
'customers', object
153+
})
154+
155+
t.assert_equals(err, nil)
156+
t.assert(result ~= nil)
157+
t.assert_equals(result.rows, {tuple})
158+
159+
check_get_with_bucket_id(g, 'customers', object.id, bucket_id, tuple)
160+
end)
161+
162+
pgroup:add('test_insert', function(g)
163+
local tuple = {2, box.NULL, 'Ivan', 20}
164+
165+
-- insert
166+
local result, err = g.cluster.main_server.net_box:call('crud.insert', {
167+
'customers', tuple,
168+
})
169+
170+
t.assert_equals(err, nil)
171+
t.assert(result ~= nil)
172+
173+
tuple[2] = nil
174+
t.assert_items_include(result.rows[1], tuple)
175+
check_get(g, 'customers', 2, tuple)
176+
end)
177+
178+
pgroup:add('test_replace_object', function(g)
179+
local object = {id = 2, name = 'Jane', age = 21}
180+
local bucket_id = get_other_storage_bucket_id(g, object.id)
181+
object.bucket_id = bucket_id
182+
183+
local tuple = crud_utils.flatten(object, g.space_format, bucket_id)
184+
185+
-- replace_object
186+
local result, err = g.cluster.main_server.net_box:call('crud.replace_object', {
187+
'customers', object,
188+
})
189+
190+
t.assert_equals(err, nil)
191+
t.assert(result ~= nil)
192+
t.assert_equals(result.rows, {tuple})
193+
194+
check_get_with_bucket_id(g, 'customers', object.id, bucket_id, tuple)
195+
end)
196+
197+
pgroup:add('test_replace', function(g)
198+
local tuple = {2, box.NULL, 'Jane', 21}
199+
200+
-- replace
201+
local result, err = g.cluster.main_server.net_box:call('crud.replace', {
202+
'customers', tuple
203+
})
204+
205+
t.assert_equals(err, nil)
206+
t.assert(result ~= nil)
207+
tuple[2] = nil
208+
t.assert_items_include(result.rows[1], tuple)
209+
210+
check_get(g, 'customers', tuple[1], tuple)
211+
end)
212+
213+
pgroup:add('test_upsert_object', function(g)
214+
local object = {id = 2, name = 'Jane', age = 21}
215+
local bucket_id = get_other_storage_bucket_id(g, object.id)
216+
object.bucket_id = bucket_id
217+
218+
local tuple = crud_utils.flatten(object, g.space_format, bucket_id)
219+
220+
-- upsert_object
221+
local result, err = g.cluster.main_server.net_box:call('crud.upsert_object', {
222+
'customers', object, {},
223+
})
224+
225+
t.assert_equals(err, nil)
226+
t.assert(result ~= nil)
227+
t.assert_equals(#result.rows, 0)
228+
229+
check_get_with_bucket_id(g, 'customers', object.id, bucket_id, tuple)
230+
end)
231+
232+
pgroup:add('test_upsert', function(g)
233+
local tuple = {1, box.NULL, 'John', 25}
234+
235+
-- upsert
236+
local result, err = g.cluster.main_server.net_box:call('crud.upsert', {
237+
'customers', tuple, {}
238+
})
239+
240+
t.assert_equals(err, nil)
241+
t.assert(result ~= nil)
242+
t.assert_equals(#result.rows, 0)
243+
244+
check_get(g, 'customers', tuple[1], tuple)
245+
end)
246+
247+
pgroup:add('test_select', function(g)
248+
local tuple = {2, box.NULL, 'Ivan', 20}
249+
250+
-- insert tuple
251+
local result, err = g.cluster.main_server.net_box:call('crud.insert', {
252+
'customers', tuple
253+
})
254+
255+
t.assert_equals(err, nil)
256+
t.assert(result ~= nil)
257+
t.assert_equals(#result.rows, 1)
258+
259+
local conditions = {{'==', 'id', tuple[1]}}
260+
261+
local result, err = g.cluster.main_server.net_box:call('crud.select', {
262+
'customers', conditions,
263+
})
264+
265+
t.assert_equals(err, nil)
266+
t.assert(result ~= nil)
267+
t.assert_equals(#result.rows, 1)
268+
end)

0 commit comments

Comments
 (0)