* [tarantool-patches] [PATCH 0/3][vshard] Add error messages @ 2018-06-06 15:57 AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 1/3] Fix test on bucket_transferring error AKhatskevich ` (3 more replies) 0 siblings, 4 replies; 11+ messages in thread From: AKhatskevich @ 2018-06-06 15:57 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy Branch: https://github.com/tarantool/vshard/tree/kh/gh-100-error-msg-wo-extra-data Issue: https://github.com/tarantool/vshard/issues/100 *** BLURB HERE *** AKhatskevich (3): Fix test on bucket_transferring error Refactor error reporting system Add error messages test/misc/check_uuid_on_connect.result | 8 +- test/router/reroute_wrong_bucket.result | 11 +- test/router/router.result | 54 +++++--- test/router/router.test.lua | 4 +- test/storage/storage.result | 7 +- test/unit/error.result | 6 +- test/unit/error.test.lua | 2 +- vshard/error.lua | 216 +++++++++++++++++++++----------- vshard/replicaset.lua | 4 +- vshard/router/init.lua | 10 +- vshard/storage/init.lua | 39 +++--- 11 files changed, 231 insertions(+), 130 deletions(-) -- 2.14.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH 1/3] Fix test on bucket_transferring error 2018-06-06 15:57 [tarantool-patches] [PATCH 0/3][vshard] Add error messages AKhatskevich @ 2018-06-06 15:57 ` AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 2/3] Refactor error reporting system AKhatskevich ` (2 subsequent siblings) 3 siblings, 0 replies; 11+ messages in thread From: AKhatskevich @ 2018-06-06 15:57 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy --- test/router/router.result | 12 ++++++------ test/router/router.test.lua | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/router/router.result b/test/router/router.result index 101369a..5f3ceca 100644 --- a/test/router/router.result +++ b/test/router/router.result @@ -328,17 +328,17 @@ test_run:cmd('switch storage_2_a') --- - true ... -box.space._bucket:replace({1, vshard.consts.BUCKET.SENDING}) +box.space._bucket:replace({1, vshard.consts.BUCKET.SENDING, '<replicaset_1>'}) --- -- [1, 'sending'] +- [1, 'sending', '<replicaset_1>'] ... test_run:cmd('switch storage_1_a') --- - true ... -box.space._bucket:replace({1, vshard.consts.BUCKET.RECEIVING}) +box.space._bucket:replace({1, vshard.consts.BUCKET.RECEIVING, '<replicaset_2>'}) --- -- [1, 'receiving'] +- [1, 'receiving', '<replicaset_2>'] ... test_run:cmd('switch router_1') --- @@ -353,7 +353,7 @@ vshard.router.call(1, 'read', 'echo', {123}) util.check_error(vshard.router.call, 1, 'write', 'echo', {123}) --- - null -- {'type': 'ShardingError', 'code': 7, 'bucket_id': 1} +- {'type': 'ShardingError', 'bucket_id': 1, 'code': 7, 'destination': '<replicaset_1>'} ... test_run:cmd('switch storage_2_a') --- @@ -369,7 +369,7 @@ test_run:cmd('switch storage_1_a') ... box.space._bucket:delete({1}) --- -- [1, 'receiving'] +- [1, 'receiving', '<replicaset_2>'] ... test_run:cmd('switch router_1') --- diff --git a/test/router/router.test.lua b/test/router/router.test.lua index 62d6730..fae8e24 100644 --- a/test/router/router.test.lua +++ b/test/router/router.test.lua @@ -119,9 +119,9 @@ replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err replicaset, err = vshard.router.bucket_discovery(2); return err == nil or err test_run:cmd('switch storage_2_a') -box.space._bucket:replace({1, vshard.consts.BUCKET.SENDING}) +box.space._bucket:replace({1, vshard.consts.BUCKET.SENDING, 'cbf06940-0790-498b-948d-042b62cf3d29'}) test_run:cmd('switch storage_1_a') -box.space._bucket:replace({1, vshard.consts.BUCKET.RECEIVING}) +box.space._bucket:replace({1, vshard.consts.BUCKET.RECEIVING, 'ac522f65-aa94-4134-9f64-51ee384f1a54'}) test_run:cmd('switch router_1') -- Ok to read sending bucket. vshard.router.call(1, 'read', 'echo', {123}) -- 2.14.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH 2/3] Refactor error reporting system 2018-06-06 15:57 [tarantool-patches] [PATCH 0/3][vshard] Add error messages AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 1/3] Fix test on bucket_transferring error AKhatskevich @ 2018-06-06 15:57 ` AKhatskevich 2018-06-07 12:22 ` [tarantool-patches] " Vladislav Shpilevoy 2018-06-06 15:57 ` [tarantool-patches] [PATCH 3/3] Add error messages AKhatskevich 2018-06-07 12:22 ` [tarantool-patches] Re: [PATCH 0/3][vshard] " Vladislav Shpilevoy 3 siblings, 1 reply; 11+ messages in thread From: AKhatskevich @ 2018-06-06 15:57 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy This commit applies stricter rules on error reporting: * error may has only arguments specified in `args` field of `error_message_template` * arguments may be used in error message * arguments are saved in error object by names from `args` Part of #100 --- test/misc/check_uuid_on_connect.result | 8 +- test/router/reroute_wrong_bucket.result | 10 +- test/router/router.result | 42 ++++--- test/storage/storage.result | 6 +- test/unit/error.result | 6 +- test/unit/error.test.lua | 2 +- vshard/error.lua | 209 +++++++++++++++++++++----------- vshard/replicaset.lua | 4 +- vshard/router/init.lua | 11 +- vshard/storage/init.lua | 17 +-- 10 files changed, 197 insertions(+), 118 deletions(-) diff --git a/test/misc/check_uuid_on_connect.result b/test/misc/check_uuid_on_connect.result index d54c367..2695665 100644 --- a/test/misc/check_uuid_on_connect.result +++ b/test/misc/check_uuid_on_connect.result @@ -182,10 +182,12 @@ test_run:switch('bad_uuid_router') vshard.router.bucket_discovery(1) --- - null -- type: ShardingError - unreachable_uuid: ac522f65-aa94-4134-9f64-51ee384f1a54 +- bucket_id: 1 code: 8 - bucket_id: 1 + unreachable_uuid: ac522f65-aa94-4134-9f64-51ee384f1a54 + message: There is no active replicas in replicaset ac522f65-aa94-4134-9f64-51ee384f1a54 + name: UNREACHABLE_REPLICASET + type: ShardingError ... -- Ok to work with correct replicasets. vshard.router.bucket_discovery(2).uuid diff --git a/test/router/reroute_wrong_bucket.result b/test/router/reroute_wrong_bucket.result index eda3bab..c7b980c 100644 --- a/test/router/reroute_wrong_bucket.result +++ b/test/router/reroute_wrong_bucket.result @@ -109,9 +109,11 @@ test_run:switch('router_1') vshard.router.call(100, 'read', 'customer_lookup', {1}, {timeout = 1}) --- - null -- type: ShardingError - bucket_id: 100 +- bucket_id: 100 code: 9 + type: ShardingError + name: NO_ROUTE_TO_BUCKET + message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":100}"' ... -- Wait reconfiguration durigin timeout, if a replicaset was not -- found by bucket.destination from WRONG_BUCKET or @@ -188,7 +190,9 @@ test_run:grep_log('router_1', 'please update configuration') ... err --- -- {'type': 'ShardingError', 'bucket_id': 100, 'code': 1, 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54'} +- {'bucket_id': 100, 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', + 'message': 'WRONG_BUCKET: "{\"bucket_id\":100,\"destination\":\"ac522f65-aa94-4134-9f64-51ee384f1a54\"}"', + 'name': 'WRONG_BUCKET'} ... -- -- Now try again, but update configuration during call(). It must diff --git a/test/router/router.result b/test/router/router.result index 5f3ceca..6955b59 100644 --- a/test/router/router.result +++ b/test/router/router.result @@ -195,15 +195,19 @@ vshard.router.bucket_count() util.check_error(vshard.router.call, 1, 'read', 'echo', {123}) --- - null -- type: ShardingError - bucket_id: 1 +- bucket_id: 1 code: 9 + type: ShardingError + name: NO_ROUTE_TO_BUCKET + message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":1}"' ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err --- -- type: ShardingError - bucket_id: 1 +- bucket_id: 1 code: 9 + type: ShardingError + name: NO_ROUTE_TO_BUCKET + message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":1}"' ... vshard.router.bootstrap() --- @@ -214,8 +218,9 @@ vshard.router.bootstrap() --- - null - type: ShardingError - code: 10 message: Cluster is already bootstrapped + code: 10 + name: NON_EMPTY ... -- -- gh-108: negative bucket count on discovery. @@ -312,9 +317,11 @@ util.check_error(vshard.router.call, 0, 'read', 'echo', {123}) ... replicaset, err = vshard.router.bucket_discovery(0); return err == nil or err --- -- type: ShardingError - bucket_id: 0 +- bucket_id: 0 code: 9 + type: ShardingError + name: NO_ROUTE_TO_BUCKET + message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":0}"' ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err --- @@ -353,7 +360,9 @@ vshard.router.call(1, 'read', 'echo', {123}) util.check_error(vshard.router.call, 1, 'write', 'echo', {123}) --- - null -- {'type': 'ShardingError', 'bucket_id': 1, 'code': 7, 'destination': '<replicaset_1>'} +- {'bucket_id': 1, 'code': 7, 'type': 'ShardingError', 'destination': '<replicaset_1>', + 'message': 'TRANSFER_IS_IN_PROGRESS: "{\"bucket_id\":1,\"destination\":\"<replicaset_1>\"}"', + 'name': 'TRANSFER_IS_IN_PROGRESS'} ... test_run:cmd('switch storage_2_a') --- @@ -397,10 +406,12 @@ test_run:cmd('stop server storage_2_a') util.check_error(vshard.router.call, 1, 'read', 'echo', {123}) --- - null -- type: ShardingError - unreachable_uuid: <replicaset_2> +- bucket_id: 1 code: 8 - bucket_id: 1 + unreachable_uuid: <replicaset_2> + message: There is no active replicas in replicaset <replicaset_2> + name: UNREACHABLE_REPLICASET + type: ShardingError ... vshard.router.buckets_info(0, 3) --- @@ -420,9 +431,11 @@ test_run:cmd('start server storage_2_a') vshard.router.route(vshard.consts.DEFAULT_BUCKET_COUNT + 100) --- - null -- type: ShardingError - bucket_id: 3100 +- bucket_id: 3100 code: 9 + type: ShardingError + name: NO_ROUTE_TO_BUCKET + message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":3100}"' ... util.check_error(vshard.router.route, 'asdfg') --- @@ -956,7 +969,8 @@ test_run:cmd("switch router_1") util.check_error(vshard.router.call, 1, 'write', 'echo', { 'hello world' }) --- - null -- {'type': 'ShardingError', 'code': 2, 'bucket_id': 1} +- {'bucket_id': 1, 'code': 2, 'type': 'ShardingError', 'name': 'NON_MASTER', 'message': 'NON_MASTER: + "{\"bucket_id\":1}"'} ... -- Reconfigure router and test that the WRITE request does work vshard.router.cfg(cfg) diff --git a/test/storage/storage.result b/test/storage/storage.result index e93ad24..aee9477 100644 --- a/test/storage/storage.result +++ b/test/storage/storage.result @@ -594,9 +594,11 @@ vshard.storage.call(1, 'read', 'customer_lookup', {1}) vshard.storage.call(100500, 'read', 'customer_lookup', {1}) --- - null -- type: ShardingError - bucket_id: 100500 +- bucket_id: 100500 code: 1 + type: ShardingError + name: WRONG_BUCKET + message: 'WRONG_BUCKET: "{\"bucket_id\":100500}"' ... -- -- Test not existing space in bucket data. diff --git a/test/unit/error.result b/test/unit/error.result index a46fb0d..ed9904a 100644 --- a/test/unit/error.result +++ b/test/unit/error.result @@ -20,13 +20,13 @@ tostring(box_error) --- - '{"type":"ClientError","code":78,"message":"Timeout exceeded","trace":[{"file":"[C]","line":4294967295}]}' ... -vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, {uuid = 'uuid', reason = 'reason'}, 'message 1 2 3') +vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, 'uuid', 'reason') --- ... tostring(vshard_error) --- -- '{"reason":"reason","code":11,"type":"ShardingError","uuid":"uuid","message":"message - 1 2 3"}' +- '{"reason":"reason","code":11,"type":"ShardingError","message":"Master of replicaset + uuid is unreachable: reason","uuid":"uuid","name":"UNREACHABLE_MASTER"}' ... log = require('log') --- diff --git a/test/unit/error.test.lua b/test/unit/error.test.lua index 4b5c20f..7b89f64 100644 --- a/test/unit/error.test.lua +++ b/test/unit/error.test.lua @@ -9,7 +9,7 @@ ok, err = pcall(box.error, box.error.TIMEOUT) box_error = lerror.box(err) tostring(box_error) -vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, {uuid = 'uuid', reason = 'reason'}, 'message 1 2 3') +vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, 'uuid', 'reason') tostring(vshard_error) log = require('log') diff --git a/vshard/error.lua b/vshard/error.lua index 8d26d11..1271d16 100644 --- a/vshard/error.lua +++ b/vshard/error.lua @@ -1,6 +1,111 @@ local ffi = require('ffi') local json = require('json') +-- +-- Error messages description. +-- * name -- Key by which an error code can be retrieved from +-- exprted `code` dictionary. +-- * msg -- Error message which can cantain parameters described +-- in `string.format` notation. +-- * args -- Names of arguments passed while constructing an +-- error. After constructed, an error-object contains provided +-- arguments by the names specified in the field. +-- +local error_message_template = { + [1] = { + name = 'WRONG_BUCKET', + args = {'bucket_id', 'destination'} + }, + [2] = { + name = 'NON_MASTER', + args = {'bucket_id', 'destination'} + }, + [3] = { + name = 'BUCKET_ALREADY_EXISTS', + args = {'bucket_id'} + }, + [4] = { + name = 'NO_SUCH_REPLICASET', + args = {'replicaset_uuid'} + }, + [5] = { + name = 'MOVE_TO_SELF', + args = {'replicaset_uuid', 'bucket_id'} + }, + [6] = { + name = 'MISSING_MASTER', + msg = 'Master is not configured for replicaset %s', + args = {'replicaset_uuid'} + }, + [7] = { + name = 'TRANSFER_IS_IN_PROGRESS', + args = {'bucket_id', 'destination'} + }, + [8] = { + name = 'UNREACHABLE_REPLICASET', + msg = 'There is no active replicas in replicaset %s', + args = {'unreachable_uuid', 'bucket_id'} + }, + [9] = { + name = 'NO_ROUTE_TO_BUCKET', + args = {'bucket_id', 'unreachable_uuid'} + }, + [10] = { + name = 'NON_EMPTY', + msg = 'Cluster is already bootstrapped' + }, + [11] = { + name = 'UNREACHABLE_MASTER', + msg = 'Master of replicaset %s is unreachable: %s', + args = {'uuid', 'reason'} + }, + [12] = { + name = 'OUT_OF_SYNC', + msg = 'Replica is out of sync' + }, + [13] = { + name = 'HIGH_REPLICATION_LAG', + msg = 'High replication lag: %f', + args = {'lag'} + }, + [14] = { + name = 'UNREACHABLE_REPLICA', + msg = "Replica %s isn't active", + args = {'unreachable_uuid'} + }, + [15] = { + name = 'LOW_REDUNDANCY', + msg = 'Only one replica is active' + }, + [16] = { + name = 'INVALID_REBALANCING', + msg = 'Sending and receiving buckets at same time is not allowed' + }, + [17] = { + name = 'SUBOPTIMAL_REPLICA', + msg = 'A current read replica in replicaset %s is not optimal' + }, + [18] = { + name = 'UNKNOWN_BUCKETS', + msg = '%d buckets are not discovered', + arg = {'not_discovered_cnt'} + }, + [19] = { + name = 'REPLICASET_IS_LOCKED', + msg = 'Replicaset is locked' + } +} + +-- +-- User-visible error_name -> error_number dictionary. +-- +local error_code = {} +for code, err in pairs(error_message_template) do + assert(type(code) == 'number') + assert(error_code[err.name] == nil, "Dublicate error name") + error_code[err.name] = code +end + -- -- There are 2 error types: -- * box_error - it is created on tarantool errors: client error, @@ -11,19 +116,43 @@ local json = require('json') -- 'ShardingError', one of codes below and optional -- message. -- - local function box_error(original_error) return setmetatable(original_error:unpack(), {__tostring = json.encode}) end -local function vshard_error(code, args, msg) - local ret = setmetatable({type = 'ShardingError', code = code, - message = msg}, {__tostring = json.encode}) - if args then - for k, v in pairs(args) do - ret[k] = v - end +-- +-- Construct an vstard error. +-- @param code Number, one of error_code constants. +-- @param ... Arguments from `error_message_template` `arg` field. +-- Caller have to pass at least as many arguments as +-- `msg` field requires. +-- @retval ShardingError object. +-- +local function vshard_error(code, ...) + local format = error_message_template[code] + assert(format, 'Error message format not found.') + local args_passed_cnt = select('#', ...) + local args = format.args or {} + assert(#args >= args_passed_cnt, + string.format('Wrong number of arguments passed to %s error', + format.name)) + local ret = setmetatable({}, {__tostring = json.encode}) + -- save error arguments + for i = 1, #args do + ret[args[i]] = select(i, ...) + end + local msg + if format.msg then + local ok + ok, msg = pcall(string.format, format.msg, ...) + assert(ok) + else + msg = string.format('%s: %s', format.name, json.encode(tostring(ret))) end + ret.type = 'ShardingError' + ret.code = code + ret.name = format.name + ret.message = msg return ret end @@ -45,70 +174,6 @@ local function make_error(e) end end -local error_code = { - WRONG_BUCKET = 1, - NON_MASTER = 2, - BUCKET_ALREADY_EXISTS = 3, - NO_SUCH_REPLICASET = 4, - MOVE_TO_SELF = 5, - MISSING_MASTER = 6, - TRANSFER_IS_IN_PROGRESS = 7, - UNREACHABLE_REPLICASET = 8, - NO_ROUTE_TO_BUCKET = 9, - NON_EMPTY = 10, - UNREACHABLE_MASTER = 11, - OUT_OF_SYNC = 12, - HIGH_REPLICATION_LAG = 13, - UNREACHABLE_REPLICA = 14, - LOW_REDUNDANCY = 15, - INVALID_REBALANCING = 16, - SUBOPTIMAL_REPLICA = 17, - UNKNOWN_BUCKETS = 18, - REPLICASET_IS_LOCKED = 19, -} - -local error_message_template = { - [error_code.MISSING_MASTER] = { - name = 'MISSING_MASTER', - msg = 'Master is not configured for replicaset %s' - }, - [error_code.UNREACHABLE_MASTER] = { - name = 'UNREACHABLE_MASTER', - msg = 'Master of replicaset %s is unreachable: %s' - }, - [error_code.OUT_OF_SYNC] = { - name = 'OUT_OF_SYNC', - msg = 'Replica is out of sync' - }, - [error_code.HIGH_REPLICATION_LAG] = { - name = 'HIGH_REPLICATION_LAG', - msg = 'High replication lag: %f' - }, - [error_code.UNREACHABLE_REPLICA] = { - name = 'UNREACHABLE_REPLICA', - msg = "Replica %s isn't active" - }, - [error_code.UNREACHABLE_REPLICASET] = { - name = 'UNREACHABLE_REPLICASET', - msg = 'There is no active replicas in replicaset %s' - }, - [error_code.LOW_REDUNDANCY] = { - name = 'LOW_REDUNDANCY', - msg = 'Only one replica is active' - }, - [error_code.INVALID_REBALANCING] = { - name = 'INVALID_REBALANCING', - msg = 'Sending and receiving buckets at same time is not allowed' - }, - [error_code.SUBOPTIMAL_REPLICA] = { - name = 'SUBOPTIMAL_REPLICA', - msg = 'A current read replica in replicaset %s is not optimal' - }, - [error_code.UNKNOWN_BUCKETS] = { - name = 'UNKNOWN_BUCKETS', - msg = '%d buckets are not discovered', - } -} local function make_alert(code, ...) local format = error_message_template[code] diff --git a/vshard/replicaset.lua b/vshard/replicaset.lua index 7fcf2df..99f59aa 100644 --- a/vshard/replicaset.lua +++ b/vshard/replicaset.lua @@ -114,8 +114,8 @@ end local function replicaset_connect_master(replicaset) local master = replicaset.master if master == nil then - return nil, lerror.vshard(lerror.code.MASTER_IS_MISSING, - {replicaset_uuid = replicaset.uuid}) + return nil, lerror.vshard(lerror.code.MISSING_MASTER, + replicaset.uuid) end return replicaset_connect_to_replica(replicaset, master) end diff --git a/vshard/router/init.lua b/vshard/router/init.lua index 42e9c86..95ee8b3 100644 --- a/vshard/router/init.lua +++ b/vshard/router/init.lua @@ -85,8 +85,7 @@ local function bucket_discovery(bucket_id) if last_err.type == 'ClientError' and last_err.code == box.error.NO_CONNECTION then err = lerror.vshard(lerror.code.UNREACHABLE_REPLICASET, - {bucket_id = bucket_id, - unreachable_uuid = unreachable_uuid}) + unreachable_uuid, bucket_id) else err = lerror.make(last_err) end @@ -97,9 +96,8 @@ local function bucket_discovery(bucket_id) -- bucket was found to be RECEIVING on one replicaset, and -- was not found on other replicasets (it was sent during -- discovery). - err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, - {bucket_id = bucket_id, - unreachable_uuid = unreachable_uuid}) + err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, bucket_id, + unreachable_uuid) end return nil, err @@ -519,8 +517,7 @@ local function cluster_bootstrap() return nil, err end if count > 0 then - return nil, lerror.vshard(lerror.code.NON_EMPTY, {}, - 'Cluster is already bootstrapped') + return nil, lerror.vshard(lerror.code.NON_EMPTY) end end lreplicaset.calculate_etalon_balance(M.replicasets, M.total_bucket_count) diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index d5e7b57..2d38ff9 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -452,8 +452,7 @@ local function bucket_check_state(bucket_id, mode) mode == 'write' and bucket_is_writable(bucket)) ::finish:: return bucket, errcode and - lerror.vshard(errcode, {bucket_id = bucket_id, - destination = bucket and bucket.destination}) + lerror.vshard(errcode, bucket_id, bucket and bucket.destination) end -- @@ -514,8 +513,7 @@ local function bucket_recv(bucket_id, from, data) local bucket = box.space._bucket:get({bucket_id}) if bucket ~= nil then - return nil, lerror.vshard(lerror.code.BUCKET_ALREADY_EXISTS, - {bucket_id = bucket_id}) + return nil, lerror.vshard(lerror.code.BUCKET_ALREADY_EXISTS, bucket_id) end box.begin() @@ -719,14 +717,12 @@ local function bucket_send(bucket_id, destination) end local replicaset = M.replicasets[destination] if replicaset == nil then - return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, - {replicaset_uuid = destination}) + return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, destination) end if destination == box.info.cluster.uuid then - return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, - {bucket_id = bucket_id, - replicaset_uuid = replicaset_uuid}) + return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, replicaset_uuid, + bucket_id) end local data = bucket_collect_internal(bucket_id) @@ -1260,8 +1256,7 @@ end -- local function rebalancer_apply_routes(routes) if is_this_replicaset_locked() then - return false, lerror.vshard(lerror.code.REPLICASET_IS_LOCKED, nil, - "Replicaset is locked"); + return false, lerror.vshard(lerror.code.REPLICASET_IS_LOCKED); end assert(not rebalancing_is_in_progress()) -- Can not apply routes here because of gh-946 in tarantool -- 2.14.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 2/3] Refactor error reporting system 2018-06-06 15:57 ` [tarantool-patches] [PATCH 2/3] Refactor error reporting system AKhatskevich @ 2018-06-07 12:22 ` Vladislav Shpilevoy 2018-06-07 16:13 ` Alex Khatskevich 0 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2018-06-07 12:22 UTC (permalink / raw) To: tarantool-patches, AKhatskevich Thanks for the patch! See 12 comments below. On 06/06/2018 18:57, AKhatskevich wrote: > This commit applies stricter rules on error reporting: > * error may has only arguments specified in `args` field of > `error_message_template` > * arguments may be used in error message > * arguments are saved in error object by names from `args` > > Part of #100 > --- > test/misc/check_uuid_on_connect.result | 8 +- > test/router/reroute_wrong_bucket.result | 10 +- > test/router/router.result | 42 ++++--- > test/storage/storage.result | 6 +- > test/unit/error.result | 6 +- > test/unit/error.test.lua | 2 +- > vshard/error.lua | 209 +++++++++++++++++++++----------- > vshard/replicaset.lua | 4 +- > vshard/router/init.lua | 11 +- > vshard/storage/init.lua | 17 +-- > 10 files changed, 197 insertions(+), 118 deletions(-) > > diff --git a/vshard/error.lua b/vshard/error.lua > index 8d26d11..1271d16 100644 > --- a/vshard/error.lua > +++ b/vshard/error.lua > @@ -1,6 +1,111 @@ > local ffi = require('ffi') > local json = require('json') > > +-- > +-- Error messages description. > +-- * name -- Key by which an error code can be retrieved from > +-- exprted `code` dictionary. 1. exprted -> ? > +-- * msg -- Error message which can cantain parameters described 2. cantain -> contain. And it must contain. Not can. > +-- in `string.format` notation. > +-- * args -- Names of arguments passed while constructing an > +-- error. After constructed, an error-object contains provided > +-- arguments by the names specified in the field. > +-- > +local error_message_template = { > + [1] = { > + name = 'WRONG_BUCKET', > + args = {'bucket_id', 'destination'} > + }, > + [2] = { > + name = 'NON_MASTER', > + args = {'bucket_id', 'destination'} > + }, > + [3] = { > + name = 'BUCKET_ALREADY_EXISTS', > + args = {'bucket_id'} > + }, > + [4] = { > + name = 'NO_SUCH_REPLICASET', > + args = {'replicaset_uuid'} > + }, > + [5] = { > + name = 'MOVE_TO_SELF', > + args = {'replicaset_uuid', 'bucket_id'} > + }, > + [6] = { > + name = 'MISSING_MASTER', > + msg = 'Master is not configured for replicaset %s', > + args = {'replicaset_uuid'} > + }, > + [7] = { > + name = 'TRANSFER_IS_IN_PROGRESS', > + args = {'bucket_id', 'destination'} > + }, > + [8] = { > + name = 'UNREACHABLE_REPLICASET', > + msg = 'There is no active replicas in replicaset %s', > + args = {'unreachable_uuid', 'bucket_id'} > + }, > + [9] = { > + name = 'NO_ROUTE_TO_BUCKET', > + args = {'bucket_id', 'unreachable_uuid'} 3. You have found that uuid is always nil for NO_ROUTE_TO_BUCKET. Why do you need it in args? > @@ -11,19 +116,43 @@ local json = require('json') > -- 'ShardingError', one of codes below and optional > -- message. > -- > - 4. Garbage diff. And this empty line was put here deliberately. The comment refers to both box_error and vshard_error. > local function box_error(original_error) > return setmetatable(original_error:unpack(), {__tostring = json.encode}) > end > > -local function vshard_error(code, args, msg) > - local ret = setmetatable({type = 'ShardingError', code = code, > - message = msg}, {__tostring = json.encode}) > - if args then > - for k, v in pairs(args) do > - ret[k] = v > - end > +-- > +-- Construct an vstard error. 5. vstard -> vshard. > +-- @param code Number, one of error_code constants. > +-- @param ... Arguments from `error_message_template` `arg` field. 6. arg -> args. Error objects above has args attribute. > +-- Caller have to pass at least as many arguments as > +-- `msg` field requires. > +-- @retval ShardingError object. > +-- > +local function vshard_error(code, ...) > + local format = error_message_template[code] > + assert(format, 'Error message format not found.') > + local args_passed_cnt = select('#', ...) > + local args = format.args or {} > + assert(#args >= args_passed_cnt, > + string.format('Wrong number of arguments passed to %s error', > + format.name))> + local ret = setmetatable({}, {__tostring = json.encode}) > + -- save error arguments 7. Please, start a sentence with capital letter and finish with dot. > + for i = 1, #args do > + ret[args[i]] = select(i, ...) > + end > + local msg > + if format.msg then > + local ok > + ok, msg = pcall(string.format, format.msg, ...) > + assert(ok) 8. Why? Either the pcall returns an error and you fail in the assert, or simple unprotected string.format() fails. No difference. Just to simple string.format(). 9. You do not need local msg here. > + else > + msg = string.format('%s: %s', format.name, json.encode(tostring(ret))) 10. Why do you need encode(tostring())? Ret already has __tostring = json.encode, so just do this: string.format('%s: %s', format.name, ret) Diff is below. I did not run the tests. Please, apply and fix the tests if needed. @@ -141,18 +141,14 @@ local function vshard_error(code, ...) for i = 1, #args do ret[args[i]] = select(i, ...) end - local msg if format.msg then - local ok - ok, msg = pcall(string.format, format.msg, ...) - assert(ok) + ret.message = string.format(format.msg, ...) else - msg = string.format('%s: %s', format.name, json.encode(tostring(ret))) + ret.message = string.format('%s: %s', format.name, ret) end ret.type = 'ShardingError' ret.code = code ret.name = format.name - ret.message = msg return ret end > diff --git a/vshard/router/init.lua b/vshard/router/init.lua > index 42e9c86..95ee8b3 100644 > --- a/vshard/router/init.lua > +++ b/vshard/router/init.lua > @@ -97,9 +96,8 @@ local function bucket_discovery(bucket_id) > -- bucket was found to be RECEIVING on one replicaset, and > -- was not found on other replicasets (it was sent during > -- discovery). > - err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, > - {bucket_id = bucket_id, > - unreachable_uuid = unreachable_uuid}) > + err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, bucket_id, > + unreachable_uuid) 11. Unreachable uuid is always nil as we have found. > end > > return nil, err > @@ -719,14 +717,12 @@ local function bucket_send(bucket_id, destination) > end > local replicaset = M.replicasets[destination] > if replicaset == nil then > - return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, > - {replicaset_uuid = destination}) > + return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, destination) > end 12. NO_SUCH_REPLICASET has two arguments. And you have assert(#args >= args_passed_cnt), that has not been failed. It means, that bucket_send has no a test on sending to non-existing replicaset. Please, * add a test on bucket_send to non-existing replicaset; * add unit tests on error building. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 2/3] Refactor error reporting system 2018-06-07 12:22 ` [tarantool-patches] " Vladislav Shpilevoy @ 2018-06-07 16:13 ` Alex Khatskevich 0 siblings, 0 replies; 11+ messages in thread From: Alex Khatskevich @ 2018-06-07 16:13 UTC (permalink / raw) To: Vladislav Shpilevoy, tarantool-patches >> +-- * name -- Key by which an error code can be retrieved from >> +-- exprted `code` dictionary. > 1. exprted -> ? Fixed. -- * name -- Key by which an error code can be retrieved from -- the expoted by the module `code` dictionary. >> +-- * msg -- Error message which can cantain parameters described > 2. cantain -> contain. And it must contain. Not can. fixed -- * msg -- Error message which can use `args` using -- `string.format` notation. > 3. You have found that uuid is always nil for NO_ROUTE_TO_BUCKET. > Why do you need it in args? Fixed > 4. Garbage diff. And this empty line was put here deliberately. > The comment refers to both box_error and vshard_error. fixed. > 5. vstard -> vshard. Fixed > 6. arg -> args. Error objects above has args attribute. fixed > 7. Please, start a sentence with capital letter and finish with dot. Ok. > 8. Why? Either the pcall returns an error and you fail in the assert, > or simple unprotected string.format() fails. No difference. Just to > simple string.format(). fixed. > 9. You do not need local msg here. fixed >> + else >> + msg = string.format('%s: %s', format.name, >> json.encode(tostring(ret))) > > 10. Why do you need encode(tostring())? Ret already has __tostring = > json.encode, > so just do this: > > string.format('%s: %s', format.name, ret) > > Diff is below. I did not run the tests. Please, apply and fix the > tests if > needed. > > @@ -141,18 +141,14 @@ local function vshard_error(code, ...) > for i = 1, #args do > ret[args[i]] = select(i, ...) > end > - local msg > if format.msg then > - local ok > - ok, msg = pcall(string.format, format.msg, ...) > - assert(ok) > + ret.message = string.format(format.msg, ...) > else > - msg = string.format('%s: %s', format.name, > json.encode(tostring(ret))) > + ret.message = string.format('%s: %s', format.name, ret) > end > ret.type = 'ShardingError' > ret.code = code > ret.name = format.name > - ret.message = msg > return ret > end fixed > 11. Unreachable uuid is always nil as we have found. Fixed >> end >> return nil, err >> @@ -719,14 +717,12 @@ local function bucket_send(bucket_id, destination) >> end >> local replicaset = M.replicasets[destination] >> if replicaset == nil then >> - return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, >> - {replicaset_uuid = destination}) >> + return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, >> destination) >> end > > 12. NO_SUCH_REPLICASET has two arguments. And you have assert(#args >= > args_passed_cnt), > that has not been failed. It means, that bucket_send has no a test on > sending to > non-existing replicaset. Please, > * add a test on bucket_send to non-existing replicaset; > * add unit tests on error building. New diff: commit af0bfccfbba87a8dcccd71f0dd7d2f8b6c2317cf Author: AKhatskevich <avkhatskevich@tarantool.org> Date: Tue Jun 5 17:05:15 2018 +0300 Refactor error reporting system This commit applies stricter rules on error reporting: * error may has only arguments specified in `args` field of `error_message_template` * arguments may be used in error message * arguments are saved in error object by names from `args` Extra: * testcase for NO_SUCH_REPLICASET added Part of #100 diff --git a/test/misc/check_uuid_on_connect.result b/test/misc/check_uuid_on_connect.result index d54c367..6ac2353 100644 --- a/test/misc/check_uuid_on_connect.result +++ b/test/misc/check_uuid_on_connect.result @@ -182,10 +182,12 @@ test_run:switch('bad_uuid_router') vshard.router.bucket_discovery(1) --- - null -- type: ShardingError - unreachable_uuid: ac522f65-aa94-4134-9f64-51ee384f1a54 +- bucket_id: 1 code: 8 - bucket_id: 1 + unreachable_uuid: ac522f65-aa94-4134-9f64-51ee384f1a54 + name: UNREACHABLE_REPLICASET + message: There is no active replicas in replicaset ac522f65-aa94-4134-9f64-51ee384f1a54 + type: ShardingError ... -- Ok to work with correct replicasets. vshard.router.bucket_discovery(2).uuid diff --git a/test/router/reroute_wrong_bucket.result b/test/router/reroute_wrong_bucket.result index eda3bab..f8021f2 100644 --- a/test/router/reroute_wrong_bucket.result +++ b/test/router/reroute_wrong_bucket.result @@ -109,9 +109,11 @@ test_run:switch('router_1') vshard.router.call(100, 'read', 'customer_lookup', {1}, {timeout = 1}) --- - null -- type: ShardingError - bucket_id: 100 +- bucket_id: 100 code: 9 + type: ShardingError + message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":100}' + name: NO_ROUTE_TO_BUCKET ... -- Wait reconfiguration durigin timeout, if a replicaset was not -- found by bucket.destination from WRONG_BUCKET or @@ -188,7 +190,8 @@ test_run:grep_log('router_1', 'please update configuration') ... err --- -- {'type': 'ShardingError', 'bucket_id': 100, 'code': 1, 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54'} +- {'bucket_id': 100, 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', + 'name': 'WRONG_BUCKET', 'message': 'WRONG_BUCKET: {"bucket_id":100,"destination":"ac522f65-aa94-4134-9f64-51ee384f1a54"}'} ... -- -- Now try again, but update configuration during call(). It must diff --git a/test/router/router.result b/test/router/router.result index 5f3ceca..5d99255 100644 --- a/test/router/router.result +++ b/test/router/router.result @@ -195,15 +195,19 @@ vshard.router.bucket_count() util.check_error(vshard.router.call, 1, 'read', 'echo', {123}) --- - null -- type: ShardingError - bucket_id: 1 +- bucket_id: 1 code: 9 + type: ShardingError + message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":1}' + name: NO_ROUTE_TO_BUCKET ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err --- -- type: ShardingError - bucket_id: 1 +- bucket_id: 1 code: 9 + type: ShardingError + message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":1}' + name: NO_ROUTE_TO_BUCKET ... vshard.router.bootstrap() --- @@ -214,8 +218,9 @@ vshard.router.bootstrap() --- - null - type: ShardingError - code: 10 + name: NON_EMPTY message: Cluster is already bootstrapped + code: 10 ... -- -- gh-108: negative bucket count on discovery. @@ -312,9 +317,11 @@ util.check_error(vshard.router.call, 0, 'read', 'echo', {123}) ... replicaset, err = vshard.router.bucket_discovery(0); return err == nil or err --- -- type: ShardingError - bucket_id: 0 +- bucket_id: 0 code: 9 + type: ShardingError + message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":0}' + name: NO_ROUTE_TO_BUCKET ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err --- @@ -353,7 +360,8 @@ vshard.router.call(1, 'read', 'echo', {123}) util.check_error(vshard.router.call, 1, 'write', 'echo', {123}) --- - null -- {'type': 'ShardingError', 'bucket_id': 1, 'code': 7, 'destination': '<replicaset_1>'} +- {'bucket_id': 1, 'code': 7, 'type': 'ShardingError', 'destination': '<replicaset_1>', + 'name': 'TRANSFER_IS_IN_PROGRESS', 'message': 'TRANSFER_IS_IN_PROGRESS: {"bucket_id":1,"destination":"<replicaset_1>"}'} ... test_run:cmd('switch storage_2_a') --- @@ -397,10 +405,12 @@ test_run:cmd('stop server storage_2_a') util.check_error(vshard.router.call, 1, 'read', 'echo', {123}) --- - null -- type: ShardingError - unreachable_uuid: <replicaset_2> +- bucket_id: 1 code: 8 - bucket_id: 1 + unreachable_uuid: <replicaset_2> + name: UNREACHABLE_REPLICASET + message: There is no active replicas in replicaset <replicaset_2> + type: ShardingError ... vshard.router.buckets_info(0, 3) --- @@ -420,9 +430,11 @@ test_run:cmd('start server storage_2_a') vshard.router.route(vshard.consts.DEFAULT_BUCKET_COUNT + 100) --- - null -- type: ShardingError - bucket_id: 3100 +- bucket_id: 3100 code: 9 + type: ShardingError + message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":3100}' + name: NO_ROUTE_TO_BUCKET ... util.check_error(vshard.router.route, 'asdfg') --- @@ -956,7 +968,8 @@ test_run:cmd("switch router_1") util.check_error(vshard.router.call, 1, 'write', 'echo', { 'hello world' }) --- - null -- {'type': 'ShardingError', 'code': 2, 'bucket_id': 1} +- {'bucket_id': 1, 'code': 2, 'type': 'ShardingError', 'message': 'NON_MASTER: {"bucket_id":1}', + 'name': 'NON_MASTER'} ... -- Reconfigure router and test that the WRITE request does work vshard.router.cfg(cfg) diff --git a/test/storage/storage.result b/test/storage/storage.result index e93ad24..51492a2 100644 --- a/test/storage/storage.result +++ b/test/storage/storage.result @@ -594,9 +594,11 @@ vshard.storage.call(1, 'read', 'customer_lookup', {1}) vshard.storage.call(100500, 'read', 'customer_lookup', {1}) --- - null -- type: ShardingError - bucket_id: 100500 +- bucket_id: 100500 code: 1 + type: ShardingError + message: 'WRONG_BUCKET: {"bucket_id":100500}' + name: WRONG_BUCKET ... -- -- Test not existing space in bucket data. @@ -614,6 +616,17 @@ vshard.storage.bucket_recv(100, 'from_uuid', {{1000, {{1}}}}) -- -- Bucket transfer -- +-- Transfer to unknown replicaset. +vshard.storage.bucket_send(1, 'unknown uuid') +--- +- null +- code: 4 + type: ShardingError + name: NO_SUCH_REPLICASET + replicaset_uuid: unknown uuid + message: 'NO_SUCH_REPLICASET: {"replicaset_uuid":"unknown uuid"}' +... +-- Successful transfer. vshard.storage.bucket_send(1, replicaset2_uuid) --- - true diff --git a/test/storage/storage.test.lua b/test/storage/storage.test.lua index 948decc..f4bbf0e 100644 --- a/test/storage/storage.test.lua +++ b/test/storage/storage.test.lua @@ -137,6 +137,11 @@ vshard.storage.bucket_recv(100, 'from_uuid', {{1000, {{1}}}}) -- -- Bucket transfer -- + +-- Transfer to unknown replicaset. +vshard.storage.bucket_send(1, 'unknown uuid') + +-- Successful transfer. vshard.storage.bucket_send(1, replicaset2_uuid) _ = test_run:cmd("switch storage_2_a") vshard.storage.buckets_info() diff --git a/test/unit/error.result b/test/unit/error.result index a46fb0d..74571ca 100644 --- a/test/unit/error.result +++ b/test/unit/error.result @@ -20,13 +20,13 @@ tostring(box_error) --- - '{"type":"ClientError","code":78,"message":"Timeout exceeded","trace":[{"file":"[C]","line":4294967295}]}' ... -vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, {uuid = 'uuid', reason = 'reason'}, 'message 1 2 3') +vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, 'uuid', 'reason') --- ... tostring(vshard_error) --- -- '{"reason":"reason","code":11,"type":"ShardingError","uuid":"uuid","message":"message - 1 2 3"}' +- '{"reason":"reason","code":11,"type":"ShardingError","name":"UNREACHABLE_MASTER","uuid":"uuid","message":"Master + of replicaset uuid is unreachable: reason"}' ... log = require('log') --- @@ -38,6 +38,42 @@ test_run:grep_log('default', '"reason":"reason","code":11,"type":"ShardingError" --- - '"reason":"reason","code":11,"type":"ShardingError"' ... +-- +-- Part of gh-100: check `error.vshard`. +-- +lerror.vshard(lerror.code.WRONG_BUCKET, 'arg1', 'arg2') +--- +- bucket_id: arg1 + code: 1 + type: ShardingError + name: WRONG_BUCKET + message: 'WRONG_BUCKET: {"bucket_id":"arg1","destination":"arg2"}' + destination: arg2 +... +-- Pass less args than msg requires. +ok, res = pcall(lerror.vshard, lerror.code.MISSING_MASTER) +--- +... +res:match('bad argument #2.*') +--- +- 'bad argument #2 to ''format'' (no value)' +... +-- Pass more args than `args` field contains. +ok, res = pcall(lerror.vshard, lerror.code.MISSING_MASTER, 'arg1', 'arg2') +--- +... +res:match('Wrong number of.*') +--- +- Wrong number of arguments passed to MISSING_MASTER error +... +-- Pass wrong format code. +ok, res = pcall(lerror.vshard, 'Wrong format code', 'arg1', 'arg2') +--- +... +res:match('Error message format not found.') +--- +- Error message format not found. +... function raise_lua_err() assert(false) end --- ... diff --git a/test/unit/error.test.lua b/test/unit/error.test.lua index 4b5c20f..7496982 100644 --- a/test/unit/error.test.lua +++ b/test/unit/error.test.lua @@ -9,13 +9,27 @@ ok, err = pcall(box.error, box.error.TIMEOUT) box_error = lerror.box(err) tostring(box_error) -vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, {uuid = 'uuid', reason = 'reason'}, 'message 1 2 3') +vshard_error = lerror.vshard(lerror.code.UNREACHABLE_MASTER, 'uuid', 'reason') tostring(vshard_error) log = require('log') log.info('Log error: %s', vshard_error) test_run:grep_log('default', '"reason":"reason","code":11,"type":"ShardingError"') +-- +-- Part of gh-100: check `error.vshard`. +-- +lerror.vshard(lerror.code.WRONG_BUCKET, 'arg1', 'arg2') +-- Pass less args than msg requires. +ok, res = pcall(lerror.vshard, lerror.code.MISSING_MASTER) +res:match('bad argument #2.*') +-- Pass more args than `args` field contains. +ok, res = pcall(lerror.vshard, lerror.code.MISSING_MASTER, 'arg1', 'arg2') +res:match('Wrong number of.*') +-- Pass wrong format code. +ok, res = pcall(lerror.vshard, 'Wrong format code', 'arg1', 'arg2') +res:match('Error message format not found.') + function raise_lua_err() assert(false) end ok, err = pcall(raise_lua_err) lerror.make(err) diff --git a/vshard/error.lua b/vshard/error.lua index 8d26d11..59e4fac 100644 --- a/vshard/error.lua +++ b/vshard/error.lua @@ -1,6 +1,111 @@ local ffi = require('ffi') local json = require('json') +-- +-- Error messages description. +-- * name -- Key by which an error code can be retrieved from +-- the expoted by the module `code` dictionary. +-- * msg -- Error message which can use `args` using +-- `string.format` notation. +-- * args -- Names of arguments passed while constructing an +-- error. After constructed, an error-object contains provided +-- arguments by the names specified in the field. +-- +local error_message_template = { + [1] = { + name = 'WRONG_BUCKET', + args = {'bucket_id', 'destination'} + }, + [2] = { + name = 'NON_MASTER', + args = {'bucket_id', 'destination'} + }, + [3] = { + name = 'BUCKET_ALREADY_EXISTS', + args = {'bucket_id'} + }, + [4] = { + name = 'NO_SUCH_REPLICASET', + args = {'replicaset_uuid'} + }, + [5] = { + name = 'MOVE_TO_SELF', + args = {'replicaset_uuid', 'bucket_id'} + }, + [6] = { + name = 'MISSING_MASTER', + msg = 'Master is not configured for replicaset %s', + args = {'replicaset_uuid'} + }, + [7] = { + name = 'TRANSFER_IS_IN_PROGRESS', + args = {'bucket_id', 'destination'} + }, + [8] = { + name = 'UNREACHABLE_REPLICASET', + msg = 'There is no active replicas in replicaset %s', + args = {'unreachable_uuid', 'bucket_id'} + }, + [9] = { + name = 'NO_ROUTE_TO_BUCKET', + args = {'bucket_id'} + }, + [10] = { + name = 'NON_EMPTY', + msg = 'Cluster is already bootstrapped' + }, + [11] = { + name = 'UNREACHABLE_MASTER', + msg = 'Master of replicaset %s is unreachable: %s', + args = {'uuid', 'reason'} + }, + [12] = { + name = 'OUT_OF_SYNC', + msg = 'Replica is out of sync' + }, + [13] = { + name = 'HIGH_REPLICATION_LAG', + msg = 'High replication lag: %f', + args = {'lag'} + }, + [14] = { + name = 'UNREACHABLE_REPLICA', + msg = "Replica %s isn't active", + args = {'unreachable_uuid'} + }, + [15] = { + name = 'LOW_REDUNDANCY', + msg = 'Only one replica is active' + }, + [16] = { + name = 'INVALID_REBALANCING', + msg = 'Sending and receiving buckets at same time is not allowed' + }, + [17] = { + name = 'SUBOPTIMAL_REPLICA', + msg = 'A current read replica in replicaset %s is not optimal' + }, + [18] = { + name = 'UNKNOWN_BUCKETS', + msg = '%d buckets are not discovered', + arg = {'not_discovered_cnt'} + }, + [19] = { + name = 'REPLICASET_IS_LOCKED', + msg = 'Replicaset is locked' + } +} + +-- +-- User-visible error_name -> error_number dictionary. +-- +local error_code = {} +for code, err in pairs(error_message_template) do + assert(type(code) == 'number') + assert(error_code[err.name] == nil, "Dublicate error name") + error_code[err.name] = code +end + -- -- There are 2 error types: -- * box_error - it is created on tarantool errors: client error, @@ -16,14 +121,35 @@ local function box_error(original_error) return setmetatable(original_error:unpack(), {__tostring = json.encode}) end -local function vshard_error(code, args, msg) - local ret = setmetatable({type = 'ShardingError', code = code, - message = msg}, {__tostring = json.encode}) - if args then - for k, v in pairs(args) do - ret[k] = v - end +-- +-- Construct an vshard error. +-- @param code Number, one of error_code constants. +-- @param ... Arguments from `error_message_template` `args` +-- field. Caller have to pass at least as many arguments +-- as `msg` field requires. +-- @retval ShardingError object. +-- +local function vshard_error(code, ...) + local format = error_message_template[code] + assert(format, 'Error message format not found.') + local args_passed_cnt = select('#', ...) + local args = format.args or {} + assert(#args >= args_passed_cnt, + string.format('Wrong number of arguments passed to %s error', + format.name)) + local ret = setmetatable({}, {__tostring = json.encode}) + -- Save error arguments. + for i = 1, #args do + ret[args[i]] = select(i, ...) end + if format.msg then + ret.message = string.format(format.msg, ...) + else + ret.message = string.format('%s: %s', format.name, ret) + end + ret.type = 'ShardingError' + ret.code = code + ret.name = format.name return ret end @@ -45,70 +171,6 @@ local function make_error(e) end end -local error_code = { - WRONG_BUCKET = 1, - NON_MASTER = 2, - BUCKET_ALREADY_EXISTS = 3, - NO_SUCH_REPLICASET = 4, - MOVE_TO_SELF = 5, - MISSING_MASTER = 6, - TRANSFER_IS_IN_PROGRESS = 7, - UNREACHABLE_REPLICASET = 8, - NO_ROUTE_TO_BUCKET = 9, - NON_EMPTY = 10, - UNREACHABLE_MASTER = 11, - OUT_OF_SYNC = 12, - HIGH_REPLICATION_LAG = 13, - UNREACHABLE_REPLICA = 14, - LOW_REDUNDANCY = 15, - INVALID_REBALANCING = 16, - SUBOPTIMAL_REPLICA = 17, - UNKNOWN_BUCKETS = 18, - REPLICASET_IS_LOCKED = 19, -} - -local error_message_template = { - [error_code.MISSING_MASTER] = { - name = 'MISSING_MASTER', - msg = 'Master is not configured for replicaset %s' - }, - [error_code.UNREACHABLE_MASTER] = { - name = 'UNREACHABLE_MASTER', - msg = 'Master of replicaset %s is unreachable: %s' - }, - [error_code.OUT_OF_SYNC] = { - name = 'OUT_OF_SYNC', - msg = 'Replica is out of sync' - }, - [error_code.HIGH_REPLICATION_LAG] = { - name = 'HIGH_REPLICATION_LAG', - msg = 'High replication lag: %f' - }, - [error_code.UNREACHABLE_REPLICA] = { - name = 'UNREACHABLE_REPLICA', - msg = "Replica %s isn't active" - }, - [error_code.UNREACHABLE_REPLICASET] = { - name = 'UNREACHABLE_REPLICASET', - msg = 'There is no active replicas in replicaset %s' - }, - [error_code.LOW_REDUNDANCY] = { - name = 'LOW_REDUNDANCY', - msg = 'Only one replica is active' - }, - [error_code.INVALID_REBALANCING] = { - name = 'INVALID_REBALANCING', - msg = 'Sending and receiving buckets at same time is not allowed' - }, - [error_code.SUBOPTIMAL_REPLICA] = { - name = 'SUBOPTIMAL_REPLICA', - msg = 'A current read replica in replicaset %s is not optimal' - }, - [error_code.UNKNOWN_BUCKETS] = { - name = 'UNKNOWN_BUCKETS', - msg = '%d buckets are not discovered', - } -} local function make_alert(code, ...) local format = error_message_template[code] diff --git a/vshard/replicaset.lua b/vshard/replicaset.lua index 7fcf2df..99f59aa 100644 --- a/vshard/replicaset.lua +++ b/vshard/replicaset.lua @@ -114,8 +114,8 @@ end local function replicaset_connect_master(replicaset) local master = replicaset.master if master == nil then - return nil, lerror.vshard(lerror.code.MASTER_IS_MISSING, - {replicaset_uuid = replicaset.uuid}) + return nil, lerror.vshard(lerror.code.MISSING_MASTER, + replicaset.uuid) end return replicaset_connect_to_replica(replicaset, master) end diff --git a/vshard/router/init.lua b/vshard/router/init.lua index 42e9c86..21093e5 100644 --- a/vshard/router/init.lua +++ b/vshard/router/init.lua @@ -85,8 +85,7 @@ local function bucket_discovery(bucket_id) if last_err.type == 'ClientError' and last_err.code == box.error.NO_CONNECTION then err = lerror.vshard(lerror.code.UNREACHABLE_REPLICASET, - {bucket_id = bucket_id, - unreachable_uuid = unreachable_uuid}) + unreachable_uuid, bucket_id) else err = lerror.make(last_err) end @@ -97,9 +96,7 @@ local function bucket_discovery(bucket_id) -- bucket was found to be RECEIVING on one replicaset, and -- was not found on other replicasets (it was sent during -- discovery). - err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, - {bucket_id = bucket_id, - unreachable_uuid = unreachable_uuid}) + err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, bucket_id) end return nil, err @@ -519,8 +516,7 @@ local function cluster_bootstrap() return nil, err end if count > 0 then - return nil, lerror.vshard(lerror.code.NON_EMPTY, {}, - 'Cluster is already bootstrapped') + return nil, lerror.vshard(lerror.code.NON_EMPTY) end end lreplicaset.calculate_etalon_balance(M.replicasets, M.total_bucket_count) diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index d5e7b57..2d38ff9 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -452,8 +452,7 @@ local function bucket_check_state(bucket_id, mode) mode == 'write' and bucket_is_writable(bucket)) ::finish:: return bucket, errcode and - lerror.vshard(errcode, {bucket_id = bucket_id, - destination = bucket and bucket.destination}) + lerror.vshard(errcode, bucket_id, bucket and bucket.destination) end -- @@ -514,8 +513,7 @@ local function bucket_recv(bucket_id, from, data) local bucket = box.space._bucket:get({bucket_id}) if bucket ~= nil then - return nil, lerror.vshard(lerror.code.BUCKET_ALREADY_EXISTS, - {bucket_id = bucket_id}) + return nil, lerror.vshard(lerror.code.BUCKET_ALREADY_EXISTS, bucket_id) end box.begin() @@ -719,14 +717,12 @@ local function bucket_send(bucket_id, destination) end local replicaset = M.replicasets[destination] if replicaset == nil then - return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, - {replicaset_uuid = destination}) + return nil, lerror.vshard(lerror.code.NO_SUCH_REPLICASET, destination) end if destination == box.info.cluster.uuid then - return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, - {bucket_id = bucket_id, - replicaset_uuid = replicaset_uuid}) + return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, replicaset_uuid, + bucket_id) end local data = bucket_collect_internal(bucket_id) @@ -1260,8 +1256,7 @@ end -- local function rebalancer_apply_routes(routes) if is_this_replicaset_locked() then - return false, lerror.vshard(lerror.code.REPLICASET_IS_LOCKED, nil, - "Replicaset is locked"); + return false, lerror.vshard(lerror.code.REPLICASET_IS_LOCKED); end assert(not rebalancing_is_in_progress()) -- Can not apply routes here because of gh-946 in tarantool ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] [PATCH 3/3] Add error messages 2018-06-06 15:57 [tarantool-patches] [PATCH 0/3][vshard] Add error messages AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 1/3] Fix test on bucket_transferring error AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 2/3] Refactor error reporting system AKhatskevich @ 2018-06-06 15:57 ` AKhatskevich 2018-06-07 12:22 ` [tarantool-patches] " Vladislav Shpilevoy 2018-06-07 12:22 ` [tarantool-patches] Re: [PATCH 0/3][vshard] " Vladislav Shpilevoy 3 siblings, 1 reply; 11+ messages in thread From: AKhatskevich @ 2018-06-06 15:57 UTC (permalink / raw) To: tarantool-patches, v.shpilevoy Add error message to each `error_message_template`. This change required to change arguments of some errors to make them more informative. Closes #100 --- test/router/reroute_wrong_bucket.result | 7 ++++--- test/router/router.result | 16 +++++++++------- test/storage/storage.result | 3 ++- vshard/error.lua | 15 +++++++++++---- vshard/router/init.lua | 3 +-- vshard/storage/init.lua | 28 ++++++++++++++++++++-------- 6 files changed, 47 insertions(+), 25 deletions(-) diff --git a/test/router/reroute_wrong_bucket.result b/test/router/reroute_wrong_bucket.result index c7b980c..75a1c5a 100644 --- a/test/router/reroute_wrong_bucket.result +++ b/test/router/reroute_wrong_bucket.result @@ -113,7 +113,7 @@ vshard.router.call(100, 'read', 'customer_lookup', {1}, {timeout = 1}) code: 9 type: ShardingError name: NO_ROUTE_TO_BUCKET - message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":100}"' + message: Bucket 100 cannot be found. Is rebalancing in progress? ... -- Wait reconfiguration durigin timeout, if a replicaset was not -- found by bucket.destination from WRONG_BUCKET or @@ -190,8 +190,9 @@ test_run:grep_log('router_1', 'please update configuration') ... err --- -- {'bucket_id': 100, 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', - 'message': 'WRONG_BUCKET: "{\"bucket_id\":100,\"destination\":\"ac522f65-aa94-4134-9f64-51ee384f1a54\"}"', +- {'bucket_id': 100, 'reason': 'bucket moving to ac522f65-aa94-4134-9f64-51ee384f1a54', + 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', + 'message': 'Cannot perform action with bucket 100, reason: bucket moving to ac522f65-aa94-4134-9f64-51ee384f1a54', 'name': 'WRONG_BUCKET'} ... -- diff --git a/test/router/router.result b/test/router/router.result index 6955b59..dab71f2 100644 --- a/test/router/router.result +++ b/test/router/router.result @@ -199,7 +199,7 @@ util.check_error(vshard.router.call, 1, 'read', 'echo', {123}) code: 9 type: ShardingError name: NO_ROUTE_TO_BUCKET - message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":1}"' + message: Bucket 1 cannot be found. Is rebalancing in progress? ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err --- @@ -207,7 +207,7 @@ replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err code: 9 type: ShardingError name: NO_ROUTE_TO_BUCKET - message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":1}"' + message: Bucket 1 cannot be found. Is rebalancing in progress? ... vshard.router.bootstrap() --- @@ -321,7 +321,7 @@ replicaset, err = vshard.router.bucket_discovery(0); return err == nil or err code: 9 type: ShardingError name: NO_ROUTE_TO_BUCKET - message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":0}"' + message: Bucket 0 cannot be found. Is rebalancing in progress? ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err --- @@ -361,7 +361,7 @@ util.check_error(vshard.router.call, 1, 'write', 'echo', {123}) --- - null - {'bucket_id': 1, 'code': 7, 'type': 'ShardingError', 'destination': '<replicaset_1>', - 'message': 'TRANSFER_IS_IN_PROGRESS: "{\"bucket_id\":1,\"destination\":\"<replicaset_1>\"}"', + 'message': 'Bucket 1 is transferring to replicaset <replicaset_1>', 'name': 'TRANSFER_IS_IN_PROGRESS'} ... test_run:cmd('switch storage_2_a') @@ -435,7 +435,7 @@ vshard.router.route(vshard.consts.DEFAULT_BUCKET_COUNT + 100) code: 9 type: ShardingError name: NO_ROUTE_TO_BUCKET - message: 'NO_ROUTE_TO_BUCKET: "{\"bucket_id\":3100}"' + message: Bucket 3100 cannot be found. Is rebalancing in progress? ... util.check_error(vshard.router.route, 'asdfg') --- @@ -969,8 +969,10 @@ test_run:cmd("switch router_1") util.check_error(vshard.router.call, 1, 'write', 'echo', { 'hello world' }) --- - null -- {'bucket_id': 1, 'code': 2, 'type': 'ShardingError', 'name': 'NON_MASTER', 'message': 'NON_MASTER: - "{\"bucket_id\":1}"'} +- {'code': 2, 'replica_uuid': '1e02ae8a-afc0-4e91-ba34-843a356b8ed7', 'type': 'ShardingError', + 'replicaset_uuid': '<replicaset_2>', 'message': 'Replica 1e02ae8a-afc0-4e91-ba34-843a356b8ed7 + is not a master for replicaset <replicaset_2> anymore', + 'name': 'NON_MASTER'} ... -- Reconfigure router and test that the WRITE request does work vshard.router.cfg(cfg) diff --git a/test/storage/storage.result b/test/storage/storage.result index aee9477..1258c9b 100644 --- a/test/storage/storage.result +++ b/test/storage/storage.result @@ -595,10 +595,11 @@ vshard.storage.call(100500, 'read', 'customer_lookup', {1}) --- - null - bucket_id: 100500 + reason: Not found code: 1 type: ShardingError name: WRONG_BUCKET - message: 'WRONG_BUCKET: "{\"bucket_id\":100500}"' + message: 'Cannot perform action with bucket 100500, reason: Not found' ... -- -- Test not existing space in bucket data. diff --git a/vshard/error.lua b/vshard/error.lua index 1271d16..8785695 100644 --- a/vshard/error.lua +++ b/vshard/error.lua @@ -14,23 +14,28 @@ local json = require('json') local error_message_template = { [1] = { name = 'WRONG_BUCKET', - args = {'bucket_id', 'destination'} + msg = 'Cannot perform action with bucket %d, reason: %s', + args = {'bucket_id', 'reason', 'destination'} }, [2] = { name = 'NON_MASTER', - args = {'bucket_id', 'destination'} + msg = 'Replica %s is not a master for replicaset %s anymore', + args = {'replica_uuid', 'replicaset_uuid'} }, [3] = { name = 'BUCKET_ALREADY_EXISTS', + msg = 'Bucket %d already exists', args = {'bucket_id'} }, [4] = { name = 'NO_SUCH_REPLICASET', + msg = 'Replicaset %s not found', args = {'replicaset_uuid'} }, [5] = { name = 'MOVE_TO_SELF', - args = {'replicaset_uuid', 'bucket_id'} + msg = 'Cannot move: bucket %d is already on replicaset %s', + args = {'bucket_id', 'replicaset_uuid'} }, [6] = { name = 'MISSING_MASTER', @@ -39,6 +44,7 @@ local error_message_template = { }, [7] = { name = 'TRANSFER_IS_IN_PROGRESS', + msg = 'Bucket %d is transferring to replicaset %s', args = {'bucket_id', 'destination'} }, [8] = { @@ -48,7 +54,8 @@ local error_message_template = { }, [9] = { name = 'NO_ROUTE_TO_BUCKET', - args = {'bucket_id', 'unreachable_uuid'} + msg = 'Bucket %d cannot be found. Is rebalancing in progress?', + args = {'bucket_id'} }, [10] = { name = 'NON_EMPTY', diff --git a/vshard/router/init.lua b/vshard/router/init.lua index 95ee8b3..21093e5 100644 --- a/vshard/router/init.lua +++ b/vshard/router/init.lua @@ -96,8 +96,7 @@ local function bucket_discovery(bucket_id) -- bucket was found to be RECEIVING on one replicaset, and -- was not found on other replicasets (it was sent during -- discovery). - err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, bucket_id, - unreachable_uuid) + err = lerror.vshard(lerror.code.NO_ROUTE_TO_BUCKET, bucket_id) end return nil, err diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index 2d38ff9..cd02657 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -428,31 +428,43 @@ local function bucket_check_state(bucket_id, mode) assert(mode == 'read' or mode == 'write') local bucket = box.space._bucket:get({bucket_id}) local errcode = nil + local reason = nil if not bucket then errcode = lerror.code.WRONG_BUCKET - goto finish + reason = 'Not found' + goto wrong_bucket elseif mode == 'read' then if not bucket_is_readable(bucket) then errcode = lerror.code.WRONG_BUCKET - goto finish + reason = 'read prohibited' + goto wrong_bucket end elseif not bucket_is_writable(bucket) then if bucket_is_transfer_in_progress(bucket) then errcode = lerror.code.TRANSFER_IS_IN_PROGRESS + return bucket, errcode and + lerror.vshard(errcode, bucket_id, bucket.destination) else errcode = lerror.code.WRONG_BUCKET + reason = 'write prohibited' + goto wrong_bucket end - goto finish elseif M.this_replicaset.master ~= M.this_replica then errcode = lerror.code.NON_MASTER - goto finish + return bucket, errcode and + lerror.vshard(errcode, M.this_replica.uuid, + M.this_replicaset.uuid) end assert(not errcode) assert(mode == 'read' and bucket_is_readable(bucket) or mode == 'write' and bucket_is_writable(bucket)) -::finish:: +::wrong_bucket:: + if bucket and bucket.destination then + reason = "bucket moving to " .. bucket.destination + end return bucket, errcode and - lerror.vshard(errcode, bucket_id, bucket and bucket.destination) + lerror.vshard(errcode, bucket_id, reason, + bucket and bucket.destination) end -- @@ -721,8 +733,8 @@ local function bucket_send(bucket_id, destination) end if destination == box.info.cluster.uuid then - return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, replicaset_uuid, - bucket_id) + return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, bucket_id, + replicaset_uuid) end local data = bucket_collect_internal(bucket_id) -- 2.14.1 ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 3/3] Add error messages 2018-06-06 15:57 ` [tarantool-patches] [PATCH 3/3] Add error messages AKhatskevich @ 2018-06-07 12:22 ` Vladislav Shpilevoy 2018-06-07 16:12 ` Alex Khatskevich 0 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2018-06-07 12:22 UTC (permalink / raw) To: tarantool-patches, AKhatskevich Thanks for the patch! See 7 comments below. On 06/06/2018 18:57, AKhatskevich wrote: > Add error message to each `error_message_template`. > This change required to change arguments of some errors to make > them more informative. > > Closes #100 > --- > test/router/reroute_wrong_bucket.result | 7 ++++--- > test/router/router.result | 16 +++++++++------- > test/storage/storage.result | 3 ++- > vshard/error.lua | 15 +++++++++++---- > vshard/router/init.lua | 3 +-- > vshard/storage/init.lua | 28 ++++++++++++++++++++-------- > 6 files changed, 47 insertions(+), 25 deletions(-) > > diff --git a/test/router/reroute_wrong_bucket.result b/test/router/reroute_wrong_bucket.result > index c7b980c..75a1c5a 100644 > --- a/test/router/reroute_wrong_bucket.result > +++ b/test/router/reroute_wrong_bucket.result > @@ -190,8 +190,9 @@ test_run:grep_log('router_1', 'please update configuration') > ... > err > --- > -- {'bucket_id': 100, 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', > - 'message': 'WRONG_BUCKET: "{\"bucket_id\":100,\"destination\":\"ac522f65-aa94-4134-9f64-51ee384f1a54\"}"', > +- {'bucket_id': 100, 'reason': 'bucket moving to ac522f65-aa94-4134-9f64-51ee384f1a54', > + 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', > + 'message': 'Cannot perform action with bucket 100, reason: bucket moving to ac522f65-aa94-4134-9f64-51ee384f1a54', 1. WRONG_BUCKET should not expose 'reason' as an attribute. > diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua > index 2d38ff9..cd02657 100644 > --- a/vshard/storage/init.lua > +++ b/vshard/storage/init.lua > @@ -428,31 +428,43 @@ local function bucket_check_state(bucket_id, mode) > assert(mode == 'read' or mode == 'write') > local bucket = box.space._bucket:get({bucket_id}) > local errcode = nil > + local reason = nil > if not bucket then > errcode = lerror.code.WRONG_BUCKET > - goto finish > + reason = 'Not found' > + goto wrong_bucket > elseif mode == 'read' then > if not bucket_is_readable(bucket) then > errcode = lerror.code.WRONG_BUCKET > - goto finish > + reason = 'read prohibited' 2. 'is prohibited'. No? > + goto wrong_bucket > end > elseif not bucket_is_writable(bucket) then > if bucket_is_transfer_in_progress(bucket) then > errcode = lerror.code.TRANSFER_IS_IN_PROGRESS > + return bucket, errcode and > + lerror.vshard(errcode, bucket_id, bucket.destination) 3. How could 'errcode' be nil, if you had set it one line above? > else > errcode = lerror.code.WRONG_BUCKET > + reason = 'write prohibited' 4. Same as 2. > + goto wrong_bucket > end > - goto finish > elseif M.this_replicaset.master ~= M.this_replica then > errcode = lerror.code.NON_MASTER > - goto finish > + return bucket, errcode and > + lerror.vshard(errcode, M.this_replica.uuid, > + M.this_replicaset.uuid) 5. Same as 3. > end > assert(not errcode) > assert(mode == 'read' and bucket_is_readable(bucket) or > mode == 'write' and bucket_is_writable(bucket)) > -::finish:: > +::wrong_bucket:: 6. The code below is executed both on error and on success. But label is named 'wrong_bucket'. It is not correct. On success path it is guaranteed that bucket ~= nil. 7. Now msg is set for each error, but you still have checks in vshard_error like 'if format.msg then'. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 3/3] Add error messages 2018-06-07 12:22 ` [tarantool-patches] " Vladislav Shpilevoy @ 2018-06-07 16:12 ` Alex Khatskevich 2018-06-07 18:44 ` Vladislav Shpilevoy 0 siblings, 1 reply; 11+ messages in thread From: Alex Khatskevich @ 2018-06-07 16:12 UTC (permalink / raw) To: Vladislav Shpilevoy, tarantool-patches > 1. WRONG_BUCKET should not expose 'reason' as an attribute. Discussed verbally. Let it just be exposed and useless and undocumented. > 2. 'is prohibited'. No? I just wanted short error messaged. Fixed. > 3. How could 'errcode' be nil, if you had set it one line above? Thanks > 4. Same as 2. Fixed along with "bucket moving to " -> "bucket moving to " > 5. Same as 3. Fixed > >> end >> assert(not errcode) >> assert(mode == 'read' and bucket_is_readable(bucket) or >> mode == 'write' and bucket_is_writable(bucket)) >> -::finish:: >> +::wrong_bucket:: > > 6. The code below is executed both on error and on success. But > label is named 'wrong_bucket'. It is not correct. On success > path it is guaranteed that bucket ~= nil. Renamed back to `finish`. IMHO `wrong_bucket` suited well here. > 7. Now msg is set for each error, but you still have checks in > vshard_error like 'if format.msg then'. Fixed New diff: commit 057e640c25dfaab8ccafe3740cddaee27b2c7bc9 Author: AKhatskevich <avkhatskevich@tarantool.org> Date: Wed Jun 6 13:28:25 2018 +0300 Add error messages Add error message to each `error_message_template`. This change required to change arguments of some errors to make them more informative. Closes #100 diff --git a/test/router/reroute_wrong_bucket.result b/test/router/reroute_wrong_bucket.result index f8021f2..58bef8d 100644 --- a/test/router/reroute_wrong_bucket.result +++ b/test/router/reroute_wrong_bucket.result @@ -112,7 +112,7 @@ vshard.router.call(100, 'read', 'customer_lookup', {1}, {timeout = 1}) - bucket_id: 100 code: 9 type: ShardingError - message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":100}' + message: Bucket 100 cannot be found. Is rebalancing in progress? name: NO_ROUTE_TO_BUCKET ... -- Wait reconfiguration durigin timeout, if a replicaset was not @@ -190,8 +190,10 @@ test_run:grep_log('router_1', 'please update configuration') ... err --- -- {'bucket_id': 100, 'code': 1, 'type': 'ShardingError', 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', - 'name': 'WRONG_BUCKET', 'message': 'WRONG_BUCKET: {"bucket_id":100,"destination":"ac522f65-aa94-4134-9f64-51ee384f1a54"}'} +- {'bucket_id': 100, 'reason': 'bucket is moving to ac522f65-aa94-4134-9f64-51ee384f1a54', + 'code': 1, 'destination': 'ac522f65-aa94-4134-9f64-51ee384f1a54', 'type': 'ShardingError', + 'name': 'WRONG_BUCKET', 'message': 'Cannot perform action with bucket 100, reason: + bucket is moving to ac522f65-aa94-4134-9f64-51ee384f1a54'} ... -- -- Now try again, but update configuration during call(). It must diff --git a/test/router/router.result b/test/router/router.result index 5d99255..2ee1bff 100644 --- a/test/router/router.result +++ b/test/router/router.result @@ -198,7 +198,7 @@ util.check_error(vshard.router.call, 1, 'read', 'echo', {123}) - bucket_id: 1 code: 9 type: ShardingError - message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":1}' + message: Bucket 1 cannot be found. Is rebalancing in progress? name: NO_ROUTE_TO_BUCKET ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err @@ -206,7 +206,7 @@ replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err - bucket_id: 1 code: 9 type: ShardingError - message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":1}' + message: Bucket 1 cannot be found. Is rebalancing in progress? name: NO_ROUTE_TO_BUCKET ... vshard.router.bootstrap() @@ -320,7 +320,7 @@ replicaset, err = vshard.router.bucket_discovery(0); return err == nil or err - bucket_id: 0 code: 9 type: ShardingError - message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":0}' + message: Bucket 0 cannot be found. Is rebalancing in progress? name: NO_ROUTE_TO_BUCKET ... replicaset, err = vshard.router.bucket_discovery(1); return err == nil or err @@ -361,7 +361,8 @@ util.check_error(vshard.router.call, 1, 'write', 'echo', {123}) --- - null - {'bucket_id': 1, 'code': 7, 'type': 'ShardingError', 'destination': '<replicaset_1>', - 'name': 'TRANSFER_IS_IN_PROGRESS', 'message': 'TRANSFER_IS_IN_PROGRESS: {"bucket_id":1,"destination":"<replicaset_1>"}'} + 'name': 'TRANSFER_IS_IN_PROGRESS', 'message': 'Bucket 1 is transferring to replicaset + <replicaset_1>'} ... test_run:cmd('switch storage_2_a') --- @@ -433,7 +434,7 @@ vshard.router.route(vshard.consts.DEFAULT_BUCKET_COUNT + 100) - bucket_id: 3100 code: 9 type: ShardingError - message: 'NO_ROUTE_TO_BUCKET: {"bucket_id":3100}' + message: Bucket 3100 cannot be found. Is rebalancing in progress? name: NO_ROUTE_TO_BUCKET ... util.check_error(vshard.router.route, 'asdfg') @@ -968,8 +969,10 @@ test_run:cmd("switch router_1") util.check_error(vshard.router.call, 1, 'write', 'echo', { 'hello world' }) --- - null -- {'bucket_id': 1, 'code': 2, 'type': 'ShardingError', 'message': 'NON_MASTER: {"bucket_id":1}', - 'name': 'NON_MASTER'} +- {'replica_uuid': '1e02ae8a-afc0-4e91-ba34-843a356b8ed7', 'replicaset_uuid': '<replicaset_2>', + 'type': 'ShardingError', 'message': 'Replica 1e02ae8a-afc0-4e91-ba34-843a356b8ed7 + is not a master for replicaset <replicaset_2> anymore', + 'name': 'NON_MASTER', 'code': 2} ... -- Reconfigure router and test that the WRITE request does work vshard.router.cfg(cfg) diff --git a/test/storage/storage.result b/test/storage/storage.result index 51492a2..d0bf792 100644 --- a/test/storage/storage.result +++ b/test/storage/storage.result @@ -595,9 +595,10 @@ vshard.storage.call(100500, 'read', 'customer_lookup', {1}) --- - null - bucket_id: 100500 + reason: Not found code: 1 type: ShardingError - message: 'WRONG_BUCKET: {"bucket_id":100500}' + message: 'Cannot perform action with bucket 100500, reason: Not found' name: WRONG_BUCKET ... -- @@ -624,7 +625,7 @@ vshard.storage.bucket_send(1, 'unknown uuid') type: ShardingError name: NO_SUCH_REPLICASET replicaset_uuid: unknown uuid - message: 'NO_SUCH_REPLICASET: {"replicaset_uuid":"unknown uuid"}' + message: Replicaset unknown uuid not found ... -- Successful transfer. vshard.storage.bucket_send(1, replicaset2_uuid) diff --git a/test/unit/error.result b/test/unit/error.result index 74571ca..d1f04d0 100644 --- a/test/unit/error.result +++ b/test/unit/error.result @@ -41,14 +41,22 @@ test_run:grep_log('default', '"reason":"reason","code":11,"type":"ShardingError" -- -- Part of gh-100: check `error.vshard`. -- -lerror.vshard(lerror.code.WRONG_BUCKET, 'arg1', 'arg2') +lerror.vshard(lerror.code.WRONG_BUCKET, 1, 'arg2') --- -- bucket_id: arg1 +- bucket_id: 1 + reason: arg2 code: 1 type: ShardingError + message: 'Cannot perform action with bucket 1, reason: arg2' name: WRONG_BUCKET - message: 'WRONG_BUCKET: {"bucket_id":"arg1","destination":"arg2"}' - destination: arg2 +... +-- Pass an arg of a wrong type. +ok, res = pcall(lerror.vshard, lerror.code.WRONG_BUCKET, 'arg1', 'arg2') +--- +... +res:match('bad argument #2.*') +--- +- 'bad argument #2 to ''format'' (number expected, got string)' ... -- Pass less args than msg requires. ok, res = pcall(lerror.vshard, lerror.code.MISSING_MASTER) diff --git a/test/unit/error.test.lua b/test/unit/error.test.lua index 7496982..ef9d5cd 100644 --- a/test/unit/error.test.lua +++ b/test/unit/error.test.lua @@ -19,7 +19,10 @@ test_run:grep_log('default', '"reason":"reason","code":11,"type":"ShardingError" -- -- Part of gh-100: check `error.vshard`. -- -lerror.vshard(lerror.code.WRONG_BUCKET, 'arg1', 'arg2') +lerror.vshard(lerror.code.WRONG_BUCKET, 1, 'arg2') +-- Pass an arg of a wrong type. +ok, res = pcall(lerror.vshard, lerror.code.WRONG_BUCKET, 'arg1', 'arg2') +res:match('bad argument #2.*') -- Pass less args than msg requires. ok, res = pcall(lerror.vshard, lerror.code.MISSING_MASTER) res:match('bad argument #2.*') diff --git a/vshard/error.lua b/vshard/error.lua index 59e4fac..429e6da 100644 --- a/vshard/error.lua +++ b/vshard/error.lua @@ -14,23 +14,28 @@ local json = require('json') local error_message_template = { [1] = { name = 'WRONG_BUCKET', - args = {'bucket_id', 'destination'} + msg = 'Cannot perform action with bucket %d, reason: %s', + args = {'bucket_id', 'reason', 'destination'} }, [2] = { name = 'NON_MASTER', - args = {'bucket_id', 'destination'} + msg = 'Replica %s is not a master for replicaset %s anymore', + args = {'replica_uuid', 'replicaset_uuid'} }, [3] = { name = 'BUCKET_ALREADY_EXISTS', + msg = 'Bucket %d already exists', args = {'bucket_id'} }, [4] = { name = 'NO_SUCH_REPLICASET', + msg = 'Replicaset %s not found', args = {'replicaset_uuid'} }, [5] = { name = 'MOVE_TO_SELF', - args = {'replicaset_uuid', 'bucket_id'} + msg = 'Cannot move: bucket %d is already on replicaset %s', + args = {'bucket_id', 'replicaset_uuid'} }, [6] = { name = 'MISSING_MASTER', @@ -39,6 +44,7 @@ local error_message_template = { }, [7] = { name = 'TRANSFER_IS_IN_PROGRESS', + msg = 'Bucket %d is transferring to replicaset %s', args = {'bucket_id', 'destination'} }, [8] = { @@ -48,6 +54,7 @@ local error_message_template = { }, [9] = { name = 'NO_ROUTE_TO_BUCKET', + msg = 'Bucket %d cannot be found. Is rebalancing in progress?', args = {'bucket_id'} }, [10] = { @@ -95,13 +102,13 @@ local error_message_template = { msg = 'Replicaset is locked' } } - -- -- User-visible error_name -> error_number dictionary. -- local error_code = {} for code, err in pairs(error_message_template) do assert(type(code) == 'number') + assert(err.msg, 'msg is required field') assert(error_code[err.name] == nil, "Dublicate error name") error_code[err.name] = code end @@ -142,11 +149,7 @@ local function vshard_error(code, ...) for i = 1, #args do ret[args[i]] = select(i, ...) end - if format.msg then - ret.message = string.format(format.msg, ...) - else - ret.message = string.format('%s: %s', format.name, ret) - end + ret.message = string.format(format.msg, ...) ret.type = 'ShardingError' ret.code = code ret.name = format.name diff --git a/vshard/storage/init.lua b/vshard/storage/init.lua index 2d38ff9..125b5b7 100644 --- a/vshard/storage/init.lua +++ b/vshard/storage/init.lua @@ -428,31 +428,42 @@ local function bucket_check_state(bucket_id, mode) assert(mode == 'read' or mode == 'write') local bucket = box.space._bucket:get({bucket_id}) local errcode = nil + local reason = nil if not bucket then errcode = lerror.code.WRONG_BUCKET + reason = 'Not found' goto finish elseif mode == 'read' then if not bucket_is_readable(bucket) then errcode = lerror.code.WRONG_BUCKET + reason = 'read is prohibited' goto finish end elseif not bucket_is_writable(bucket) then if bucket_is_transfer_in_progress(bucket) then errcode = lerror.code.TRANSFER_IS_IN_PROGRESS + return bucket, lerror.vshard(errcode, bucket_id, bucket.destination) else errcode = lerror.code.WRONG_BUCKET + reason = 'write is prohibited' + goto finish end - goto finish elseif M.this_replicaset.master ~= M.this_replica then errcode = lerror.code.NON_MASTER - goto finish + return bucket, lerror.vshard(errcode, M.this_replica.uuid, + M.this_replicaset.uuid) end assert(not errcode) assert(mode == 'read' and bucket_is_readable(bucket) or mode == 'write' and bucket_is_writable(bucket)) +-- Only WRONG_BUCKET errors use this label. ::finish:: + if errcode and bucket and bucket.destination then + reason = "bucket is moving to " .. bucket.destination + end return bucket, errcode and - lerror.vshard(errcode, bucket_id, bucket and bucket.destination) + lerror.vshard(errcode, bucket_id, reason, + bucket and bucket.destination) end -- @@ -721,8 +732,8 @@ local function bucket_send(bucket_id, destination) end if destination == box.info.cluster.uuid then - return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, replicaset_uuid, - bucket_id) + return nil, lerror.vshard(lerror.code.MOVE_TO_SELF, bucket_id, + replicaset_uuid) end local data = bucket_collect_internal(bucket_id) ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 3/3] Add error messages 2018-06-07 16:12 ` Alex Khatskevich @ 2018-06-07 18:44 ` Vladislav Shpilevoy 0 siblings, 0 replies; 11+ messages in thread From: Vladislav Shpilevoy @ 2018-06-07 18:44 UTC (permalink / raw) To: Alex Khatskevich, tarantool-patches Thanks for the fixes! Finally the errors have become pretty. >> 4. Same as 2. > Fixed along with > "bucket moving to " -> "bucket moving to " What the difference? "bucket moving to" == "bucket moving to". >> >>> end >>> assert(not errcode) >>> assert(mode == 'read' and bucket_is_readable(bucket) or >>> mode == 'write' and bucket_is_writable(bucket)) >>> -::finish:: >>> +::wrong_bucket:: >> >> 6. The code below is executed both on error and on success. But >> label is named 'wrong_bucket'. It is not correct. On success >> path it is guaranteed that bucket ~= nil. > Renamed back to `finish`. IMHO `wrong_bucket` suited well here. You had labeled the success code as error processing. It is not ok. And in the same code you check 'if bucket and ...', but bucket on success is not nil - it is guaranteed. We must optimize the common case. And the bucket is ok most of time. +-- Only WRONG_BUCKET errors use this label. This is wrong. See three lines above - they process a case when the bucket is ok. You can label it ::wrong_bucket::, but then you must separate this code from success path. I refactored this function, you can look at it on the master branch. The patchset is pushed with my fixes. ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 0/3][vshard] Add error messages 2018-06-06 15:57 [tarantool-patches] [PATCH 0/3][vshard] Add error messages AKhatskevich ` (2 preceding siblings ...) 2018-06-06 15:57 ` [tarantool-patches] [PATCH 3/3] Add error messages AKhatskevich @ 2018-06-07 12:22 ` Vladislav Shpilevoy 2018-06-07 13:16 ` Alex Khatskevich 3 siblings, 1 reply; 11+ messages in thread From: Vladislav Shpilevoy @ 2018-06-07 12:22 UTC (permalink / raw) To: tarantool-patches, AKhatskevich Hello. Thanks for the patchset! On 06/06/2018 18:57, AKhatskevich wrote: > Branch: https://github.com/tarantool/vshard/tree/kh/gh-100-error-msg-wo-extra-data > Issue: https://github.com/tarantool/vshard/issues/100 > > *** BLURB HERE *** Why do I see this placeholder here? You should write here a short description of the patchset. Please, answer on this email with the description. > > AKhatskevich (3): > Fix test on bucket_transferring error > Refactor error reporting system > Add error messages > > test/misc/check_uuid_on_connect.result | 8 +- > test/router/reroute_wrong_bucket.result | 11 +- > test/router/router.result | 54 +++++--- > test/router/router.test.lua | 4 +- > test/storage/storage.result | 7 +- > test/unit/error.result | 6 +- > test/unit/error.test.lua | 2 +- > vshard/error.lua | 216 +++++++++++++++++++++----------- > vshard/replicaset.lua | 4 +- > vshard/router/init.lua | 10 +- > vshard/storage/init.lua | 39 +++--- > 11 files changed, 231 insertions(+), 130 deletions(-) > ^ permalink raw reply [flat|nested] 11+ messages in thread
* [tarantool-patches] Re: [PATCH 0/3][vshard] Add error messages 2018-06-07 12:22 ` [tarantool-patches] Re: [PATCH 0/3][vshard] " Vladislav Shpilevoy @ 2018-06-07 13:16 ` Alex Khatskevich 0 siblings, 0 replies; 11+ messages in thread From: Alex Khatskevich @ 2018-06-07 13:16 UTC (permalink / raw) To: Vladislav Shpilevoy, tarantool-patches > Why do I see this placeholder here? You should write here a short > description of the patchset. Please, answer on this email with the > description. The aim of this patchset is to make error messages more readable for a user. It consists of three commits: 1. Fix test on bucket_transferring error 2. Refactor error reporting system 3. Add error messages ^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2018-06-07 18:44 UTC | newest] Thread overview: 11+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2018-06-06 15:57 [tarantool-patches] [PATCH 0/3][vshard] Add error messages AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 1/3] Fix test on bucket_transferring error AKhatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 2/3] Refactor error reporting system AKhatskevich 2018-06-07 12:22 ` [tarantool-patches] " Vladislav Shpilevoy 2018-06-07 16:13 ` Alex Khatskevich 2018-06-06 15:57 ` [tarantool-patches] [PATCH 3/3] Add error messages AKhatskevich 2018-06-07 12:22 ` [tarantool-patches] " Vladislav Shpilevoy 2018-06-07 16:12 ` Alex Khatskevich 2018-06-07 18:44 ` Vladislav Shpilevoy 2018-06-07 12:22 ` [tarantool-patches] Re: [PATCH 0/3][vshard] " Vladislav Shpilevoy 2018-06-07 13:16 ` Alex Khatskevich
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox