Skip to content

Commit 6225fad

Browse files
ddl: fix error on explicit bucket_id
After introducing sharding hash info comparison [1], requests with ddl and explicit bucket_id in options started to fail with "Sharding hash mismatch" error. It affected following methods: - insert - insert_object - replace - replace_object - upsert - upsert_object - count The situation is as follows. Due to a code mistake, router hasn't passed a sharding hash with a request if bucket_id was specified. If there was any ddl information for a space on storage, it has caused a hash mismatch error. Since sharding info reload couldn't fix broken hash extraction, request failed after a number of retries. This patch fixes this behavior by skipping hash comparison if sharding info wasn't used (we already do it in other methods). 1. #268 Closes #278
1 parent edbe9ad commit 6225fad

File tree

9 files changed

+437
-5
lines changed

9 files changed

+437
-5
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
1212
### Changed
1313

1414
### Fixed
15+
* Requests no more fail with "Sharding hash mismatch" error
16+
if ddl set and bucket_id is explicitly specified (#278).
1517

1618
## [0.11.0] - 20-04-22
1719

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ with sharding func definition as a part of
8383
or insert manually to the space `_ddl_sharding_func`.
8484

8585
Automatic sharding key and function reload is supported since version 0.11.0.
86+
Version 0.11.0 contains critical bug that causes some CRUD methods to fail
87+
with "Sharding hash mismatch" error if ddl is set and bucket_id is provided
88+
explicitly ([#278](https://github.com/tarantool/crud/issues/278)). Please,
89+
upgrade to 0.11.1 instead.
8690

8791
CRUD uses `strcrc32` as sharding function by default.
8892
The reason why using of `strcrc32` is undesirable is that

crud/common/sharding/init.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,8 @@ function sharding.tuple_set_and_return_bucket_id(tuple, space, specified_bucket_
100100
return nil, err
101101
end
102102
tuple[bucket_id_fieldno] = sharding_data.bucket_id
103+
else
104+
sharding_data.skip_sharding_hash_check = true
103105
end
104106

105107
return sharding_data

crud/count.lua

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,18 @@ local function call_count_on_router(space_name, user_conditions, opts)
126126
return nil, CountError:new("Space %q doesn't exist", space_name), const.NEED_SCHEMA_RELOAD
127127
end
128128

129-
local sharding_key_data, err = sharding_metadata_module.fetch_sharding_key_on_router(space_name)
130-
if err ~= nil then
131-
return nil, err
129+
local sharding_key_data = {}
130+
local sharding_func_hash = nil
131+
local skip_sharding_hash_check = nil
132+
133+
-- We don't need sharding info if bucket_id specified.
134+
if opts.bucket_id == nil then
135+
sharding_key_data, err = sharding_metadata_module.fetch_sharding_key_on_router(space_name)
136+
if err ~= nil then
137+
return nil, err
138+
end
139+
else
140+
skip_sharding_hash_check = true
132141
end
133142

134143
-- plan count
@@ -171,8 +180,6 @@ local function call_count_on_router(space_name, user_conditions, opts)
171180
-- eye to resharding. However, AFAIU, the optimization
172181
-- does not make the result less consistent (sounds
173182
-- weird, huh?).
174-
local sharding_func_hash = nil
175-
local skip_sharding_hash_check = nil
176183

177184
local perform_map_reduce = opts.force_map_call == true or
178185
(opts.bucket_id == nil and plan.sharding_key == nil)

crud/insert.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ local function insert_on_storage(space_name, tuple, opts)
2121
fields = '?table',
2222
sharding_key_hash = '?number',
2323
sharding_func_hash = '?number',
24+
skip_sharding_hash_check = '?boolean',
2425
})
2526

2627
opts = opts or {}
@@ -82,6 +83,7 @@ local function call_insert_on_router(space_name, original_tuple, opts)
8283
fields = opts.fields,
8384
sharding_func_hash = sharding_data.sharding_func_hash,
8485
sharding_key_hash = sharding_data.sharding_key_hash,
86+
skip_sharding_hash_check = sharding_data.skip_sharding_hash_check,
8587
}
8688

8789
local call_opts = {

crud/replace.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ local function replace_on_storage(space_name, tuple, opts)
2121
fields = '?table',
2222
sharding_key_hash = '?number',
2323
sharding_func_hash = '?number',
24+
skip_sharding_hash_check = '?boolean',
2425
})
2526

2627
opts = opts or {}
@@ -86,6 +87,7 @@ local function call_replace_on_router(space_name, original_tuple, opts)
8687
fields = opts.fields,
8788
sharding_func_hash = sharding_data.sharding_func_hash,
8889
sharding_key_hash = sharding_data.sharding_key_hash,
90+
skip_sharding_hash_check = sharding_data.skip_sharding_hash_check,
8991
}
9092

9193
local call_opts = {

crud/upsert.lua

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ local function upsert_on_storage(space_name, tuple, operations, opts)
2020
add_space_schema_hash = '?boolean',
2121
sharding_key_hash = '?number',
2222
sharding_func_hash = '?number',
23+
skip_sharding_hash_check = '?boolean',
2324
})
2425

2526
opts = opts or {}
@@ -93,6 +94,7 @@ local function call_upsert_on_router(space_name, original_tuple, user_operations
9394
fields = opts.fields,
9495
sharding_func_hash = sharding_data.sharding_func_hash,
9596
sharding_key_hash = sharding_data.sharding_key_hash,
97+
skip_sharding_hash_check = sharding_data.skip_sharding_hash_check,
9698
}
9799

98100
local call_opts = {

test/integration/ddl_sharding_func_test.lua

Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -540,3 +540,203 @@ cache_group.test_update_cache_with_incorrect_func = function(g)
540540
cache_size = helpers.get_sharding_func_cache_size(g.cluster)
541541
t.assert_equals(cache_size, 1)
542542
end
543+
544+
545+
local known_bucket_id_key = {1, 'Emma'}
546+
local known_bucket_id_tuple = {
547+
known_bucket_id_key[1],
548+
box.NULL,
549+
known_bucket_id_key[2],
550+
22
551+
}
552+
local known_bucket_id_object = {
553+
id = known_bucket_id_key[1],
554+
bucket_id = box.NULL,
555+
name = known_bucket_id_key[2],
556+
age = 22
557+
}
558+
local known_bucket_id = 1111
559+
local known_bucket_id_result_tuple = {
560+
known_bucket_id_key[1],
561+
known_bucket_id,
562+
known_bucket_id_key[2],
563+
22
564+
}
565+
local known_bucket_id_result = {
566+
s1 = nil,
567+
s2 = known_bucket_id_result_tuple,
568+
}
569+
local known_bucket_id_update = {{'+', 'age', 1}}
570+
local known_bucket_id_updated_result = {
571+
s1 = nil,
572+
s2 = {known_bucket_id_key[1], known_bucket_id, known_bucket_id_key[2], 23},
573+
}
574+
local prepare_known_bucket_id_data = function(g)
575+
if known_bucket_id_result.s1 ~= nil then
576+
local conn_s1 = g.cluster:server('s1-master').net_box
577+
local result = conn_s1.space[g.params.space_name]:insert(known_bucket_id_result.s1)
578+
t.assert_equals(result, known_bucket_id_result.s1)
579+
end
580+
581+
if known_bucket_id_result.s2 ~= nil then
582+
local conn_s2 = g.cluster:server('s2-master').net_box
583+
local result = conn_s2.space[g.params.space_name]:insert(known_bucket_id_result.s2)
584+
t.assert_equals(result, known_bucket_id_result.s2)
585+
end
586+
end
587+
588+
local known_bucket_id_write_cases = {
589+
insert = {
590+
func = 'crud.insert',
591+
input_2 = known_bucket_id_tuple,
592+
input_3 = {bucket_id = known_bucket_id},
593+
result = known_bucket_id_result,
594+
},
595+
insert_object = {
596+
func = 'crud.insert_object',
597+
input_2 = known_bucket_id_object,
598+
input_3 = {bucket_id = known_bucket_id},
599+
result = known_bucket_id_result,
600+
},
601+
replace = {
602+
func = 'crud.replace',
603+
input_2 = known_bucket_id_tuple,
604+
input_3 = {bucket_id = known_bucket_id},
605+
result = known_bucket_id_result,
606+
},
607+
replace_object = {
608+
func = 'crud.replace_object',
609+
input_2 = known_bucket_id_object,
610+
input_3 = {bucket_id = known_bucket_id},
611+
result = known_bucket_id_result,
612+
},
613+
upsert = {
614+
func = 'crud.upsert',
615+
input_2 = known_bucket_id_tuple,
616+
input_3 = {},
617+
input_4 = {bucket_id = known_bucket_id},
618+
result = known_bucket_id_result,
619+
},
620+
upsert_object = {
621+
func = 'crud.upsert_object',
622+
input_2 = known_bucket_id_object,
623+
input_3 = {},
624+
input_4 = {bucket_id = known_bucket_id},
625+
result = known_bucket_id_result,
626+
},
627+
update = {
628+
before_test = prepare_known_bucket_id_data,
629+
func = 'crud.update',
630+
input_2 = known_bucket_id_key,
631+
input_3 = known_bucket_id_update,
632+
input_4 = {bucket_id = known_bucket_id},
633+
result = known_bucket_id_updated_result,
634+
},
635+
delete = {
636+
before_test = prepare_known_bucket_id_data,
637+
func = 'crud.delete',
638+
input_2 = known_bucket_id_key,
639+
input_3 = {bucket_id = known_bucket_id},
640+
result = {},
641+
},
642+
}
643+
644+
for name, case in pairs(known_bucket_id_write_cases) do
645+
local test_name = ('test_gh_278_%s_with_explicit_bucket_id_and_ddl'):format(name)
646+
647+
if case.before_test ~= nil then
648+
pgroup.before_test(test_name, case.before_test)
649+
end
650+
651+
pgroup[test_name] = function(g)
652+
local obj, err = g.cluster.main_server.net_box:call(
653+
case.func, {
654+
g.params.space_name,
655+
case.input_2,
656+
case.input_3,
657+
case.input_4,
658+
})
659+
t.assert_equals(err, nil)
660+
t.assert_is_not(obj, nil)
661+
662+
local conn_s1 = g.cluster:server('s1-master').net_box
663+
local result = conn_s1.space[g.params.space_name]:get(known_bucket_id_key)
664+
t.assert_equals(result, case.result.s1)
665+
666+
local conn_s2 = g.cluster:server('s2-master').net_box
667+
local result = conn_s2.space[g.params.space_name]:get(known_bucket_id_key)
668+
t.assert_equals(result, case.result.s2)
669+
end
670+
end
671+
672+
local known_bucket_id_read_cases = {
673+
get = {
674+
func = 'crud.get',
675+
input_2 = known_bucket_id_key,
676+
input_3 = {bucket_id = known_bucket_id},
677+
},
678+
select = {
679+
func = 'crud.select',
680+
input_2 = {{ '==', 'id', known_bucket_id_key}},
681+
input_3 = {bucket_id = known_bucket_id},
682+
},
683+
}
684+
685+
for name, case in pairs(known_bucket_id_read_cases) do
686+
local test_name = ('test_gh_278_%s_with_explicit_bucket_id_and_ddl'):format(name)
687+
688+
pgroup.before_test(test_name, prepare_known_bucket_id_data)
689+
690+
pgroup[test_name] = function(g)
691+
local obj, err = g.cluster.main_server.net_box:call(
692+
case.func, {
693+
g.params.space_name,
694+
case.input_2,
695+
case.input_3,
696+
})
697+
t.assert_equals(err, nil)
698+
t.assert_is_not(obj, nil)
699+
t.assert_equals(obj.rows, {known_bucket_id_result_tuple})
700+
end
701+
end
702+
703+
pgroup.before_test(
704+
'test_gh_278_pairs_with_explicit_bucket_id_and_ddl',
705+
prepare_known_bucket_id_data)
706+
707+
pgroup.test_gh_278_pairs_with_explicit_bucket_id_and_ddl = function(g)
708+
local obj, err = g.cluster.main_server.net_box:eval([[
709+
local res = {}
710+
for _, row in crud.pairs(...) do
711+
table.insert(res, row)
712+
end
713+
714+
return res
715+
]], {
716+
g.params.space_name,
717+
{{ '==', 'id', known_bucket_id_key}},
718+
{bucket_id = known_bucket_id}
719+
})
720+
721+
t.assert_equals(err, nil)
722+
t.assert_is_not(obj, nil)
723+
t.assert_equals(obj, {known_bucket_id_result_tuple})
724+
end
725+
726+
pgroup.before_test(
727+
'test_gh_278_count_with_explicit_bucket_id_and_ddl',
728+
prepare_known_bucket_id_data)
729+
730+
pgroup.test_gh_278_count_with_explicit_bucket_id_and_ddl = function(g)
731+
local obj, err = g.cluster.main_server.net_box:call(
732+
'crud.count',
733+
{
734+
g.params.space_name,
735+
{{ '==', 'id', known_bucket_id_key}},
736+
{bucket_id = known_bucket_id}
737+
})
738+
739+
t.assert_equals(err, nil)
740+
t.assert_is_not(obj, nil)
741+
t.assert_equals(obj, 1)
742+
end

0 commit comments

Comments
 (0)