From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from smtp18.mail.ru (smtp18.mail.ru [94.100.176.155]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id BE4ED43E88B for ; Tue, 24 Mar 2020 15:46:14 +0300 (MSK) From: Leonid Vasiliev Date: Tue, 24 Mar 2020 15:46:02 +0300 Message-Id: In-Reply-To: References: In-Reply-To: References: Subject: [Tarantool-patches] [PATCH 4/6] error: Add extended error transfer format List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , To: alexander.turenko@tarantool.org Cc: tarantool-patches@dev.tarantool.org Extended error transfer format (through IPROTO_ERROR) has been added. Extended error transfer format (through IPROTO_ERROR) has been added. Which error transmission format (default or extended) will be used depends on the session negotiation parameters. @TarantoolBot document Title: error: extended error transfer format The error_extended option has been added to connection options. This option adds a negotiation phase to the connection establishing. It tries to establish a connection with the possibility of transmitting errors in an extended format and throws an error in case of failure. The extended format also transmit a backtrace and custom type. The new IPROTO_ERROR representation (using an array(MP_ARRAY) is useful for the stacked diagnostics task) 0 5 +------++================+================++===================+ | || | || | | BODY || 0x00: 0x8XXX | 0x01: SYNC || 0x31: ERROR | |HEADER|| MP_INT: MP_INT | MP_INT: MP_INT || MP_INT: MP_ARRAY | | SIZE || | || | +------++================+================++===================+ MP_INT MP_MAP MP_MAP Every error is a map with keys like ERROR_MSG, ERROR_BT ... Example: local connection = netbox.connect(host, port, {error_extended = true}) Needed for #4398 --- src/box/iproto.cc | 20 ++++++++++--- src/box/iproto_constants.h | 14 +++++++++ src/box/lua/error.cc | 21 +++++++++++++ src/box/lua/net_box.lua | 36 ++++++++++++++++++++++- src/box/session.h | 4 ++- src/box/xrow.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++ src/box/xrow.h | 10 ++++++- 7 files changed, 171 insertions(+), 7 deletions(-) diff --git a/src/box/iproto.cc b/src/box/iproto.cc index 2df41ba..bb494f4 100644 --- a/src/box/iproto.cc +++ b/src/box/iproto.cc @@ -1506,9 +1506,15 @@ tx_accept_msg(struct cmsg *m) static void tx_reply_error(struct iproto_msg *msg) { + struct session *ses = msg->connection->session; struct obuf *out = msg->connection->tx.p_obuf; - iproto_reply_error(out, diag_last_error(&fiber()->diag), - msg->header.sync, ::schema_version); + if (ses->neg_param.err_format_ver == ERR_FORMAT_EX) { + iproto_reply_error_ex(out, diag_last_error(&fiber()->diag), + msg->header.sync, ::schema_version); + } else { + iproto_reply_error(out, diag_last_error(&fiber()->diag), + msg->header.sync, ::schema_version); + } iproto_wpos_create(&msg->wpos, out); } @@ -1520,9 +1526,15 @@ static void tx_reply_iproto_error(struct cmsg *m) { struct iproto_msg *msg = tx_accept_msg(m); + struct session *ses = msg->connection->session; struct obuf *out = msg->connection->tx.p_obuf; - iproto_reply_error(out, diag_last_error(&msg->diag), - msg->header.sync, ::schema_version); + if (ses->neg_param.err_format_ver == ERR_FORMAT_EX) { + iproto_reply_error_ex(out, diag_last_error(&msg->diag), + msg->header.sync, ::schema_version); + } else { + iproto_reply_error(out, diag_last_error(&msg->diag), + msg->header.sync, ::schema_version); + } iproto_wpos_create(&msg->wpos, out); } diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h index 1851712..058e07f 100644 --- a/src/box/iproto_constants.h +++ b/src/box/iproto_constants.h @@ -471,6 +471,20 @@ vy_row_index_key_name(enum vy_row_index_key key) } /** + * Error details keys + */ +enum error_details_key { + /** Reason */ + ERROR_DET_REASON, + /** Error code */ + ERROR_DET_CODE, + /** Backtrace */ + ERROR_DET_BT, + /** Custom type */ + ERROR_DET_CT +}; + +/** * Negotiation protocol's keys */ enum neg_key { diff --git a/src/box/lua/error.cc b/src/box/lua/error.cc index 708d338..4a66335 100644 --- a/src/box/lua/error.cc +++ b/src/box/lua/error.cc @@ -189,6 +189,23 @@ luaT_error_custom_type(lua_State *L) } static int +luaT_error_set_lua_bt(lua_State *L) +{ + if (lua_gettop(L) < 2) + return luaL_error(L, "Usage: box.error.set_lua_bt(error, bt)"); + + struct error *e = luaL_checkerror(L, 1); + + if (lua_type(L, 2) == LUA_TSTRING) { + error_set_lua_bt(e, lua_tostring(L, 2)); + } else { + return luaL_error(L, "Usage: box.error.set_lua_bt(error, bt)"); + } + + return 0; +} + +static int luaT_error_clear(lua_State *L) { if (lua_gettop(L) >= 1) @@ -316,6 +333,10 @@ box_lua_error_init(struct lua_State *L) { lua_pushcfunction(L, luaT_error_custom_type); lua_setfield(L, -2, "custom_type"); } + { + lua_pushcfunction(L, luaT_error_set_lua_bt); + lua_setfield(L, -2, "set_lua_bt"); + } lua_setfield(L, -2, "__index"); } lua_setmetatable(L, -2); diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua index 7fdad64..7fd317c 100644 --- a/src/box/lua/net_box.lua +++ b/src/box/lua/net_box.lua @@ -9,6 +9,7 @@ local errno = require('errno') local urilib = require('uri') local internal = require('net.box.lib') local trigger = require('internal.trigger') +local debug = require('debug') local band = bit.band local max = math.max @@ -64,6 +65,14 @@ local neg_keys = { ERROR_FORMAT_VERSION = 0 } +-- error details +local error_details = { + REASON = 0, + CODE = 1, + BACKTRACE = 2, + CUSTOM_TYPE = 3 +} + local function decode_nil(raw_data, raw_data_end) return nil, raw_data_end end @@ -291,7 +300,32 @@ local function create_transport(host, port, user, password, callback, -- @retval nil, error Error occured. -- function request_index:result() - if self.errno then + local function parse_extended_error(error_ex) + local reason = error_ex[error_details.REASON] + local code = error_ex[error_details.CODE] + local r_bt = error_ex[error_details.BACKTRACE] + local custom_type = error_ex[error_details.CUSTOM_TYPE] + local err = box.error.new({code = code, + reason = reason, + custom_type = custom_type}) + local cur_bt = debug.traceback() + local result_bt = string.format("Remote(%s:%s):" + .."\n%s\nEnd Remote\n%s", + host, tostring(port), + r_bt, cur_bt) + box.error.set_lua_bt(err, result_bt) + return err + end + + if self.errno and type(self.response) == 'table' then + local response = self.response[1] + if not response then + return nil, box.error.new(box.error.PROC_LUA, + 'Can\'t parse a remote error') + end + local err = parse_extended_error(response) + return nil, err + elseif self.errno then return nil, box.error.new({code = self.errno, reason = self.response}) elseif not self.id then diff --git a/src/box/session.h b/src/box/session.h index 3b4dfb0..c775fd0 100644 --- a/src/box/session.h +++ b/src/box/session.h @@ -87,7 +87,9 @@ enum error_formats { /** Default(old) format */ ERR_FORMAT_DEF, /** Extended format */ - ERR_FORMAT_EX + ERR_FORMAT_EX, + /** The max version of error format */ + ERR_FORMAT_UNK }; /** diff --git a/src/box/xrow.c b/src/box/xrow.c index 90dffea..4cda99a 100644 --- a/src/box/xrow.c +++ b/src/box/xrow.c @@ -385,6 +385,10 @@ static const struct iproto_body_bin iproto_error_bin = { 0x81, IPROTO_ERROR, 0xdb, 0 }; +static const struct iproto_body_bin iproto_error_bin_ex = { + 0x81, IPROTO_ERROR, 0xdd, 0 +}; + /** Return a 4-byte numeric error code, with status flags. */ static inline uint32_t iproto_encode_error(uint32_t error) @@ -534,6 +538,70 @@ iproto_reply_error(struct obuf *out, const struct error *e, uint64_t sync, obuf_dup(out, e->errmsg, msg_len) != msg_len ? -1 : 0; } +int +iproto_reply_error_ex(struct obuf *out, const struct error *e, + uint64_t sync, uint32_t schema_version) +{ + uint32_t errcode = box_error_code(e); + const char *err_type = box_error_type(e); + const char *custom_type = NULL; + + struct iproto_body_bin body = iproto_error_bin_ex; + + uint32_t buf_size = IPROTO_HEADER_LEN + sizeof(body); + /* Reason and code are the necessary fields */ + uint32_t details_num = 2; + + buf_size += mp_sizeof_uint(ERROR_DET_CODE); + buf_size += mp_sizeof_uint(errcode); + buf_size += mp_sizeof_uint(ERROR_DET_REASON); + buf_size += mp_sizeof_str(strlen(e->errmsg)); + if (strcmp(err_type, "CustomError") == 0) { + ++details_num; + buf_size += mp_sizeof_uint(ERROR_DET_CT); + custom_type = box_custom_error_type(e); + buf_size += mp_sizeof_str(strlen(custom_type)); + } + if (e->lua_bt) { + ++details_num; + buf_size += mp_sizeof_uint(ERROR_DET_BT); + buf_size += mp_sizeof_str(strlen(e->lua_bt)); + } + buf_size += mp_sizeof_map(details_num); + + char *buf = (char *)obuf_alloc(out, buf_size); + if (buf == NULL) { + diag_set(OutOfMemory, buf_size, "obuf_alloc", "buf"); + return -1; + } + + char *data = buf; + iproto_header_encode(data, iproto_encode_error(errcode), sync, + schema_version, buf_size - IPROTO_HEADER_LEN); + body.v_data_len = mp_bswap_u32(1); + data += IPROTO_HEADER_LEN; + + memcpy(data, &body, sizeof(body)); + data += sizeof(body); + + data = mp_encode_map(data, details_num); + data = mp_encode_uint(data, ERROR_DET_CODE); + data = mp_encode_uint(data, errcode); + data = mp_encode_uint(data, ERROR_DET_REASON); + data = mp_encode_str(data, e->errmsg, strlen(e->errmsg)); + if (custom_type) { + data = mp_encode_uint(data, ERROR_DET_CT); + data = mp_encode_str(data, custom_type, strlen(custom_type)); + } + if(e->lua_bt) { + data = mp_encode_uint(data, ERROR_DET_BT); + data = mp_encode_str(data, e->lua_bt, strlen(e->lua_bt)); + } + assert(data == buf + buf_size); + + return 0; +} + void iproto_write_error(int fd, const struct error *e, uint32_t schema_version, uint64_t sync) @@ -1591,6 +1659,11 @@ error: if (mp_typeof(*data) != MP_UINT) goto error; request->err_format_ver = mp_decode_uint(&data); + if (request->err_format_ver >= ERR_FORMAT_UNK) { + diag_set(ClientError, ER_ILLEGAL_PARAMS, + "unknown version of error format"); + return -1; + } break; default: /* unknown key */ diff --git a/src/box/xrow.h b/src/box/xrow.h index bbd0496..be06003 100644 --- a/src/box/xrow.h +++ b/src/box/xrow.h @@ -571,7 +571,7 @@ iproto_reply_vote(struct obuf *out, const struct ballot *ballot, uint64_t sync, uint32_t schema_version); /** - * Write an error packet int output buffer. Doesn't throw if out + * Write an error packet to output buffer. Doesn't throw if out * of memory */ int @@ -579,6 +579,14 @@ iproto_reply_error(struct obuf *out, const struct error *e, uint64_t sync, uint32_t schema_version); /** + * Write an extended error packet to output buffer. Doesn't throw if out + * of memory + */ +int +iproto_reply_error_ex(struct obuf *out, const struct error *e, uint64_t sync, + uint32_t schema_version); + +/** * Write a negotiation reply packet to output buffer. * @param out Buffer to write to. * @param neg_param Current negotiation parameters. -- 2.7.4