[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