diff --git a/doc/www/content/doc/box-protocol.rst b/doc/www/content/doc/box-protocol.rst
index 95bb25dc2d40540285077ff48e33c920eac769e5..51677dd093bb672cf9c05e559a018378e66e5209 100644
--- a/doc/www/content/doc/box-protocol.rst
+++ b/doc/www/content/doc/box-protocol.rst
@@ -29,7 +29,7 @@
 
 MsgPack data types:
 
-* **MP_INT** - Unsigned Integer
+* **MP_INT** - Integer
 * **MP_MAP** - Map
 * **MP_ARR** - Array
 * **MP_STRING** - String
diff --git a/src/box/box.cc b/src/box/box.cc
index c397d226556cd16d93abc6ee4c49742427f7f7f8..2371de06a21e51ad775f5a83f266b73c555ff723 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -52,13 +52,11 @@
 #include "cfg.h"
 #include "iobuf.h"
 
-static void process_ro(struct port *port, struct request *request);
-static void process_rw(struct port *port, struct request *request);
+static void process_ro(struct request *request, struct port *port);
 box_process_func box_process = process_ro;
 
 struct recovery_state *recovery;
 
-static int stat_base;
 int snapshot_pid = 0; /* snapshot processes pid */
 
 static void
@@ -74,24 +72,11 @@ struct request_replace_body {
 } __attribute__((packed));
 
 static void
-process_rw(struct port *port, struct request *request)
-{
-	try {
-		stat_collect(stat_base, request->type, 1);
-		request->execute(request, port);
-		port_eof(port);
-	} catch (Exception *e) {
-		txn_rollback_stmt();
-		throw;
-	}
-}
-
-static void
-process_ro(struct port *port, struct request *request)
+process_ro(struct request *request, struct port *port)
 {
 	if (!iproto_type_is_select(request->type))
 		tnt_raise(LoggedError, ER_READONLY);
-	return process_rw(port, request);
+	return process_rw(request, port);
 }
 
 void
@@ -118,7 +103,7 @@ recover_row(struct recovery_state *r, void *param, struct xrow_header *row)
 	request_decode(&request, (const char *) row->body[0].iov_base,
 		row->body[0].iov_len);
 	request.header = row;
-	process_rw(&null_port, &request);
+	process_rw(&request, &null_port);
 }
 
 /* {{{ configuration bindings */
@@ -249,7 +234,7 @@ box_leave_local_standby_mode(void *data __attribute__((unused)))
 	*/
 	space_end_recover();
 
-	stat_cleanup(stat_base, IPROTO_TYPE_DML_MAX);
+	stat_cleanup(stat_base, IPROTO_TYPE_STAT_MAX);
 	box_set_wal_mode(cfg_gets("wal_mode"));
 
 	if (recovery_has_remote(recovery))
@@ -299,7 +284,7 @@ boxk(enum iproto_type type, uint32_t space_id, const char *format, ...)
 	assert(data <= buf + sizeof(buf));
 	req.tuple = buf;
 	req.tuple_end = data;
-	process_rw(&null_port, &req);
+	process_rw(&req, &null_port);
 }
 
 /**
@@ -436,7 +421,7 @@ box_init()
 			     cfg_geti("panic_on_snap_error"),
 			     cfg_geti("panic_on_wal_error"));
 
-	stat_base = stat_register(iproto_type_strs, IPROTO_TYPE_DML_MAX);
+	stat_base = stat_register(iproto_type_strs, IPROTO_TYPE_STAT_MAX);
 
 	if (recovery_has_data(recovery)) {
 		/* Process existing snapshot */
diff --git a/src/box/box.h b/src/box/box.h
index ca18d7043d8d6b4375751f376d9df1ba51fc5027..4729c99d10ed36f7f5d13affaf51c53b5431b102 100644
--- a/src/box/box.h
+++ b/src/box/box.h
@@ -63,7 +63,7 @@ box_atfork();
  * change when entering/leaving read-only mode
  * (master->slave propagation).
  */
-typedef void (*box_process_func)(struct port *port, struct request *request);
+typedef void (*box_process_func)(struct request *request, struct port *port);
 /** For read-write operations. */
 extern box_process_func box_process;
 
@@ -100,6 +100,9 @@ const char *box_status(void);
 void
 box_leave_local_standby_mode(void *data __attribute__((unused)));
 
+void
+box_process_auth(struct request *request);
+
 void
 box_process_join(int fd, struct xrow_header *header);
 
diff --git a/src/box/cluster.cc b/src/box/cluster.cc
index 56490b4ce8f485683c1ed9c92bef38e746345f1d..fda7caac46d04d94cce76a98ca6d54821f295b92 100644
--- a/src/box/cluster.cc
+++ b/src/box/cluster.cc
@@ -53,7 +53,8 @@ cluster_set_server(const tt_uuid *server_uuid, uint32_t server_id)
 	if (r->server_id == server_id) {
 		if (tt_uuid_is_equal(&r->server_uuid, server_uuid))
 			return;
-		say_warn("server uuid changed to %s", tt_uuid_str(server_uuid));
+		say_warn("server UUID changed to %s",
+			 tt_uuid_str(server_uuid));
 		assert(vclock_has(&r->vclock, server_id));
 		memcpy(&r->server_uuid, server_uuid, sizeof(*server_uuid));
 		return;
diff --git a/src/box/iproto.cc b/src/box/iproto.cc
index c2b04236d9dff9a8d9d47821b2209efa34dffed9..814ad86818fe5fb7feafd740cf12a17739138947 100644
--- a/src/box/iproto.cc
+++ b/src/box/iproto.cc
@@ -46,6 +46,9 @@
 #include "xrow.h"
 #include "iproto_constants.h"
 #include "user_def.h"
+#include "authentication.h"
+#include "stat.h"
+#include "lua/call.h"
 
 /* {{{ iproto_request - declaration */
 
@@ -84,10 +87,7 @@ static void
 iproto_process_disconnect(struct iproto_request *request);
 
 static void
-iproto_process_dml(struct iproto_request *request);
-
-static void
-iproto_process_admin(struct iproto_request *request);
+iproto_process(struct iproto_request *request);
 
 struct IprotoRequestGuard {
 	struct iproto_request *ireq;
@@ -471,7 +471,7 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in)
 		if (reqend > in->end)
 			break;
 		struct iproto_request *ireq =
-			iproto_request_new(con, iproto_process_dml);
+			iproto_request_new(con, iproto_process);
 		IprotoRequestGuard guard(ireq);
 
 		xrow_header_decode(&ireq->header, &pos, reqend);
@@ -482,7 +482,9 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in)
 		 * as well as in->pos must not be advanced, to
 		 * stay in sync.
 		 */
-		if (iproto_type_is_dml(ireq->header.type)) {
+		if (ireq->header.type >= IPROTO_SELECT &&
+		    ireq->header.type <= IPROTO_AUTH) {
+			/* Pre-parse request before putting it into the queue */
 			if (ireq->header.bodycnt == 0) {
 				tnt_raise(ClientError, ER_INVALID_MSGPACK,
 					  "request type");
@@ -491,8 +493,6 @@ iproto_enqueue_batch(struct iproto_connection *con, struct ibuf *in)
 			pos = (const char *) ireq->header.body[0].iov_base;
 			request_decode(&ireq->request, pos,
 				       ireq->header.body[0].iov_len);
-		} else {
-			ireq->process = iproto_process_admin;
 		}
 		ireq->request.header = &ireq->header;
 		iproto_queue_push(&request_queue, guard.release());
@@ -630,45 +630,10 @@ iproto_connection_on_output(ev_loop *loop, struct ev_io *watcher,
 /* {{{ iproto_process_* functions */
 
 static void
-iproto_process_dml(struct iproto_request *ireq)
+iproto_process(struct iproto_request *ireq)
 {
 	struct iobuf *iobuf = ireq->iobuf;
-	struct iproto_connection *con = ireq->connection;
-
-	auto scope_guard = make_scoped_guard([=]{
-		/* Discard request (see iproto_enqueue_batch()) */
-		iobuf->in.pos += ireq->total_len;
-
-		if (evio_is_active(&con->output)) {
-			if (! ev_is_active(&con->output))
-				ev_feed_event(con->loop,
-					      &con->output,
-					      EV_WRITE);
-		} else if (iproto_connection_is_idle(con)) {
-			iproto_connection_delete(con);
-		}
-	});
-
-	if (unlikely(! evio_is_active(&con->output)))
-		return;
-
 	struct obuf *out = &iobuf->out;
-
-	struct iproto_port port;
-	iproto_port_init(&port, out, ireq->header.sync);
-	try {
-		box_process((struct port *) &port, &ireq->request);
-	} catch (Exception *e) {
-		if (port.found)
-			obuf_rollback_to_svp(out, &port.svp);
-		iproto_reply_error(out, e, ireq->header.sync);
-	}
-}
-
-static void
-iproto_process_admin(struct iproto_request *ireq)
-{
-	struct iobuf *iobuf = ireq->iobuf;
 	struct iproto_connection *con = ireq->connection;
 
 	auto scope_guard = make_scoped_guard([=]{
@@ -688,11 +653,33 @@ iproto_process_admin(struct iproto_request *ireq)
 	if (unlikely(! evio_is_active(&con->output)))
 		return;
 
+	struct obuf_svp svp = obuf_create_svp(out);
 	try {
 		switch (ireq->header.type) {
+		case IPROTO_SELECT:
+		case IPROTO_INSERT:
+		case IPROTO_REPLACE:
+		case IPROTO_UPDATE:
+		case IPROTO_DELETE:
+			struct iproto_port port;
+			iproto_port_init(&port, out, ireq->header.sync);
+			box_process(&ireq->request, (struct port *) &port);
+			break;
+		case IPROTO_CALL:
+			stat_collect(stat_base, ireq->request.type, 1);
+			box_lua_call(&ireq->request, &iobuf->out);
+			break;
+		case IPROTO_AUTH:
+		{
+			const char *user = ireq->request.key;
+			uint32_t len = mp_decode_strl(&user);
+			authenticate(user, len, ireq->request.tuple,
+				     ireq->request.tuple_end);
+			iproto_reply_ok(&ireq->iobuf->out, ireq->header.sync);
+			break;
+		}
 		case IPROTO_PING:
-			iproto_reply_ping(&ireq->iobuf->out,
-					  ireq->header.sync);
+			iproto_reply_ok(&ireq->iobuf->out, ireq->header.sync);
 			break;
 		case IPROTO_JOIN:
 			ev_io_stop(con->loop, &con->input);
@@ -713,7 +700,7 @@ iproto_process_admin(struct iproto_request *ireq)
 				   (uint32_t) ireq->header.type);
 		}
 	} catch (Exception *e) {
-		say_error("admin command error: %s", e->errmsg());
+		obuf_rollback_to_svp(&iobuf->out, &svp);
 		iproto_reply_error(&iobuf->out, e, ireq->header.sync);
 	}
 }
diff --git a/src/box/iproto_constants.c b/src/box/iproto_constants.c
index a0893501f7cf4bcb4e31acf23663fc7e05e1acb1..230454326d9df8f0866228558ca60f4fa1023d20 100644
--- a/src/box/iproto_constants.c
+++ b/src/box/iproto_constants.c
@@ -98,7 +98,7 @@ const char *iproto_type_strs[] =
 };
 
 #define bit(c) (1ULL<<IPROTO_##c)
-const uint64_t iproto_body_key_map[IPROTO_TYPE_DML_MAX] = {
+const uint64_t iproto_body_key_map[IPROTO_AUTH + 1] = {
 	0,                                                     /* unused */
 	bit(SPACE_ID) | bit(LIMIT) | bit(KEY),                 /* SELECT */
 	bit(SPACE_ID) | bit(TUPLE),                            /* INSERT */
diff --git a/src/box/iproto_constants.h b/src/box/iproto_constants.h
index e89ad4fd3301e3912b8d8753503b00ef1dd833f7..917dbe62d72b0f3bfb5862e210f98dd2c7592093 100644
--- a/src/box/iproto_constants.h
+++ b/src/box/iproto_constants.h
@@ -116,9 +116,10 @@ enum iproto_type {
 	IPROTO_REPLACE = 3,
 	IPROTO_UPDATE = 4,
 	IPROTO_DELETE = 5,
+	IPROTO_TYPE_DML_MAX = IPROTO_DELETE + 1,
 	IPROTO_CALL = 6,
+	IPROTO_TYPE_STAT_MAX = IPROTO_CALL + 1,
 	IPROTO_AUTH = 7,
-	IPROTO_TYPE_DML_MAX = IPROTO_AUTH + 1,
 	/* admin command codes */
 	IPROTO_PING = 64,
 	IPROTO_JOIN = 65,
diff --git a/src/box/iproto_port.cc b/src/box/iproto_port.cc
index d2810a08b550f478bdda7ab3b0dcfa7cf2f12858..8cfcb5bfd060460baded7829c46bdb2ab3870590 100644
--- a/src/box/iproto_port.cc
+++ b/src/box/iproto_port.cc
@@ -69,12 +69,14 @@ iproto_encode_error(uint32_t error)
 }
 
 void
-iproto_reply_ping(struct obuf *out, uint64_t sync)
+iproto_reply_ok(struct obuf *out, uint64_t sync)
 {
 	struct iproto_header_bin reply = iproto_header_bin;
-	reply.v_len = mp_bswap_u32(sizeof(iproto_header_bin) - 5);
+	reply.v_len = mp_bswap_u32(sizeof(iproto_header_bin) - 5 + 1);
 	reply.v_sync = mp_bswap_u64(sync);
+	uint8_t empty_map[1] = { 0x80 };
 	obuf_dup(out, &reply, sizeof(reply));
+	obuf_dup(out, &empty_map, sizeof(empty_map));
 }
 
 static inline uint32_t
@@ -121,20 +123,32 @@ iproto_port_eof(struct port *ptr)
 	struct iproto_port *port = iproto_port(ptr);
 	/* found == 0 means add_tuple wasn't called at all. */
 	if (port->found == 0) {
-		port->svp = obuf_book(port->buf, SVP_SIZE);
-		port->size += SVP_SIZE;
+		port->svp = iproto_prepare_select(port->buf);
 	}
 
-	uint32_t len = port->size - 5;
+	iproto_reply_select(port->buf, &port->svp, port->sync, port->found);
+}
+
+struct obuf_svp
+iproto_prepare_select(struct obuf *buf)
+{
+	return obuf_book(buf, SVP_SIZE);
+}
+
+void
+iproto_reply_select(struct obuf *buf, struct obuf_svp *svp, uint64_t sync,
+			uint32_t count)
+{
+	uint32_t len = obuf_size(buf) - svp->size - 5;
 
 	struct iproto_header_bin header = iproto_header_bin;
 	header.v_len = mp_bswap_u32(len);
-	header.v_sync = mp_bswap_u64(port->sync);
+	header.v_sync = mp_bswap_u64(sync);
 
 	struct iproto_body_bin body = iproto_body_bin;
-	body.v_data_len = mp_bswap_u32(port->found);
+	body.v_data_len = mp_bswap_u32(count);
 
-	char *pos = (char *) obuf_svp_to_ptr(port->buf, &port->svp);
+	char *pos = (char *) obuf_svp_to_ptr(buf, svp);
 	memcpy(pos, &header, sizeof(header));
 	memcpy(pos + sizeof(header), &body, sizeof(body));
 }
@@ -145,11 +159,9 @@ iproto_port_add_tuple(struct port *ptr, struct tuple *tuple)
 	struct iproto_port *port = iproto_port(ptr);
 	if (++port->found == 1) {
 		/* Found the first tuple, add header. */
-		port->svp = obuf_book(port->buf, SVP_SIZE);
-		port->size += SVP_SIZE;
+		port->svp = iproto_prepare_select(port->buf);
 	}
 	tuple_to_obuf(tuple, port->buf);
-	port->size += tuple->bsize;
 }
 
 struct port_vtab iproto_port_vtab = {
diff --git a/src/box/iproto_port.h b/src/box/iproto_port.h
index 9349f71c60c568605bc84ae191188d23ed0d8015..4233271ee2d2785943f10557d42a216688f8b9e3 100644
--- a/src/box/iproto_port.h
+++ b/src/box/iproto_port.h
@@ -64,7 +64,6 @@ struct iproto_port
 	/** A pointer in the reply buffer where the reply starts. */
 	struct obuf_svp svp;
 	/** Size of data written after reply starts */
-	uint32_t size;
 };
 
 extern struct port_vtab iproto_port_vtab;
@@ -77,15 +76,21 @@ iproto_port_init(struct iproto_port *port, struct obuf *buf,
 	port->buf = buf;
 	port->sync = sync;
 	port->found = 0;
-	port->size = 0;
 }
 
 /** Stack a reply to 'ping' packet. */
 void
-iproto_reply_ping(struct obuf *out, uint64_t sync);
+iproto_reply_ok(struct obuf *out, uint64_t sync);
 
 /** Send an error packet back. */
 void
 iproto_reply_error(struct obuf *out, const Exception *e, uint64_t sync);
 
+struct obuf_svp
+iproto_prepare_select(struct obuf *buf);
+
+void
+iproto_reply_select(struct obuf *buf, struct obuf_svp *svp, uint64_t sync,
+		    uint32_t count);
+
 #endif /* TARANTOOL_IPROTO_PORT_H_INCLUDED */
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index 196eefdd85047893f2b49ee11865640739a62ba2..eb661774c1ad8e9c71f2948c7cf36df5dc6cac72 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -53,6 +53,7 @@
 #include "box/schema.h"
 #include "box/session.h"
 #include "box/iproto_constants.h"
+#include "box/iproto_port.h"
 
 /* contents of box.lua, misc.lua, box.net.lua respectively */
 extern char session_lua[],
@@ -147,60 +148,6 @@ port_lua_table_create(struct lua_State *L)
 	return (struct port *) port;
 }
 
-static void
-port_add_lua_ret(struct port *port, struct lua_State *L, int index)
-{
-	struct tuple *tuple = lua_totuple(L, index, index);
-	TupleGuard guard(tuple);
-	port_add_tuple(port, tuple);
-}
-
-/**
- * Add all elements from Lua stack to fiber iov.
- *
- * To allow clients to understand a complex return from
- * a procedure, we are compatible with SELECT protocol,
- * and return the number of return values first, and
- * then each return value as a tuple.
- *
- * If a Lua stack contains at least one scalar, each
- * value on the stack is converted to a tuple. A Lua
- * is converted to a tuple with multiple fields.
- *
- * If the stack is a Lua table, each member of which is
- * not scalar, each member of the table is converted to
- * a tuple. This way very large lists of return values can
- * be used, since Lua stack size is limited by 8000 elements,
- * while Lua table size is pretty much unlimited.
- */
-static void
-port_add_lua_multret(struct port *port, struct lua_State *L)
-{
-	int nargs = lua_gettop(L);
-	/** Check if we deal with a table of tables. */
-	if (nargs == 1 && lua_istable(L, 1)) {
-		/*
-		 * The table is not empty and consists of tables
-		 * or tuples. Treat each table element as a tuple,
-		 * and push it.
-		 */
-		lua_pushnil(L);
-		int has_keys = lua_next(L, 1);
-		if (has_keys  && (lua_istable(L, -1) || lua_istuple(L, -1))) {
-			do {
-				port_add_lua_ret(port, L, lua_gettop(L));
-				lua_pop(L, 1);
-			} while (lua_next(L, 1));
-			return;
-		} else if (has_keys) {
-			lua_pop(L, 1);
-		}
-	}
-	for (int i = 1; i <= nargs; ++i) {
-		port_add_lua_ret(port, L, i);
-	}
-}
-
 /* }}} */
 
 /**
@@ -236,7 +183,7 @@ lbox_process(lua_State *L)
 	struct request request;
 	request_create(&request, op);
 	request_decode(&request, req, sz);
-	box_process(port_lua, &request);
+	box_process(&request, port_lua);
 
 	return 1;
 }
@@ -319,7 +266,7 @@ boxffi_select(struct port *port, uint32_t space_id, uint32_t index_id,
 	request.key_end = key_end;
 
 	try {
-		box_process(port, &request);
+		box_process(&request, port);
 		return 0;
 	} catch (Exception *e) {
 		/* will be hanled by box.error() in Lua */
@@ -337,7 +284,7 @@ lbox_insert(lua_State *L)
 	struct port_lua port;
 	lbox_request_create(&request, L, IPROTO_INSERT, -1, 2);
 	port_lua_create(&port, L);
-	box_process((struct port *) &port, &request);
+	box_process(&request, (struct port *) &port);
 	return lua_gettop(L) - 2;
 }
 
@@ -351,7 +298,7 @@ lbox_replace(lua_State *L)
 	struct port_lua port;
 	lbox_request_create(&request, L, IPROTO_REPLACE, -1, 2);
 	port_lua_create(&port, L);
-	box_process((struct port *) &port, &request);
+	box_process(&request, (struct port *) &port);
 	return lua_gettop(L) - 2;
 }
 
@@ -367,7 +314,7 @@ lbox_update(lua_State *L)
 	request.field_base = 1; /* field ids are one-indexed */
 	port_lua_create(&port, L);
 	/* Ignore index_id for now */
-	box_process((struct port *) &port, &request);
+	box_process(&request, (struct port *) &port);
 	return lua_gettop(L) - 4;
 }
 
@@ -382,7 +329,7 @@ lbox_delete(lua_State *L)
 	lbox_request_create(&request, L, IPROTO_DELETE, 3, -1);
 	port_lua_create(&port, L);
 	/* Ignore index_id for now */
-	box_process((struct port *) &port, &request);
+	box_process(&request, (struct port *) &port);
 	return lua_gettop(L) - 3;
 }
 
@@ -547,11 +494,9 @@ SetuidGuard::~SetuidGuard()
  * Invoke a Lua stored procedure from the binary protocol
  * (implementation of 'CALL' command code).
  */
-void
-box_lua_call(struct request *request, struct port *port)
+static inline void
+execute_call(lua_State *L, struct request *request, struct obuf *out)
 {
-	lua_State *L = lua_newthread(tarantool_L);
-	LuarefGuard coro_ref(tarantool_L);
 	const char *name = request->key;
 	uint32_t name_len = mp_decode_strl(&name);
 
@@ -573,9 +518,76 @@ box_lua_call(struct request *request, struct port *port)
 	for (uint32_t i = 0; i < arg_count; i++) {
 		luamp_decode(L, luaL_msgpack_default, &args);
 	}
-	lbox_call(L, arg_count + oc - 1, LUA_MULTRET);
-	/* Send results of the called procedure to the client. */
-	port_add_lua_multret(port, L);
+	lua_call(L, arg_count + oc - 1, LUA_MULTRET);
+
+	/**
+	 * Add all elements from Lua stack to iproto.
+	 *
+	 * To allow clients to understand a complex return from
+	 * a procedure, we are compatible with SELECT protocol,
+	 * and return the number of return values first, and
+	 * then each return value as a tuple.
+	 *
+	 * If a Lua stack contains at least one scalar, each
+	 * value on the stack is converted to a tuple. A Lua
+	 * is converted to a tuple with multiple fields.
+	 *
+	 * If the stack is a Lua table, each member of which is
+	 * not scalar, each member of the table is converted to
+	 * a tuple. This way very large lists of return values can
+	 * be used, since Lua stack size is limited by 8000 elements,
+	 * while Lua table size is pretty much unlimited.
+	 */
+
+	uint32_t count = 0;
+	struct obuf_svp svp = iproto_prepare_select(out);
+
+	/** Check if we deal with a table of tables. */
+	int nrets = lua_gettop(L);
+	if (nrets == 1 && lua_istable(L, 1)) {
+		/*
+		 * The table is not empty and consists of tables
+		 * or tuples. Treat each table element as a tuple,
+		 * and push it.
+		 */
+		lua_pushnil(L);
+		int has_keys = lua_next(L, 1);
+		if (has_keys  && (lua_istable(L, -1) || lua_istuple(L, -1))) {
+			do {
+				int top = lua_gettop(L);
+				luamp_encodestack(L, out, top, top);
+				++count;
+				lua_pop(L, 1);
+			} while (lua_next(L, 1));
+			goto done;
+		} else if (has_keys) {
+			lua_pop(L, 1);
+		}
+	}
+	for (int i = 1; i <= nrets; ++i) {
+		luamp_encodestack(L, out, i, i);
+		++count;
+	}
+
+done:
+	iproto_reply_select(out, &svp, request->header->sync, count);
+}
+
+void
+box_lua_call(struct request *request, struct obuf *out)
+{
+	lua_State *L = NULL;
+	try {
+		L = lua_newthread(tarantool_L);
+		LuarefGuard coro_ref(tarantool_L);
+		execute_call(L, request, out);
+	} catch (Exception *e) {
+		/* Let all well-behaved exceptions pass through. */
+		throw;
+	} catch (...) {
+		/* Convert Lua error to a Tarantool exception. */
+		tnt_raise(LuajitError, L != NULL ? L : tarantool_L);
+	}
 }
 
 static int
diff --git a/src/box/lua/call.h b/src/box/lua/call.h
index 44ea45bd4a9abb987d4bdf666748ac8479dac0d6..bbb1a41f5a10613d5554b66a216ad2de24285996 100644
--- a/src/box/lua/call.h
+++ b/src/box/lua/call.h
@@ -39,7 +39,7 @@ struct port;
  * (implementation of 'CALL' command code).
  */
 void
-box_lua_call(struct request *request, struct port *port);
+box_lua_call(struct request *request, struct obuf *out);
 
 extern "C" {
 struct port_ffi
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index fe16c3316b8f0ae6f5d811b2c91c773369c30171..17a980992b6a3e87d5ab2398a6606974fc51331f 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -1085,7 +1085,7 @@ box.schema.user.drop = function(name)
 end
 
 box.schema.user.grant = function(user_name, privilege, object_type,
-                                 object_name, grantor)
+                                 object_name, options)
     -- From user point of view, role is the same thing
     -- as a privilege. Allow syntax grant(user, role).
     if object_name == nil and object_type == nil then
@@ -1104,10 +1104,16 @@ box.schema.user.grant = function(user_name, privilege, object_type,
     end
     privilege_hex = privilege_resolve(privilege)
     local oid = object_resolve(object_type, object_name)
-    if grantor == nil then
-        grantor = session.uid()
+    if options == nil then
+        options = {}
+    end
+    if options.grantor == nil then
+        options.grantor = session.uid()
     else
-        grantor = user_or_role_resolve(grantor)
+        options.grantor = user_or_role_resolve(options.grantor)
+    end
+    if options.if_not_exists == nil then
+        options.if_not_exists = false
     end
     local _priv = box.space[box.schema.PRIV_ID]
     -- add the granted privilege to the current set
@@ -1123,18 +1129,18 @@ box.schema.user.grant = function(user_name, privilege, object_type,
     -- XXX bug if we decide to add a grant option: new grantor
     -- replaces the old one, old grantor is lost
     if privilege_hex ~= old_privilege then
-        _priv:replace{grantor, uid, object_type, oid, privilege_hex}
-    else
-        if object_type == 'role' then
-            box.error(box.error.ROLE_GRANTED, user_name, object_name)
-        else
-            box.error(box.error.PRIV_GRANTED, user_name, privilege,
-                      object_type, object_name)
-        end
+        _priv:replace{options.grantor, uid, object_type, oid, privilege_hex}
+    elseif options.if_not_exists == false then
+            if object_type == 'role' then
+                box.error(box.error.ROLE_GRANTED, user_name, object_name)
+            else
+                box.error(box.error.PRIV_GRANTED, user_name, privilege,
+                          object_type, object_name)
+            end
     end
 end
 
-box.schema.user.revoke = function(user_name, privilege, object_type, object_name)
+box.schema.user.revoke = function(user_name, privilege, object_type, object_name, options)
     -- From user point of view, role is the same thing
     -- as a privilege. Allow syntax revoke(user, role).
     if object_name == nil and object_type == nil then
@@ -1148,10 +1154,19 @@ box.schema.user.revoke = function(user_name, privilege, object_type, object_name
     if uid == nil then
         box.error(box.error.NO_SUCH_USER, name)
     end
+    if options == nil then
+        options = {}
+    end
+    if options.if_exists == nil then
+        options.if_exists = false
+    end
     local oid = object_resolve(object_type, object_name)
     local _priv = box.space[box.schema.PRIV_ID]
     local tuple = _priv:get{uid, object_type, oid}
     if tuple == nil then
+        if options.if_exists then
+            return
+        end
         if object_type == 'role' then
             box.error(box.error.ROLE_NOT_GRANTED, user_name, object_name)
         else
diff --git a/src/box/lua/tuple.h b/src/box/lua/tuple.h
index 05a830b168ad46897ccd6fc49a28bc52235d8db2..6943d64fee5488f2cde5436e56183b3a8102faff 100644
--- a/src/box/lua/tuple.h
+++ b/src/box/lua/tuple.h
@@ -31,7 +31,6 @@
 struct lua_State;
 struct txn;
 struct tuple;
-struct tbuf;
 
 /**
  * Push tuple on lua stack
@@ -46,7 +45,7 @@ struct tuple*
 lua_totuple(struct lua_State *L, int first, int last);
 
 int
-luamp_encodestack(struct lua_State *L, struct tbuf *b,
+luamp_encodestack(struct lua_State *L, struct obuf *b,
 		  int first, int last);
 
 void
diff --git a/src/box/recovery.cc b/src/box/recovery.cc
index b4077b17d9ea8ddc102a45c413c8564f73a28cc2..619f4b6e212b18e0cc89746b632b731df416019c 100644
--- a/src/box/recovery.cc
+++ b/src/box/recovery.cc
@@ -168,9 +168,9 @@ recovery_new(const char *snap_dirname, const char *wal_dirname,
 
 	r->snapshot_handler = snapshot_handler;
 
-	xdir_create(&r->snap_dir, snap_dirname, SNAP);
+	xdir_create(&r->snap_dir, snap_dirname, SNAP, &r->server_uuid);
 
-	xdir_create(&r->wal_dir, wal_dirname, XLOG);
+	xdir_create(&r->wal_dir, wal_dirname, XLOG, &r->server_uuid);
 
 	if (r->wal_mode == WAL_FSYNC)
 		(void) strcat(r->wal_dir.open_wflags, "s");
@@ -181,7 +181,14 @@ recovery_new(const char *snap_dirname, const char *wal_dirname,
 
 	try {
 		xdir_scan(&r->snap_dir);
-		xdir_scan(&r->wal_dir);
+		/**
+		 * Avoid scanning WAL dir before we recovered
+		 * the snapshot and know server UUID - this will
+		 * make sure the scan skips files with wrong
+		 * UUID, see replication/cluster.test for
+		 * details.
+		 */
+		xdir_check(&r->wal_dir);
 	} catch (Exception *e) {
 		e->log();
 		panic("can't scan a data directory");
@@ -265,10 +272,8 @@ recovery_bootstrap(struct recovery_state *r)
 	const char *filename = "bootstrap.snap";
 	FILE *f = fmemopen((void *) &bootstrap_bin,
 			   sizeof(bootstrap_bin), "r");
-	struct xlog *snap = xlog_open_stream(&r->snap_dir,
-		filename, NULL, NONE, f);
-	if (snap == NULL)
-		panic("failed to open %s", filename);
+	struct xlog *snap = xlog_open_stream(&r->snap_dir, 0, NONE, f,
+					     filename);
 	int rc = recover_xlog(r, snap);
 	xlog_close(snap);
 
@@ -300,12 +305,10 @@ recover_snap(struct recovery_state *r)
 	if (res == NULL)
 		panic("can't find snapshot");
 	sign = vclock_signature(res);
-	snap = xlog_open(&r->snap_dir, sign, NULL, NONE);
-	if (snap == NULL)
-		panic("can't open snapshot");
+	snap = xlog_open(&r->snap_dir, sign, NONE);
 
 	/* Save server uuid */
-	memcpy(&r->server_uuid, &snap->server_uuid, sizeof(r->server_uuid));
+	r->server_uuid = snap->server_uuid;
 
 	/* Add a surrogate server id for snapshot rows */
 	vclock_add_server(&r->vclock, 0);
@@ -314,7 +317,7 @@ recover_snap(struct recovery_state *r)
 	if (recover_xlog(r, snap) != 0)
 		panic("can't process snapshot");
 
-	/* Replace server vclock using data from snapshot */
+	/* Replace server vclock using the data from snapshot */
 	vclock_copy(&r->vclock, &snap->vclock);
 }
 
@@ -414,13 +417,14 @@ recover_remaining_wals(struct recovery_state *r)
 		 * xlog_rename().
 		 */
 		suffix = current_signt == last_signt ? INPROGRESS : NONE;
-		next_wal = xlog_open(&r->wal_dir, current_signt,
-			&r->server_uuid, suffix);
-		/*
-		 * When doing final recovery, and dealing with the
-		 * last file, try opening .<ext>.inprogress.
-		 */
-		if (next_wal == NULL) {
+		try {
+			next_wal = xlog_open(&r->wal_dir, current_signt, suffix);
+		} catch (XlogError *e) {
+			e->log();
+			/*
+			 * When doing final recovery, and dealing with the
+			 * last file, try opening .<ext>.inprogress.
+			 */
 			if (r->finalize && suffix == INPROGRESS) {
 				/*
 				 * There is an .inprogress file, but
@@ -936,7 +940,7 @@ wal_opt_rotate(struct xlog **wal, struct recovery_state *r,
 	}
 	if (l == NULL) {
 		/* Open WAL with '.inprogress' suffix. */
-		l = xlog_create(&r->wal_dir, &r->server_uuid, vclock);
+		l = xlog_create(&r->wal_dir, vclock);
 		/*
 		 * Close the file *after* we create the new WAL, since
 		 * this is when replication relays get an inotify alarm
@@ -1200,8 +1204,7 @@ void
 snapshot_save(struct recovery_state *r)
 {
 	assert(r->snapshot_handler != NULL);
-	struct xlog *snap = xlog_create(&r->snap_dir, &r->server_uuid,
-					&r->vclock);
+	struct xlog *snap = xlog_create(&r->snap_dir, &r->vclock);
 	if (snap == NULL)
 		panic_status(errno, "Failed to save snapshot: failed to open file in write mode.");
 	/*
diff --git a/src/box/request.cc b/src/box/request.cc
index e3289d6d58cf7f49d22cf55c8b6723aa1fbb0be7..8e1cb396fdf597aa1d6271f3e29aa47253065a41 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -38,10 +38,11 @@
 #include <errinj.h>
 #include <fiber.h>
 #include <scoped_guard.h>
-#include <third_party/base64.h>
-#include "authentication.h"
 #include "user_def.h"
 #include "iproto_constants.h"
+#include "stat.h"
+
+int stat_base;
 
 enum dup_replace_mode
 dup_replace_mode(uint32_t op)
@@ -160,35 +161,41 @@ execute_select(struct request *request, struct port *port)
 	}
 }
 
+/** }}} */
+
 void
-execute_auth(struct request *request, struct port * /* port */)
+request_create(struct request *request, uint32_t type)
 {
-	const char *user = request->key;
-	uint32_t len = mp_decode_strl(&user);
-	authenticate(user, len, request->tuple, request->tuple_end);
+
+	memset(request, 0, sizeof(*request));
+	request->type = type;
 }
 
-/** }}} */
+typedef void (*request_execute_f)(struct request *, struct port *);
 
 void
-request_create(struct request *request, uint32_t type)
+process_rw(struct request *request, struct port *port)
 {
-	if (!iproto_type_is_dml(type))
-		tnt_raise(LoggedError, ER_UNKNOWN_REQUEST_TYPE, type);
+	assert(iproto_type_is_dml(request->type));
 	static const request_execute_f execute_map[] = {
 		NULL, execute_select, execute_replace, execute_replace,
-		execute_update, execute_delete, box_lua_call,
-		execute_auth,
+		execute_update, execute_delete
 	};
-	memset(request, 0, sizeof(*request));
-	request->type = type;
-	request->execute = execute_map[type];
+	request_execute_f fun = execute_map[request->type];
+	assert(fun != NULL);
+	stat_collect(stat_base, request->type, 1);
+	try {
+		fun(request, port);
+		port_eof(port);
+	} catch (Exception *e) {
+		txn_rollback_stmt();
+		throw;
+	}
 }
 
 void
 request_decode(struct request *request, const char *data, uint32_t len)
 {
-	assert(request->execute != NULL);
 	const char *end = data + len;
 	uint64_t key_map = iproto_body_key_map[request->type];
 
diff --git a/src/box/request.h b/src/box/request.h
index e257109f65fc3b07820e2d040831cc57b7c77b8c..b63f3dd74da04c797e85f2153dd55d09c1fcaab3 100644
--- a/src/box/request.h
+++ b/src/box/request.h
@@ -33,8 +33,7 @@
 
 struct txn;
 struct port;
-
-typedef void (*request_execute_f)(struct request *, struct port *);
+extern int stat_base;
 
 struct request
 {
@@ -61,13 +60,14 @@ struct request
 	const char *tuple_end;
 	/** Base field offset for error messages, e.g. 0 for C and 1 for Lua. */
 	int field_base;
-
-	request_execute_f execute;
 };
 
 void
 request_create(struct request *request, uint32_t code);
 
+void
+process_rw(struct request *request, struct port *port);
+
 void
 request_decode(struct request *request, const char *data, uint32_t len);
 
diff --git a/src/box/xlog.cc b/src/box/xlog.cc
index d9f033a14f26974093fa97a13222d6d001481ae0..452429475a935241b88a0cfc94f953e667e7b7ab 100644
--- a/src/box/xlog.cc
+++ b/src/box/xlog.cc
@@ -54,16 +54,27 @@ static const log_magic_t eof_marker = mp_bswap_u32(0xd510aded); /* host byte ord
 static const char inprogress_suffix[] = ".inprogress";
 static const char v12[] = "0.12\n";
 
+XlogError::XlogError(const char *file, unsigned line,
+		     const char *format, ...)
+	:Exception(file, line)
+{
+	va_list ap;
+	va_start(ap, format);
+	vsnprintf(m_errmsg, sizeof(m_errmsg), format, ap);
+	va_end(ap);
+}
+
 /* {{{ struct xdir */
 
 void
 xdir_create(struct xdir *dir, const char *dirname,
-	    enum xdir_type type)
+	    enum xdir_type type, const tt_uuid *server_uuid)
 {
 	memset(dir, 0, sizeof(*dir));
 	vclockset_new(&dir->index);
 	/* Default mode. */
 	dir->mode = 0660;
+	dir->server_uuid = server_uuid;
 	snprintf(dir->dirname, PATH_MAX, "%s", dirname);
 	if (type == SNAP) {
 		strcpy(dir->open_wflags, "wxd");
@@ -114,40 +125,39 @@ xdir_index_file(struct xdir *dir, int64_t signature)
 	 * The vclock stores the state of the log at the
 	 * time it is created.
 	 */
-	struct xlog *wal = xlog_open(dir, signature, NULL, INPROGRESS);
-	if (wal == NULL) {
-		tnt_raise(ClientError, ER_INVALID_XLOG,
-			  format_filename(dir, signature, NONE));
+	struct xlog *wal;
+
+	try {
+		wal = xlog_open(dir, signature, INPROGRESS);
+		/*
+		 * All log files in a directory must satisfy Lamport's
+		 * eventual order: events in each log file must be
+		 * separable with consistent cuts, for example:
+		 *
+		 * log1: {1, 1, 0, 1}, log2: {1, 2, 0, 2} -- good
+		 * log2: {1, 1, 0, 1}, log2: {2, 0, 2, 0} -- bad
+		 */
+		struct vclock *dup = vclockset_search(&dir->index,
+						      &wal->vclock);
+		if (dup != NULL) {
+			XlogError *e = tnt_error(XlogError,
+						 "%s: invalid xlog order",
+						 wal->filename);
+			xlog_close(wal);
+			throw e;
+		}
+	} catch (XlogError *e) {
+		if (dir->panic_if_error)
+			throw;
+		/** Skip a corrupted file */
+		e->log();
+		return;
 	}
+
 	auto log_guard = make_scoped_guard([=]{
 		xlog_close(wal);
 	});
 
-	/*
-	 * Check the match between log file name and contents:
-	 * the sum of vector clock coordinates must be the same
-	 * as the name of the file.
-	 */
-	int64_t signature_check = vclock_signature(&wal->vclock);
-	if (signature_check != signature) {
-		tnt_raise(ClientError, ER_INVALID_XLOG_NAME,
-			  (long long) signature_check,
-			  (long long) signature);
-	}
-	/*
-	 * All log files in a directory must satisfy Lamport's
-	 * eventual order: events in each log file must be
-	 * separable with consistent cuts, for example:
-	 *
-	 * log1: {1, 1, 0, 1}, log2: {1, 2, 0, 2} -- good
-	 * log2: {1, 1, 0, 1}, log2: {2, 0, 2, 0} -- bad
-	 */
-	struct vclock *dup = vclockset_search(&dir->index, &wal->vclock);
-	if (dup != NULL) {
-		tnt_raise(ClientError, ER_INVALID_XLOG_ORDER,
-			  (long long) signature,
-			  (long long) vclock_signature(dup));
-	}
 	/*
 	 * Append the clock describing the file to the
 	 * directory index.
@@ -212,7 +222,7 @@ xdir_scan(struct xdir *dir)
 			  dir->dirname);
 	}
 
-	auto log_guard = make_scoped_guard([&]{
+	auto dir_guard = make_scoped_guard([&]{
 		closedir(dh);
 		free(signatures);
 	});
@@ -278,7 +288,7 @@ xdir_scan(struct xdir *dir)
 			signatures = (int64_t *) realloc(signatures, size);
 			if (signatures == NULL) {
 				tnt_raise(OutOfMemory,
-					  size, "signatures array", "realloc");
+					  size, "realloc", "signatures array");
 			}
 		}
 		signatures[s_count++] = signature;
@@ -304,15 +314,7 @@ xdir_scan(struct xdir *dir)
 			vclock = next;
 		} else if (s_old > s_new) {
 			/** Add a new file. */
-			try {
-				xdir_index_file(dir, s_new);
-			} catch (Exception *e) {
-				e->log();
-				say_warn("failed to scan %s",
-					 format_filename(dir, s_new, NONE));
-				if (dir->panic_if_error)
-					throw;
-			}
+			xdir_index_file(dir, s_new);
 			i++;
 		} else {
 			assert(s_old == s_new && i < s_count &&
@@ -323,6 +325,17 @@ xdir_scan(struct xdir *dir)
 	}
 }
 
+void
+xdir_check(struct xdir *dir)
+{
+	DIR *dh = opendir(dir->dirname);        /* log dir */
+	if (dh == NULL) {
+		tnt_raise(SystemError, "error reading directory '%s'",
+			  dir->dirname);
+	}
+	closedir(dh);
+}
+
 char *
 format_filename(struct xdir *dir, int64_t signature,
 		enum log_suffix suffix)
@@ -355,8 +368,11 @@ row_reader(FILE *f, struct xrow_header *row)
 		if (feof(f))
 			return 1;
 error:
-		tnt_raise(ClientError, ER_INVALID_MSGPACK,
-			  "invalid fixed header");
+		char buf[PATH_MAX];
+		snprintf(buf, sizeof(buf), "%s: failed to read or parse row header"
+			 " at offset %zu", fio_filename(fileno(f)),
+			 (uint64_t) ftello(f));
+		tnt_raise(ClientError, ER_INVALID_MSGPACK, buf);
 	}
 
 	/* Decode len, previous crc32 and row crc32 */
@@ -370,8 +386,11 @@ row_reader(FILE *f, struct xrow_header *row)
 		goto error;
 	uint32_t len = mp_decode_uint(&data);
 	if (len > IPROTO_BODY_LEN_MAX) {
-		tnt_raise(ClientError, ER_INVALID_MSGPACK,
-			  "packet is too big");
+		char buf[PATH_MAX];
+		snprintf(buf, sizeof(buf),
+			 "%s: row is too big at offset %zu",
+			 fio_filename(fileno(f)), (uint64_t) ftello(f));
+		tnt_raise(ClientError, ER_INVALID_MSGPACK, buf);
 	}
 
 	/* Read previous crc32 */
@@ -394,8 +413,15 @@ row_reader(FILE *f, struct xrow_header *row)
 		return 1;
 
 	/* Validate checksum */
-	if (crc32_calc(0, bodybuf, len) != crc32c)
-		tnt_raise(ClientError, ER_INVALID_MSGPACK, "invalid crc32");
+	if (crc32_calc(0, bodybuf, len) != crc32c) {
+		char buf[PATH_MAX];
+
+		snprintf(buf, sizeof(buf), "%s: row checksum mismatch (expected %u)"
+			 " at offset %zu",
+			 fio_filename(fileno(f)), (unsigned) crc32c,
+			 (uint64_t) ftello(f));
+		tnt_raise(ClientError, ER_INVALID_MSGPACK, buf);
+	}
 
 	data = bodybuf;
 	xrow_header_decode(row, &data, bodybuf + len);
@@ -459,9 +485,7 @@ xlog_cursor_close(struct xlog_cursor *i)
 	 * Seek back to last known good offset.
 	 */
 	fseeko(l->f, i->good_offset, SEEK_SET);
-#if 0
 	region_free(&fiber()->gc);
-#endif
 }
 
 /**
@@ -483,14 +507,12 @@ xlog_cursor_next(struct xlog_cursor *i, struct xrow_header *row)
 	say_debug("xlog_cursor_next: marker:0x%016X/%zu",
 		  row_marker, sizeof(row_marker));
 
-#if 0
 	/*
 	 * Don't let gc pool grow too much. Yet to
 	 * it before reading the next row, to make
 	 * sure it's not freed along here.
 	 */
 	region_free_after(&fiber()->gc, 128 * 1024);
-#endif
 
 restart:
 	if (marker_offset > 0)
@@ -522,8 +544,7 @@ xlog_cursor_next(struct xlog_cursor *i, struct xrow_header *row)
 		if (l->dir->panic_if_error)
 			throw;
 		else {
-			say_warn("failed to read row:");
-			e->log();
+			say_warn("failed to read row");
 			goto restart;
 		}
 	}
@@ -538,8 +559,8 @@ xlog_cursor_next(struct xlog_cursor *i, struct xrow_header *row)
 eof:
 	/*
 	 * The only two cases of fully read file:
-	 * 1. sizeof(eof_marker) > 0 and it is the last record in file
-	 * 2. sizeof(eof_marker) == 0 and there is no unread data in file
+	 * 1. eof_marker is present and it is the last record in file
+	 * 2. eof_marker is missing but there is no unread data in file
 	 */
 	if (ftello(l->f) == i->good_offset + sizeof(eof_marker)) {
 		fseeko(l->f, i->good_offset, SEEK_SET);
@@ -587,7 +608,7 @@ xlog_rename(struct xlog *l)
 	new_filename[suffix - filename] = '\0';
 
 	if (rename(filename, new_filename) != 0) {
-		say_syserror("can't rename %s to %s", filename, new_filename);
+		say_syserror("%s: rename to %s failed", filename, new_filename);
 
 		return -1;
 	}
@@ -608,7 +629,7 @@ inprogress_log_unlink(char *filename)
 		if (errno == ENOENT)
 			return 0;
 
-		say_syserror("can't unlink %s", filename);
+		say_syserror("%s: unlink() failed", filename);
 
 		return -1;
 	}
@@ -634,12 +655,13 @@ xlog_close(struct xlog *l)
 		if (! strchr(l->dir->open_wflags, 's'))
 			xlog_sync(l);
 		if (l->is_inprogress && xlog_rename(l) != 0)
-			panic("can't rename 'inprogress' WAL");
+			panic("%s: rename() of 'inprogress' WAL failed",
+			      l->filename);
 	}
 
 	r = fclose(l->f);
 	if (r < 0)
-		say_syserror("can't close");
+		say_syserror("%s: close() failed", l->filename);
 	free(l);
 	return r;
 }
@@ -668,11 +690,13 @@ xlog_atfork(struct xlog **lptr)
 static int
 sync_cb(eio_req *req)
 {
-	if (req->result)
-		say_error("%s: fsync failed, errno: %d",
-			  __func__, (int) req->result);
-
 	int fd = (intptr_t) req->data;
+	if (req->result) {
+		errno = req->result;
+		say_syserror("%s: fsync() failed",
+			     fio_filename(fd));
+		errno = 0;
+	}
 	close(fd);
 	return 0;
 }
@@ -683,7 +707,7 @@ xlog_sync(struct xlog *l)
 	if (l->dir->sync_is_async) {
 		int fd = dup(fileno(l->f));
 		if (fd == -1) {
-			say_syserror("%s: dup() failed", __func__);
+			say_syserror("%s: dup() failed", l->filename);
 			return -1;
 		}
 		eio_fsync(fd, 0, sync_cb, (void *) (intptr_t) fd);
@@ -698,14 +722,13 @@ xlog_sync(struct xlog *l)
 #define VCLOCK_KEY "VClock"
 
 static int
-xlog_write_meta(struct xlog *l, const tt_uuid *server_uuid,
-		  const struct vclock *vclock)
+xlog_write_meta(struct xlog *l)
 {
 	char *vstr = NULL;
 	if (fprintf(l->f, "%s%s", l->dir->filetype, v12) < 0 ||
 	    fprintf(l->f, SERVER_UUID_KEY ": %s\n",
-		    tt_uuid_str(server_uuid)) < 0 ||
-	    (vstr = vclock_to_string(vclock)) == NULL ||
+		    tt_uuid_str(l->dir->server_uuid)) < 0 ||
+	    (vstr = vclock_to_string(&l->vclock)) == NULL ||
 	    fprintf(l->f, VCLOCK_KEY ": %s\n\n", vstr) < 0) {
 		free(vstr);
 		return -1;
@@ -722,8 +745,8 @@ xlog_write_meta(struct xlog *l, const tt_uuid *server_uuid,
  *
  * @return 0 if success, -1 on error.
  */
-static int
-xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid)
+static void
+xlog_read_meta(struct xlog *l, int64_t signature)
 {
 	char filetype[32], version[32], buf[256];
 	struct xdir *dir = l->dir;
@@ -731,23 +754,22 @@ xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid)
 
 	if (fgets(filetype, sizeof(filetype), stream) == NULL ||
 	    fgets(version, sizeof(version), stream) == NULL) {
-		say_error("%s: failed to read log file header", l->filename);
-		return -1;
+		tnt_raise(XlogError, "%s: failed to read log file header",
+			  l->filename);
 	}
 	if (strcmp(dir->filetype, filetype) != 0) {
-		say_error("%s: unknown filetype", l->filename);
-		return -1;
+		tnt_raise(XlogError, "%s: unknown filetype", l->filename);
 	}
 
 	if (strcmp(v12, version) != 0) {
-		say_error("%s: unsupported file format version", l->filename);
-		return -1;
+		tnt_raise(XlogError, "%s: unsupported file format version",
+			  l->filename);
 	}
 	for (;;) {
 		if (fgets(buf, sizeof(buf), stream) == NULL) {
-			say_error("%s: failed to read log file header",
+			tnt_raise(XlogError,
+				  "%s: failed to read log file header",
 				  l->filename);
-			return -1;
 		}
 		/** Empty line indicates the end of file header. */
 		if (strcmp(buf, "\n") == 0)
@@ -760,8 +782,7 @@ xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid)
 		char *key = buf;
 		char *val = strchr(buf, ':');
 		if (val == NULL) {
-			say_error("%s: invalid meta", l->filename);
-			return -1;
+			tnt_raise(XlogError, "%s: invalid meta", l->filename);
 		}
 		*val++ = 0;
 		while (isspace(*val))
@@ -770,86 +791,82 @@ xlog_read_meta(struct xlog *l, const tt_uuid *server_uuid)
 		if (strcmp(key, SERVER_UUID_KEY) == 0) {
 			if ((end - val) != UUID_STR_LEN ||
 			    tt_uuid_from_string(val, &l->server_uuid) != 0) {
-				say_error("%s: can't parse node uuid",
+				tnt_raise(XlogError, "%s: can't parse node UUID",
 					  l->filename);
-				return -1;
 			}
 		} else if (strcmp(key, VCLOCK_KEY) == 0){
 			size_t offset = vclock_from_string(&l->vclock, val);
 			if (offset != 0) {
-				say_error("%s: invalid vclock at offset %zd",
+				tnt_raise(XlogError, "%s: invalid vclock at offset %zd",
 					  l->filename, offset);
-				return -1;
 			}
 		} else {
 			/* Skip unknown key */
 		}
 	}
 
-	if (server_uuid != NULL && !tt_uuid_is_nil(server_uuid) &&
-	    !tt_uuid_is_equal(server_uuid, &l->server_uuid)) {
-		say_error("%s: invalid server uuid", l->filename);
-		if (l->dir->panic_if_error)
-			return -1;
+	if (!tt_uuid_is_nil(dir->server_uuid) &&
+	    !tt_uuid_is_equal(dir->server_uuid, &l->server_uuid)) {
+		tnt_raise(XlogError, "%s: invalid server UUID",
+			  l->filename);
+	}
+	/*
+	 * Check the match between log file name and contents:
+	 * the sum of vector clock coordinates must be the same
+	 * as the name of the file.
+	 */
+	int64_t signature_check = vclock_signature(&l->vclock);
+	if (signature_check != signature) {
+		tnt_raise(XlogError, "%s: signature check failed",
+			  l->filename);
 	}
-	return 0;
 }
 
 struct xlog *
-xlog_open_stream(struct xdir *dir, const char *filename,
-		 const tt_uuid *server_uuid, enum log_suffix suffix,
-		 FILE *file)
+xlog_open_stream(struct xdir *dir, int64_t signature, enum log_suffix suffix,
+		 FILE *file, const char *filename)
 {
-	struct xlog *l = NULL;
-	int save_errno;
 	/*
 	 * Check fopen() result the caller first thing, to
 	 * preserve the errno.
 	 */
-	if (file == NULL) {
-		save_errno = errno;
-		say_syserror("%s: failed to open file", filename);
-		goto error_1;
-	}
-	l = (struct xlog *) calloc(1, sizeof(*l));
-	if (l == NULL) {
-		save_errno = errno;
-		say_syserror("%s: out of memory", filename);
-		goto error_2;
-	}
+	if (file == NULL)
+		tnt_raise(SystemError, "%s: failed to open file", filename);
+
+	struct xlog *l = (struct xlog *) calloc(1, sizeof(*l));
+
+	auto log_guard = make_scoped_guard([=]{
+		fclose(file);
+		free(l);
+	});
+
+	if (l == NULL)
+		tnt_raise(OutOfMemory, sizeof(*l), "malloc", "struct xlog");
+
 	l->f = file;
 	snprintf(l->filename, PATH_MAX, "%s", filename);
 	l->mode = LOG_READ;
 	l->dir = dir;
 	l->is_inprogress = (suffix == INPROGRESS);
 	vclock_create(&l->vclock);
-	if (xlog_read_meta(l, server_uuid) != 0) {
-		save_errno = EINVAL;
-		goto error_3;
-	}
-	return l;
 
-error_3:
-	free(l);
-error_2:
-	fclose(file);
-error_1:
-	errno = save_errno;
-	return NULL;
+	xlog_read_meta(l, signature);
+
+	log_guard.is_active = false;
+	return l;
 }
 
 struct xlog *
-xlog_open(struct xdir *dir, int64_t signature,
-	  const tt_uuid *server_uuid, enum log_suffix suffix)
+xlog_open(struct xdir *dir, int64_t signature, enum log_suffix suffix)
 {
 	const char *filename = format_filename(dir, signature, suffix);
 	FILE *f = fopen(filename, "r");
-	if (suffix == INPROGRESS && f == NULL) {
-		filename = format_filename(dir, signature, NONE);
-		f = fopen(filename, "r");
+	if (f == NULL && suffix == INPROGRESS) {
 		suffix = NONE;
+		filename = format_filename(dir, signature, suffix);
+		f = fopen(filename, "r");
 	}
-	return xlog_open_stream(dir, filename, server_uuid, suffix, f);
+	return xlog_open_stream(dir, signature, suffix, f, filename);
 }
 
 /**
@@ -857,8 +874,7 @@ xlog_open(struct xdir *dir, int64_t signature,
  * and sets errno.
  */
 struct xlog *
-xlog_create(struct xdir *dir, const tt_uuid *server_uuid,
-		      const struct vclock *vclock)
+xlog_create(struct xdir *dir, const struct vclock *vclock)
 {
 	char *filename;
 	FILE *f = NULL;
@@ -866,6 +882,7 @@ xlog_create(struct xdir *dir, const tt_uuid *server_uuid,
 
 	int64_t signt = vclock_signature(vclock);
 	assert(signt >= 0);
+	assert(!tt_uuid_is_nil(dir->server_uuid));
 
 	/*
 	* Check whether a file with this name already exists.
@@ -894,8 +911,9 @@ xlog_create(struct xdir *dir, const tt_uuid *server_uuid,
 	l->mode = LOG_WRITE;
 	l->dir = dir;
 	l->is_inprogress = true;
+	vclock_copy(&l->vclock, vclock);
 	setvbuf(l->f, NULL, _IONBF, 0);
-	if (xlog_write_meta(l, server_uuid, vclock) != 0)
+	if (xlog_write_meta(l) != 0)
 		goto error;
 
 	return l;
@@ -906,6 +924,7 @@ xlog_create(struct xdir *dir, const tt_uuid *server_uuid,
 		fclose(f);
 		unlink(filename); /* try to remove incomplete file */
 	}
+	free(l);
 	errno = save_errno;
 	return NULL;
 }
diff --git a/src/box/xlog.h b/src/box/xlog.h
index 4ded0a2115647ba50641ac8e1f0e59f828a5cd38..df549ed8acd77833d611d8be07026c7c6d9bbc07 100644
--- a/src/box/xlog.h
+++ b/src/box/xlog.h
@@ -33,6 +33,18 @@
 #include "tt_uuid.h"
 #include "vclock.h"
 
+/**
+ * XlogError is raised when there is an error with contents
+ * of the data directory or a log file. A special subclass
+ * of exception is introduced to gracefully skip such errors
+ * in panic_if_error = false mode.
+ */
+struct XlogError: public Exception
+{
+	XlogError(const char *file, unsigned line,
+		  const char *format, ...);
+};
+
 /* {{{ log dir */
 
 /**
@@ -70,6 +82,14 @@ struct xdir {
 	 * O_DIRECT flag, for example.
 	 */
 	char open_wflags[6];
+	/**
+	 * A pointer to this server uuid. If not assigned
+	 * (tt_uuid_is_nil returns true), server id check
+	 * for logs in this directory is not performed.
+	 * Otherwise, any log in this directory must have
+	 * the matching server id.
+	 */
+	const struct tt_uuid *server_uuid;
 	/**
 	 * Text of a marker written to the text file header:
 	 * XLOG (meaning it's a write ahead log) or SNAP (a
@@ -97,8 +117,8 @@ struct xdir {
  * Initialize a log dir.
  */
 void
-xdir_create(struct xdir *dir, const char *dirname,
-	    enum xdir_type type);
+xdir_create(struct xdir *dir, const char *dirname, enum xdir_type type,
+	    const struct tt_uuid *server_uuid);
 
 /**
  * Destroy a log dir object.
@@ -115,6 +135,12 @@ xdir_destroy(struct xdir *dir);
 void
 xdir_scan(struct xdir *dir);
 
+/**
+ * Check that a directory exists and is writable.
+ */
+void
+xdir_check(struct xdir *dir);
+
 /* }}} */
 
 /**
@@ -175,8 +201,6 @@ struct xlog {
  *
  * @param  dir           the log directory to look for a file
  * @param  signature     file name
- * @param  server_uuid   the file must have been created by
- *                       the server with this uuid, check it
  * @param  suffix        if IN_PROGRESS, look also for
  *                       .inprogress  files
  *
@@ -185,8 +209,8 @@ struct xlog {
  * Raises an exception in case of error.
  */
 struct xlog *
-xlog_open(struct xdir *dir, int64_t signature,
-	  const tt_uuid *server_uuid, enum log_suffix suffix);
+xlog_open(struct xdir *dir, int64_t signature, enum log_suffix suffix);
+
 /**
  * Open an xlog from a pre-created stdio stream. The log
  * The log is open for reading.
@@ -199,9 +223,8 @@ xlog_open(struct xdir *dir, int64_t signature,
  */
 
 struct xlog *
-xlog_open_stream(struct xdir *dir, const char *filename,
-			    const tt_uuid *server_uuid, enum log_suffix suffix,
-			    FILE *file);
+xlog_open_stream(struct xdir *dir, int64_t signature,
+		 enum log_suffix suffix, FILE *file, const char *filename);
 
 /**
  * Create a new file and open it in write (append) mode.
@@ -212,11 +235,11 @@ xlog_open_stream(struct xdir *dir, const char *filename,
  * @param vclock        the global state of replication (vector
  *			clock) at the moment the file is created.
  *
- * @return  xlog object. Raises an exception in case of error.
+ * @return  xlog object or NULL in case of error.
  */
 struct xlog *
-xlog_create(struct xdir *dir, const tt_uuid *server_uuid,
-	    const struct vclock *vclock);
+xlog_create(struct xdir *dir, const struct vclock *vclock);
+
 /**
  * Sync a log file. The exact action is defined
  * by xdir flags.
diff --git a/src/coio.cc b/src/coio.cc
index e2903365f1f0cb0c18aa77eda240d63fdf902002..282b3db407718a6764347a79f3a2416fe3e47cf9 100644
--- a/src/coio.cc
+++ b/src/coio.cc
@@ -44,11 +44,8 @@ struct CoioGuard {
 	~CoioGuard() { ev_io_stop(loop(), ev_io); }
 };
 
-static inline void
-fiber_schedule_coio(ev_loop * /* loop */, ev_io *watcher, int event)
-{
-	return fiber_schedule((ev_watcher *) watcher, event);
-}
+typedef void (*ev_io_cb)(ev_loop *, ev_io *, int);
+typedef void (*ev_stat_cb)(ev_loop *, ev_stat *, int);
 
 /** Note: this function does not throw */
 void
@@ -56,13 +53,18 @@ coio_init(struct ev_io *coio)
 {
 	/* Prepare for ev events. */
 	coio->data = fiber();
-	ev_init(coio, fiber_schedule_coio);
+	ev_init(coio, (ev_io_cb) fiber_schedule);
 	coio->fd = -1;
 }
 
 static inline void
 coio_fiber_yield(struct ev_io *coio)
 {
+	/**
+	 * We may create an event in one fiber, but wait for it
+	 * in another. Hence we set the coroutine right before the
+	 * yield.
+	 */
 	coio->data = fiber();
 	fiber_yield();
 #ifdef DEBUG
@@ -607,3 +609,21 @@ coio_service_init(struct coio_service *service, const char *name,
 	service->handler = handler;
 	service->handler_param = handler_param;
 }
+
+void
+coio_stat_init(ev_stat *stat, const char *path)
+{
+	ev_stat_init(stat, (ev_stat_cb) fiber_schedule, path, 0.0);
+}
+
+void
+coio_stat_stat_timeout(ev_stat *stat, ev_tstamp timeout)
+{
+	stat->data = fiber();
+	ev_stat_start(loop(), stat);
+	ev_tstamp start, delay;
+	coio_timeout_init(&start, &delay, timeout);
+	fiber_yield_timeout(delay);
+	ev_stat_stop(loop(), stat);
+	fiber_testcancel();
+}
diff --git a/src/coio.h b/src/coio.h
index 876778a44cc54e321f6ae5e9210ac7acafe993d7..25bf23ede9a8a72877c80187a45f989a5d99d879 100644
--- a/src/coio.h
+++ b/src/coio.h
@@ -149,4 +149,11 @@ coio_service_init(struct coio_service *service, const char *name,
 		  const char *uri,
 		  void (*handler)(va_list ap), void *handler_param);
 
+
+void
+coio_stat_init(ev_stat *stat, const char *path);
+
+void
+coio_stat_stat_timeout(ev_stat *stat, ev_tstamp delay);
+
 #endif /* TARANTOOL_COIO_H_INCLUDED */
diff --git a/src/exception.cc b/src/exception.cc
index 0a19f9af1d92ee31831c816963dbba7c273a8019..ca2080928224a9212adf011d6fbeae1df0889714 100644
--- a/src/exception.cc
+++ b/src/exception.cc
@@ -112,11 +112,20 @@ Exception::Exception(const Exception& e)
 	memcpy(m_errmsg, e.m_errmsg, sizeof(m_errmsg));
 }
 
+/** A quick & dirty version of name demangle for class names */
+static const char *
+demangle(const char *name)
+{
+	char *res = NULL;
+	(void) strtol(name, &res, 10);
+	return res && strlen(res) ? res : name;
+}
+
 void
 Exception::log() const
 {
-	_say(S_ERROR, m_file, m_line, "%s %s",
-	     typeid(*this).name(), m_errmsg);
+	_say(S_ERROR, m_file, m_line, m_errmsg, "%s",
+	     demangle(typeid(*this).name()));
 }
 
 
diff --git a/src/exception.h b/src/exception.h
index 1de31cf161c6aa589f77bacb07619859109b6fe2..c2d68033fbed1679cb2979723ea8eb6a9e77b5b1 100644
--- a/src/exception.h
+++ b/src/exception.h
@@ -113,10 +113,13 @@ class OutOfMemory: public SystemError {
 		    const char *object);
 };
 
-#define tnt_raise(...) tnt_raise0(__VA_ARGS__)
-#define tnt_raise0(class, ...) do {					\
+#define tnt_error(class, ...) ({					\
 	say_debug("%s at %s:%i", #class, __FILE__, __LINE__);		\
-	throw new class(__FILE__, __LINE__, ##__VA_ARGS__);		\
+	new class(__FILE__, __LINE__, ##__VA_ARGS__);			\
+})
+
+#define tnt_raise(...) do {						\
+	throw tnt_error(__VA_ARGS__);					\
 } while (0)
 
 #endif /* TARANTOOL_EXCEPTION_H_INCLUDED */
diff --git a/src/ffisyms.cc b/src/ffisyms.cc
index e0d34d7703811759ade585ed49df800b980dd77f..3e9f0f65b1791ff1db19fbca06b0f99938bb99ce 100644
--- a/src/ffisyms.cc
+++ b/src/ffisyms.cc
@@ -11,6 +11,7 @@
 #include "lua/bsdsocket.h"
 #include "lua/digest.h"
 #include "base64.h"
+#include "random.h"
 #include <lib/salad/guava.h>
 
 /*
@@ -52,5 +53,6 @@ void *ffi_symbols[] = {
 	(void *) base64_encode,
 	(void *) base64_bufsize,
 	(void *) SHA1internal,
-	(void *) guava
+	(void *) guava,
+	(void *) random_bytes,
 };
diff --git a/src/fiber.cc b/src/fiber.cc
index b6f7109b531cd75253ea50c260e876019934b3f2..7754cedc49eedec7feb842d366486a7860d826dc 100644
--- a/src/fiber.cc
+++ b/src/fiber.cc
@@ -273,22 +273,19 @@ fiber_sleep(ev_tstamp delay)
 	fiber_testcancel();
 }
 
+typedef void (*ev_child_cb)(ev_loop *, ev_child *, int);
+
 /** Wait for a forked child to complete.
  * @note: this is a cancellation point (@sa fiber_testcancel()).
  * @return process return status
 */
-void
-fiber_schedule_child(ev_loop * /* loop */, ev_child *watcher, int event)
-{
-	return fiber_schedule((ev_watcher *) watcher, event);
-}
 
 int
 wait_for_child(pid_t pid)
 {
 	assert(cord() == &main_cord);
 	ev_child cw;
-	ev_init(&cw, fiber_schedule_child);
+	ev_init(&cw, (ev_child_cb) fiber_schedule);
 	ev_child_set(&cw, pid, 0);
 	cw.data = fiber();
 	ev_child_start(loop(), &cw);
@@ -300,7 +297,7 @@ wait_for_child(pid_t pid)
 }
 
 void
-fiber_schedule(ev_watcher *watcher, int event __attribute__((unused)))
+fiber_schedule(ev_loop * /* loop */, ev_watcher *watcher, int /* revents */)
 {
 	assert(fiber() == &cord()->sched);
 	fiber_call((struct fiber *) watcher->data);
diff --git a/src/fiber.h b/src/fiber.h
index 3f72521aef10fafcc2ffb71c61301faed0bf69c5..43bd981663fe8043003aa67c92ea10a6b8f77b62 100644
--- a/src/fiber.h
+++ b/src/fiber.h
@@ -261,8 +261,9 @@ void fiber_testcancel(void);
  */
 bool fiber_setcancellable(bool enable);
 void fiber_sleep(ev_tstamp s);
-struct tbuf;
-void fiber_schedule(ev_watcher *watcher, int event __attribute__((unused)));
+
+void
+fiber_schedule(ev_loop * /* loop */, ev_watcher *watcher, int revents);
 
 /**
  * \brief Associate \a value with \a key in fiber local storage
@@ -277,6 +278,9 @@ fiber_set_key(struct fiber *fiber, enum fiber_key key, void *value)
 	fiber->fls[key] = value;
 }
 
+bool
+fiber_is_cancelled();
+
 /**
  * \brief Retrieve value by \a key from fiber local storage
  * \param fiber fiber
diff --git a/src/fio.c b/src/fio.c
index 7f1024bd8e1a1cf9b269e95894d654fc422a30e1..2edae2f054e3663ab70f8e52dbea0ee5406e6efe 100644
--- a/src/fio.c
+++ b/src/fio.c
@@ -40,7 +40,7 @@
 
 #include <say.h>
 
-static const char *
+const char *
 fio_filename(int fd)
 {
 #ifdef TARGET_OS_LINUX
diff --git a/src/fio.h b/src/fio.h
index 957c8273a5097202f6044b84dca382341251dfbb..db7a59362c0118aca3b8cc3bfd6999a7ba662548 100644
--- a/src/fio.h
+++ b/src/fio.h
@@ -41,6 +41,9 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+const char *
+fio_filename(int fd);
+
 struct iovec;
 /**
  * Read up to N bytes from file into the buffer,
diff --git a/src/lua/digest.lua b/src/lua/digest.lua
index 724bfb24d7825ae9c86009c879495f59f9c4f75d..96a86e3d386e9b35223bd779aced21b522be3b91 100644
--- a/src/lua/digest.lua
+++ b/src/lua/digest.lua
@@ -30,6 +30,9 @@ ffi.cdef[[
    int base64_bufsize(int binsize);
    int base64_decode(const char *in_base64, int in_len, char *out_bin, int out_len);
    int base64_encode(const char *in_bin, int in_len, char *out_base64, int out_len);
+
+   /* random */
+   void random_bytes(char *, size_t);
 ]]
 
 local ssl
@@ -135,6 +138,15 @@ local m = {
     guava = function(state, buckets)
        return ffi.C.guava(state, buckets)
     end,
+
+    urandom = function(n)
+        if n == nil then
+            error('Usage: digest.urandom(len)')
+        end
+        local buf = ffi.new('char[?]', n)
+        ffi.C.random_bytes(buf, n)
+        return ffi.string(buf, n)
+    end
 }
 
 if ssl ~= nil then
diff --git a/src/lua/log.lua b/src/lua/log.lua
index 8b37eab52196a980c223529b21f05e1acab8e19b..fdad8836637dea08104a4d4c3e40e055eff822a1 100644
--- a/src/lua/log.lua
+++ b/src/lua/log.lua
@@ -20,16 +20,7 @@ ffi.cdef[[
 ]]
 
 local function say(level, fmt, ...)
-    local str
-   
-    args = { ... }
-    if not pcall(function() str = string.format(fmt, unpack(args)) end) then
-        str = fmt .. ' ['
-        for i = 1, select('#', ...) do
-            str = str .. select(i, ...) .. ', '
-        end
-        str = str .. ']'
-    end
+    local str = string.format(fmt, ...)
     local frame = debug.getinfo(3, "Sl")
     local line = 0
     local file = 'eval'
diff --git a/test/app/trigger_atexit.result b/test/app/trigger_atexit.result
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..19f86f493ab110b8dc8279a024880e44203968d8 100644
--- a/test/app/trigger_atexit.result
+++ b/test/app/trigger_atexit.result
@@ -0,0 +1 @@
+done
diff --git a/test/app/trigger_atexit.test.lua b/test/app/trigger_atexit.test.lua
index 7ac54bd5e961396560ef15d4637ce6c8110205d6..a6044ec3975c6765df4d8f7d031fc018fa950dae 100755
--- a/test/app/trigger_atexit.test.lua
+++ b/test/app/trigger_atexit.test.lua
@@ -27,10 +27,11 @@ box.space.abc:create_index('pk', { type = 'tree' })
 box.space.abc:on_replace(test_replace)
 
 
-cleanup_list = fio.glob(fio.pathjoin(tempdir), '*')
+cleanup_list = fio.glob(fio.pathjoin(tempdir, '*'))
 for _, file in pairs(cleanup_list) do
     fio.unlink(file)
 end
 fio.rmdir(tempdir)
+print("done")
 os.exit(0)
 
diff --git a/test/box/access.result b/test/box/access.result
index bef0e0c8cde03382829e5c70fca542a2212db376..6ba4f59a0b572eceb6fc23d006753c7e574a0b0f 100644
--- a/test/box/access.result
+++ b/test/box/access.result
@@ -529,3 +529,24 @@ box.schema.role.exists('public')
 ---
 - true
 ...
+-- test if_exists/if_not_exists in grant/revoke
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+---
+- error: User 'guest' already has read,write,execute access on universe 'nil'
+...
+box.schema.user.grant('guest', 'read,write,execute', 'universe', '', { if_not_exists = true })
+---
+...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+---
+- error: User 'guest' does not have read,write,execute access on universe 'nil'
+...
+box.schema.user.revoke('guest', 'read,write,execute', 'universe', '', { if_exists = true })
+---
+...
diff --git a/test/box/access.test.lua b/test/box/access.test.lua
index 9c39abe7f58151b98afad79e610dde3332b32b55..41fe522ed98f820a6d133c7ed708cb8fc7527935 100644
--- a/test/box/access.test.lua
+++ b/test/box/access.test.lua
@@ -212,3 +212,10 @@ box.schema.func.exists('box.schema.user.info')
 -- gh-665: user.exists() should nto be true for roles
 box.schema.user.exists('public')
 box.schema.role.exists('public')
+-- test if_exists/if_not_exists in grant/revoke
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+box.schema.user.grant('guest', 'read,write,execute', 'universe')
+box.schema.user.grant('guest', 'read,write,execute', 'universe', '', { if_not_exists = true })
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+box.schema.user.revoke('guest', 'read,write,execute', 'universe')
+box.schema.user.revoke('guest', 'read,write,execute', 'universe', '', { if_exists = true })
diff --git a/test/box/bsdsocket.result b/test/box/bsdsocket.result
index 5ef12fd2ebb1399b96800390a8c81b2d9b029a4f..691d63bd6030755bf6f0b26ac5171952aa40fb2d 100644
--- a/test/box/bsdsocket.result
+++ b/test/box/bsdsocket.result
@@ -168,11 +168,11 @@ pong = s:sysread()
 ...
 string.len(pong)
 ---
-- 22
+- 23
 ...
 msgpack.decode(pong)
 ---
-- 17
+- 18
 - 6
 ...
 msgpack.decode(pong, 6)
diff --git a/test/box/digest.result b/test/box/digest.result
index f290ccc0823c6ee878a1dc2859630557c0922cc0..a087ba03bc84b072e1645cd7ea5c2c2f7676fc69 100644
--- a/test/box/digest.result
+++ b/test/box/digest.result
@@ -193,19 +193,19 @@ digest.base64_decode(b) == s
 ...
 digest.base64_decode(nil)
 ---
-- error: 'builtin/digest.lua:88: Usage: digest.base64_decode(string)'
+- error: 'builtin/digest.lua:91: Usage: digest.base64_decode(string)'
 ...
 digest.base64_encode(nil)
 ---
-- error: 'builtin/digest.lua:77: Usage: digest.base64_encode(string)'
+- error: 'builtin/digest.lua:80: Usage: digest.base64_encode(string)'
 ...
 digest.base64_encode(123)
 ---
-- error: 'builtin/digest.lua:77: Usage: digest.base64_encode(string)'
+- error: 'builtin/digest.lua:80: Usage: digest.base64_encode(string)'
 ...
 digest.base64_decode(123)
 ---
-- error: 'builtin/digest.lua:88: Usage: digest.base64_decode(string)'
+- error: 'builtin/digest.lua:91: Usage: digest.base64_decode(string)'
 ...
 digest.guava('hello', 0)
 ---
@@ -227,6 +227,22 @@ digest.guava(1673758223894951030, 11)
 ---
 - 7
 ...
+digest.urandom()
+---
+- error: 'builtin/digest.lua:144: Usage: digest.urandom(len)'
+...
+#digest.urandom(0)
+---
+- 0
+...
+#digest.urandom(1)
+---
+- 1
+...
+#digest.urandom(16)
+---
+- 16
+...
 digest = nil
 ---
 ...
diff --git a/test/box/digest.test.lua b/test/box/digest.test.lua
index a4583eece4f2fb356fd3df99330086a35c11c338..5c1b8b00f22f1000802b36c3fa8eeeda11cb0959 100644
--- a/test/box/digest.test.lua
+++ b/test/box/digest.test.lua
@@ -65,4 +65,9 @@ digest.guava(10863919174838991, 11)
 digest.guava(2016238256797177309, 11)
 digest.guava(1673758223894951030, 11)
 
+digest.urandom()
+#digest.urandom(0)
+#digest.urandom(1)
+#digest.urandom(16)
+
 digest = nil
diff --git a/test/box/misc.result b/test/box/misc.result
index 4a6b2751e572cbe622e672e66348f3227db44573..fd4be8cb229ab86d67ac48a1041754790c504888 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -88,7 +88,6 @@ t;
   - SELECT
   - REPLACE
   - INSERT
-  - AUTH
   - CALL
   - UPDATE
   - total
diff --git a/test/lib/unittest_server.py b/test/lib/unittest_server.py
index a700aa2da1ff827d857557a3d8d763f832ae47f1..708a34304b6479ba04b96bd9eb143bb57d4bc567 100644
--- a/test/lib/unittest_server.py
+++ b/test/lib/unittest_server.py
@@ -4,7 +4,7 @@ import sys
 import glob
 import traceback
 import subprocess
-from subprocess import Popen, PIPE
+from subprocess import Popen, PIPE, STDOUT
 
 from lib.server import Server
 from lib.tarantool_server import Test
@@ -12,7 +12,7 @@ from lib.tarantool_server import Test
 class UnitTest(Test):
     def execute(self, server):
         execs = [os.path.join(server.builddir, "test", self.name)]
-        proc = Popen(execs, stdout=PIPE)
+        proc = Popen(execs, stdout=PIPE, stderr=STDOUT)
         sys.stdout.write(proc.communicate()[0])
 
 class UnittestServer(Server):
diff --git a/test/replication/cluster.result b/test/replication/cluster.result
index 520f03d4ea42fee624cab79f1a88296757456c82..230cd06a13e0bcc6a4a72297c3d7a0ca53354182 100644
--- a/test/replication/cluster.result
+++ b/test/replication/cluster.result
@@ -9,9 +9,9 @@ box.info.server.uuid
 ---
 - 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae
 ...
-check log line for 'server uuid changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae'
+check log line for 'server UUID changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae'
 
-'server uuid changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' exists in server log
+'server UUID changed to 8c7ff474-65f9-4abe-81a4-a3e1019bb1ae' exists in server log
 
 box.info.server.uuid
 ---
diff --git a/test/replication/cluster.test.py b/test/replication/cluster.test.py
index 11ef2c1a4c7ada947c0c4c148151b762ff3ee775..6abd7af917b542b2c34fa5140183d4b939577204 100644
--- a/test/replication/cluster.test.py
+++ b/test/replication/cluster.test.py
@@ -18,7 +18,7 @@ server.admin("box.info.server.uuid")
 server.stop()
 server.start()
 
-line = "server uuid changed to " + new_uuid
+line = "server UUID changed to " + new_uuid
 print "check log line for '%s'" % line
 print
 if server.logfile_pos.seek_once(line) >= 0:
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index 1091ee26dd8b55c979b9d6c98b7e40eec19172a9..c15d0861ab701aebcd96d20a6437834b30463572 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -6,9 +6,9 @@ include_directories(${PROJECT_SOURCE_DIR}/src)
 include_directories(${PROJECT_BINARY_DIR}/src)
 include_directories(${PROJECT_SOURCE_DIR}/src/lib)
 include_directories(${CMAKE_SOURCE_DIR}/third_party)
-add_executable(rlist.test rlist.c test.c ${CMAKE_SOURCE_DIR}/src/lib/salad/rlist.c)
-add_executable(uri.test uri.c test.c ${CMAKE_SOURCE_DIR}/src/uri.c)
-add_executable(fiob.test test.c fiob.c ${CMAKE_SOURCE_DIR}/src/fiob.c)
+add_executable(rlist.test rlist.c unit.c ${CMAKE_SOURCE_DIR}/src/lib/salad/rlist.c)
+add_executable(uri.test uri.c unit.c ${CMAKE_SOURCE_DIR}/src/uri.c)
+add_executable(fiob.test unit.c fiob.c ${CMAKE_SOURCE_DIR}/src/fiob.c)
 add_executable(queue.test queue.c)
 add_executable(mhash.test mhash.c)
 add_executable(mhash_bytemap.test mhash_bytemap.c)
@@ -48,17 +48,27 @@ add_executable(rtree_itr.test rtree_itr.cc ${CMAKE_SOURCE_DIR}/src/lib/salad/rtr
 target_link_libraries(rtree_itr.test)
 add_executable(matras.test matras.cc)
 target_link_libraries(matras.test small)
-add_executable(vclock.test vclock.cc test.c
+add_executable(vclock.test vclock.cc unit.c
     ${CMAKE_SOURCE_DIR}/src/box/vclock.c
     ${CMAKE_SOURCE_DIR}/src/box/errcode.c
     ${CMAKE_SOURCE_DIR}/src/box/error.cc)
 target_link_libraries(vclock.test core small)
-add_executable(quota.test quota.cc test.c)
+add_executable(quota.test quota.cc unit.c)
 target_link_libraries(quota.test pthread)
 
 add_executable(fiber.test fiber.cc)
 target_link_libraries(fiber.test core)
 
+add_executable(coio.test coio.cc unit.c
+        ${CMAKE_SOURCE_DIR}/src/sio.cc
+        ${CMAKE_SOURCE_DIR}/src/evio.cc
+        ${CMAKE_SOURCE_DIR}/src/coio.cc
+        ${CMAKE_SOURCE_DIR}/src/coeio.cc
+        ${CMAKE_SOURCE_DIR}/src/uri.c
+        ${CMAKE_SOURCE_DIR}/src/fio.c
+        ${CMAKE_SOURCE_DIR}/src/iobuf.cc)
+target_link_libraries(coio.test core eio bit)
+
 set(MSGPUCK_DIR ${PROJECT_SOURCE_DIR}/src/lib/msgpuck/)
 add_executable(msgpack.test
     ${MSGPUCK_DIR}/test/msgpuck.c
diff --git a/test/unit/coio.cc b/test/unit/coio.cc
new file mode 100644
index 0000000000000000000000000000000000000000..65c28296094ae22e66c009007ffd20e2989158d5
--- /dev/null
+++ b/test/unit/coio.cc
@@ -0,0 +1,72 @@
+#include "memory.h"
+#include "fiber.h"
+#include "coio.h"
+#include "fio.h"
+#include "unit.h"
+#include "unit.h"
+
+void
+touch_f(va_list ap)
+{
+	FILE *f = va_arg(ap, FILE *);
+	const char *c = "c";
+	while (true) {
+		int rc = fwrite(c, strlen(c), 1, f);
+		fail_unless(rc == 1);
+		fflush(f);
+		fiber_sleep(0.01);
+	}
+}
+
+static void
+stat_notify_test(FILE *f, const char *filename)
+{
+	header();
+
+	struct fiber *touch = fiber_new("touch", touch_f);
+	fiber_call(touch, f);
+	ev_stat stat;
+	note("filename: %s", filename);
+	coio_stat_init(&stat, filename);
+	coio_stat_stat_timeout(&stat, TIMEOUT_INFINITY);
+	fail_unless(stat.prev.st_size < stat.attr.st_size);
+	fiber_cancel(touch);
+
+	footer();
+}
+
+static void
+stat_timeout_test(const char *filename)
+{
+	header();
+
+	ev_stat stat;
+	coio_stat_init(&stat, filename);
+	coio_stat_stat_timeout(&stat, 0.01);
+
+	footer();
+}
+
+void
+main_f(va_list ap)
+{
+	const char *filename = "1.out";
+	FILE *f = fopen(filename, "w+");
+	stat_timeout_test(filename);
+	stat_notify_test(f, filename);
+	fclose(f);
+	remove(filename);
+	ev_break(loop(), EVBREAK_ALL);
+}
+
+int main()
+{
+	memory_init();
+	fiber_init();
+	struct fiber *test = fiber_new("coio_stat", main_f);
+	fiber_wakeup(test);
+	ev_run(loop(), 0);
+	fiber_free();
+	memory_free();
+	return 0;
+}
diff --git a/test/unit/coio.result b/test/unit/coio.result
new file mode 100644
index 0000000000000000000000000000000000000000..8874127d95e5fb5df992a8341cdc3e52965c1ded
--- /dev/null
+++ b/test/unit/coio.result
@@ -0,0 +1,8 @@
+(null): fiber `touch' has been cancelled
+(null): fiber `touch': exiting
+	*** stat_timeout_test ***
+	*** stat_timeout_test: done ***
+ 	*** stat_notify_test ***
+# filename: 1.out
+	*** stat_notify_test: done ***
+ 
\ No newline at end of file
diff --git a/test/unit/fiob.c b/test/unit/fiob.c
index 02dce5f7721297bc19f1953342a317f802641ddc..9325b752e03429379a9822756d7f2a6a0def4685 100644
--- a/test/unit/fiob.c
+++ b/test/unit/fiob.c
@@ -13,7 +13,7 @@
 #include <string.h>
 #include <errno.h>
 
-#include "test.h"
+#include "unit.h"
 #include <fiob.h>
 #include <say.h>
 #include <stdarg.h>
diff --git a/test/unit/quota.cc b/test/unit/quota.cc
index 79b49e4eec015210eb0dc50c688e21077ba3e9b2..f1cf7ae3de4b074bd95e468f8b27af03a7bd5293 100644
--- a/test/unit/quota.cc
+++ b/test/unit/quota.cc
@@ -3,7 +3,7 @@
 #include <pthread.h>
 #include <sched.h>
 
-#include "test.h"
+#include "unit.h"
 
 struct quota quota;
 
diff --git a/test/unit/rlist.c b/test/unit/rlist.c
index fb0ed37eabee1532511535073dff9cccc9bd41ed..48d904e8e52e1982b357d30763cc9594638d7b07 100644
--- a/test/unit/rlist.c
+++ b/test/unit/rlist.c
@@ -1,7 +1,7 @@
 #include "salad/rlist.h"
 #include <stdio.h>
 #include <stdarg.h>
-#include "test.h"
+#include "unit.h"
 
 
 #define PLAN		87
diff --git a/test/unit/test.h b/test/unit/test.h
deleted file mode 100644
index 84478f97e72a1f10884832a032a22782aca25e1f..0000000000000000000000000000000000000000
--- a/test/unit/test.h
+++ /dev/null
@@ -1,102 +0,0 @@
-#ifndef TARANTOOL_TEST_H_INCLUDED
-#define TARANTOOL_TEST_H_INCLUDED
-
-#include <stdio.h>
-
-#if defined(__cplusplus)
-extern "C" {
-#endif /* defined(__cplusplus) */
-
-/**
-@brief example
-
-@code
-	#include "test.h"
-
-	int main(void) {
-		plan(3);		// count of test You planned to check
-		ok(1, "Test name 1");
-		is(4, 2 * 2, "2 * 2 == 4");
-		isnt(5, 2 * 2, "2 * 2 != 5);
-		return check_plan();	// print resume
-	}
-@endcode
-
-
-*/
-
-/* private function, use ok(...) instead */
-int _ok(int condition, const char *fmt, ...);
-
-/* private function, use note(...) or diag(...) instead */
-void _space(FILE *stream);
-
-#define msg(stream, ...) ({ _space(stream); fprintf(stream, "# ");            \
-	fprintf(stream, __VA_ARGS__); fprintf(stream, "\n"); })
-
-#define note(...) msg(stdout, __VA_ARGS__)
-#define diag(...) msg(stderr, __VA_ARGS__)
-
-/**
-@brief set and print plan
-@param count
-Before anything else, you need a testing plan.  This basically declares
-how many tests your program is going to run to protect against premature
-failure.
-*/
-void plan(int count);
-
-/**
-@brief check if plan is reached and print report
-*/
-int check_plan(void);
-
-#define ok(condition, fmt, args...)	{		\
-	int res = _ok(condition, fmt, ##args);		\
-	if (!res) {					\
-		_space(stderr);			\
-		fprintf(stderr, "#   Failed test '");	\
-		fprintf(stderr, fmt, ##args);		\
-		fprintf(stderr, "'\n");			\
-		_space(stderr);			\
-		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
-	}						\
-	res = res;					\
-}
-
-#define is(a, b, fmt, args...)	{			\
-	int res = _ok((a) == (b), fmt, ##args);	\
-	if (!res) {					\
-		_space(stderr);			\
-		fprintf(stderr, "#   Failed test '");	\
-		fprintf(stderr, fmt, ##args);		\
-		fprintf(stderr, "'\n");			\
-		_space(stderr);			\
-		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
-	}						\
-	res = res;					\
-}
-
-#define isnt(a, b, fmt, args...) {			\
-	int res = _ok((a) != (b), fmt, ##args);	\
-	if (!res) {					\
-		_space(stderr);			\
-		fprintf(stderr, "#   Failed test '");	\
-		fprintf(stderr, fmt, ##args);		\
-		fprintf(stderr, "'\n");			\
-		_space(stderr);			\
-		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
-	}						\
-	res = res;					\
-}
-
-#define fail(fmt, args...)		\
-	ok(0, fmt, ##args)
-
-
-#if defined(__cplusplus)
-}
-#endif /* defined(__cplusplus) */
-
-#endif /* TARANTOOL_TEST_H_INCLUDED */
-
diff --git a/test/unit/test.c b/test/unit/unit.c
similarity index 98%
rename from test/unit/test.c
rename to test/unit/unit.c
index adab399153acf10d31ce771ebc4f7d7fb68e0299..86305280a5b99559f30bf8784097b24798bf3989 100644
--- a/test/unit/test.c
+++ b/test/unit/unit.c
@@ -1,4 +1,4 @@
-#include "test.h"
+#include "unit.h"
 
 #include <stdio.h>
 #include <stdarg.h>
diff --git a/test/unit/unit.h b/test/unit/unit.h
index 4ab24ea935bb68e71501c21a0e111b734dd9757c..76d8602f7bbe34cc5aa73db9b3e9e15cafd3b8f8 100644
--- a/test/unit/unit.h
+++ b/test/unit/unit.h
@@ -35,7 +35,7 @@
 #define header() printf("\t*** %s ***\n", __func__)
 #define footer() printf("\t*** %s: done ***\n ", __func__)
 
-#define fail(expr, result) do {						\
+#define fail(expr, result) do {					\
 	fprintf(stderr, "Test failed: %s is %s at %s:%d, in function '%s'\n",\
 		expr, result, __FILE__, __LINE__, __func__);		\
 	exit(-1);							\
@@ -44,4 +44,95 @@
 #define fail_if(expr) if (expr) fail(#expr, "true")
 #define fail_unless(expr) if (!(expr)) fail(#expr, "false")
 
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/**
+@brief example
+
+@code
+	#include "unit.h"
+
+	int main(void) {
+		plan(3);		// count of test You planned to check
+		ok(1, "Test name 1");
+		is(4, 2 * 2, "2 * 2 == 4");
+		isnt(5, 2 * 2, "2 * 2 != 5);
+		return check_plan();	// print resume
+	}
+@endcode
+
+
+*/
+
+/* private function, use ok(...) instead */
+int _ok(int condition, const char *fmt, ...);
+
+/* private function, use note(...) or diag(...) instead */
+void _space(FILE *stream);
+
+#define msg(stream, ...) ({ _space(stream); fprintf(stream, "# ");            \
+	fprintf(stream, __VA_ARGS__); fprintf(stream, "\n"); })
+
+#define note(...) msg(stdout, __VA_ARGS__)
+#define diag(...) msg(stderr, __VA_ARGS__)
+
+/**
+@brief set and print plan
+@param count
+Before anything else, you need a testing plan.  This basically declares
+how many tests your program is going to run to protect against premature
+failure.
+*/
+void plan(int count);
+
+/**
+@brief check if plan is reached and print report
+*/
+int check_plan(void);
+
+#define ok(condition, fmt, args...)	{		\
+	int res = _ok(condition, fmt, ##args);		\
+	if (!res) {					\
+		_space(stderr);			\
+		fprintf(stderr, "#   Failed test '");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "'\n");			\
+		_space(stderr);			\
+		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
+	}						\
+	res = res;					\
+}
+
+#define is(a, b, fmt, args...)	{			\
+	int res = _ok((a) == (b), fmt, ##args);	\
+	if (!res) {					\
+		_space(stderr);			\
+		fprintf(stderr, "#   Failed test '");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "'\n");			\
+		_space(stderr);			\
+		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
+	}						\
+	res = res;					\
+}
+
+#define isnt(a, b, fmt, args...) {			\
+	int res = _ok((a) != (b), fmt, ##args);	\
+	if (!res) {					\
+		_space(stderr);			\
+		fprintf(stderr, "#   Failed test '");	\
+		fprintf(stderr, fmt, ##args);		\
+		fprintf(stderr, "'\n");			\
+		_space(stderr);			\
+		fprintf(stderr, "#   in %s at line %d\n", __FILE__, __LINE__); \
+	}						\
+	res = res;					\
+}
+
+#if defined(__cplusplus)
+}
+#endif /* defined(__cplusplus) */
+
 #endif /* INCLUDES_TARANTOOL_TEST_UNIT_H */
diff --git a/test/unit/uri.c b/test/unit/uri.c
index b978a12233d7fb5dd181878f468160aec878dd49..33e7014caf2834c4f0376914dab7d8292c51363c 100644
--- a/test/unit/uri.c
+++ b/test/unit/uri.c
@@ -1,4 +1,4 @@
-#include "test.h"
+#include "unit.h"
 #include <uri.h>
 #include <string.h>
 
diff --git a/test/unit/vclock.cc b/test/unit/vclock.cc
index 79cd124aab9ab4011469af6f07661a5704113152..172d2637481f0912643acfed1a0314315ebfe5e1 100644
--- a/test/unit/vclock.cc
+++ b/test/unit/vclock.cc
@@ -27,15 +27,14 @@
  * SUCH DAMAGE.
  */
 extern "C" {
-#include "test.h"
+#include "unit.h"
+#include "unit.h"
 } /* extern "C" */
 
 #include <stdarg.h>
 
 #include "box/vclock.h"
 
-#define header() note("*** %s ***", __func__)
-#define footer() note("*** %s: done ***", __func__)
 #define str2(x) #x
 #define str(x) str2(x)
 #define arg(...) __VA_ARGS__
diff --git a/test/unit/vclock.result b/test/unit/vclock.result
index 902b97ffc77d335ff20c75259aedf8fa01e3ce4c..334dd7ee9db6f8a6e003bfd45d05d019016ee81e 100644
--- a/test/unit/vclock.result
+++ b/test/unit/vclock.result
@@ -1,6 +1,6 @@
 1..5
     1..40
-    # *** test_compare ***
+	*** test_compare ***
     ok 1 - compare (), () => 0
     ok 2 - compare (), () => 0
     ok 3 - compare (), (10) => -1
@@ -41,10 +41,10 @@
     ok 38 - compare (10, 10, 10, 1, 2, 3), (10, 10, 10) => 1
     ok 39 - compare (0, 0, 0), (10, 0, 0, 0, 0) => -1
     ok 40 - compare (10, 0, 0, 0, 0), (0, 0, 0) => 1
-    # *** test_compare: done ***
-ok 1 - subtests
+	*** test_compare: done ***
+ ok 1 - subtests
     1..36
-    # *** test_isearch ***
+	*** test_isearch ***
     ok 1 - query #1
     ok 2 - query #2
     ok 3 - query #3
@@ -81,10 +81,10 @@ ok 1 - subtests
     ok 34 - query #34
     ok 35 - query #35
     ok 36 - query #36
-    # *** test_isearch: done ***
-ok 2 - subtests
+	*** test_isearch: done ***
+ ok 2 - subtests
     1..8
-    # *** test_tostring ***
+	*** test_tostring ***
     ok 1 - tostring () => {}
     ok 2 - tostring (-1, -1, -1) => {}
     ok 3 - tostring (1) => {0: 1}
@@ -93,10 +93,10 @@ ok 2 - subtests
     ok 6 - tostring (10, -1, 15, -1, 20) => {0: 10, 2: 15, 4: 20}
     ok 7 - tostring (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15) => {0: 0, 1: 1, 2: 2, 3: 3, 4: 4, 5: 5, 6: 6, 7: 7, 8: 8, 9: 9, 10: 10, 11: 11, 12: 12, 13: 13, 14: 14, 15: 15}
     ok 8 - tostring (9223372036854775000, 9223372036854775001, 9223372036854775002, 9223372036854775003, 9223372036854775004, 9223372036854775005, 9223372036854775006, 9223372036854775007, 9223372036854775008, 9223372036854775009, 9223372036854775010, 9223372036854775011, 9223372036854775012, 9223372036854775013, 9223372036854775014, 9223372036854775015) => {0: 9223372036854775000, 1: 9223372036854775001, 2: 9223372036854775002, 3: 9223372036854775003, 4: 9223372036854775004, 5: 9223372036854775005, 6: 9223372036854775006, 7: 9223372036854775007, 8: 9223372036854775008, 9: 9223372036854775009, 10: 9223372036854775010, 11: 9223372036854775011, 12: 9223372036854775012, 13: 9223372036854775013, 14: 9223372036854775014, 15: 9223372036854775015}
-    # *** test_tostring: done ***
-ok 3 - subtests
+	*** test_tostring: done ***
+ ok 3 - subtests
     1..12
-    # *** test_fromstring ***
+	*** test_fromstring ***
     ok 1 - fromstring {} => ()
     ok 2 - fromstring  	 	 { 	 	 } 	 	  => ()
     ok 3 - fromstring {0: 10} => (10)
@@ -109,10 +109,10 @@ ok 3 - subtests
     ok 10 - fromstring {0: 4294967295} => (4294967295)
     ok 11 - fromstring {0: 4294967296} => (4294967296)
     ok 12 - fromstring {0: 9223372036854775807} => (9223372036854775807)
-    # *** test_fromstring: done ***
-ok 4 - subtests
+	*** test_fromstring: done ***
+ ok 4 - subtests
     1..32
-    # *** test_fromstring_invalid ***
+	*** test_fromstring_invalid ***
     ok 1 - fromstring "" => 1
     ok 2 - fromstring " " => 2
     ok 3 - fromstring "	 	 	 " => 7
@@ -145,5 +145,5 @@ ok 4 - subtests
     ok 30 - fromstring "{1:340282366920938463463374607431768211456}" => 43
     ok 31 - fromstring "{1:10, 1:20}" => 12
     ok 32 - fromstring "{1:20, 1:10}" => 12
-    # *** test_fromstring_invalid: done ***
-ok 5 - subtests
+	*** test_fromstring_invalid: done ***
+ ok 5 - subtests