[Tarantool-patches] [PATCH 4/6] error: Add extended error transfer format
Leonid Vasiliev
lvasiliev at tarantool.org
Tue Mar 24 15:46:02 MSK 2020
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
More information about the Tarantool-patches
mailing list