From 1d1b8953ffc18d50692f8a46e7b375356174f46b Mon Sep 17 00:00:00 2001
From: Konstantin Osipov <kostja@tarantool.org>
Date: Sun, 13 Jul 2014 18:15:24 +0400
Subject: [PATCH] A pre-requisite patch for multi-statement transactions.

Refactor statement transaction lifecycle. Avoid nested
statement transactions. Make sure there is no more than 1
statement transaction present in the system at the moment.

Resolve the bugs this revealed:
- in local hot standby we would parasite on the main or
script fiber session to process requests. When an assert
was added that there is no more than 1 transaction active
at a given moment in time, it turned out that transactions
running on behalf of local hot standby were running in
the scope of session of the run_script or sched fibers.

Create and use an own session there.

- refactor session on_connect/on_disconnect trigger
invocation: do not invoke these triggers for just any session,
only for real connects/disconnects (iproto, admin).

- split session creation/destruction and trigger invocation
Conclude that session storage implementation needs to be
done differently.
---
 src/admin.cc                    | 11 +----
 src/box/box.cc                  | 16 +------
 src/box/lua/call.cc             | 77 ++++++++++++++-------------------
 src/box/lua/call.h              |  3 +-
 src/box/recovery.cc             | 11 +++++
 src/box/request.cc              | 64 ++++++++-------------------
 src/box/request.h               |  2 +-
 src/box/txn.cc                  | 61 ++++++++++++++++++--------
 src/box/txn.h                   |  8 +++-
 src/iproto.cc                   | 10 +++--
 src/lib/small/mempool.h         |  4 ++
 src/lua/init.cc                 |  5 ---
 src/session.cc                  | 45 +++++++++++--------
 src/session.h                   | 20 ++++++++-
 test/box/session.storage.result |  2 +-
 15 files changed, 173 insertions(+), 166 deletions(-)

diff --git a/src/admin.cc b/src/admin.cc
index 026bbbc06f..5cf2331128 100644
--- a/src/admin.cc
+++ b/src/admin.cc
@@ -100,21 +100,14 @@ admin_handler(va_list ap)
 	struct iobuf *iobuf = va_arg(ap, struct iobuf *);
 	lua_State *L = lua_newthread(tarantool_L);
 	LuarefGuard coro_guard(tarantool_L);
-	/*
-	 * Admin and iproto connections must have a
-	 * session object, representing the state of
-	 * a remote client: it's used in Lua
-	 * stored procedures.
-	 */
-	SessionGuard sesion_guard(coio.fd, *(uint64_t *) addr);
+	/* Session stores authentication and transaction state.  */
+	SessionGuardWithTriggers sesion_guard(coio.fd, *(uint64_t *) addr);
 
 	auto scoped_guard = make_scoped_guard([&] {
 		evio_close(loop(), &coio);
 		iobuf_delete(iobuf);
 	});
 
-	trigger_run(&session_on_connect, NULL);
-
 	for (;;) {
 		if (admin_dispatch(&coio, iobuf, L) < 0)
 			return;
diff --git a/src/box/box.cc b/src/box/box.cc
index 9c3dcad584..ef048d4a7c 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -73,27 +73,15 @@ struct request_replace_body {
 	uint8_t k_tuple;
 } __attribute__((packed));
 
-void
-port_send_tuple(struct port *port, struct txn *txn)
-{
-	struct tuple *tuple;
-	if ((tuple = txn->new_tuple) || (tuple = txn->old_tuple))
-		port_add_tuple(port, tuple);
-}
-
 static void
 process_rw(struct port *port, struct request *request)
 {
-	struct txn *txn = txn_begin();
 	try {
 		stat_collect(stat_base, request->type, 1);
-		request->execute(request, txn, port);
-		txn_commit(txn);
-		port_send_tuple(port, txn);
+		request->execute(request, port);
 		port_eof(port);
-		txn_finish(txn);
 	} catch (Exception *e) {
-		txn_rollback(txn);
+		txn_rollback();
 		throw;
 	}
 }
diff --git a/src/box/lua/call.cc b/src/box/lua/call.cc
index 2eb52794cf..2566692685 100644
--- a/src/box/lua/call.cc
+++ b/src/box/lua/call.cc
@@ -89,19 +89,15 @@ port_lua_add_tuple(struct port *port, struct tuple *tuple)
 	}
 }
 
-
-static struct port *
-port_lua_create(struct lua_State *L)
+void
+port_lua_create(struct port_lua *port, struct lua_State *L)
 {
 	static struct port_vtab port_lua_vtab = {
 		port_lua_add_tuple,
 		null_port_eof,
 	};
-	struct port_lua *port = (struct port_lua *)
-			region_alloc(&fiber()->gc, sizeof(struct port_lua));
 	port->vtab = &port_lua_vtab;
 	port->L = L;
-	return (struct port *) port;
 }
 
 static void
@@ -219,32 +215,21 @@ lbox_process(lua_State *L)
 		 */
 		return luaL_error(L, "box.process(CALL, ...) is not allowed");
 	}
-	size_t allocated_size = region_used(&fiber()->gc);
+	/* Capture all output into a Lua table. */
 	struct port *port_lua = port_lua_table_create(L);
-	try {
-		struct request request;
-		request_create(&request, op);
-		request_decode(&request, req, sz);
-		box_process(port_lua, &request);
+	struct request request;
+	request_create(&request, op);
+	request_decode(&request, req, sz);
+	box_process(port_lua, &request);
 
-		/*
-		 * This only works as long as port_lua doesn't
-		 * use fiber->cleanup and fiber->gc.
-		 */
-		region_truncate(&fiber()->gc, allocated_size);
-	} catch (Exception *e) {
-		region_truncate(&fiber()->gc, allocated_size);
-		throw;
-	}
 	return 1;
 }
 
-static struct request *
-lbox_request_create(struct lua_State *L, enum iproto_request_type type,
+void
+lbox_request_create(struct request *request,
+		    struct lua_State *L, enum iproto_request_type type,
 		    int key, int tuple)
 {
-	struct request *request = (struct request *)
-		region_alloc(&fiber()->gc, sizeof(struct request));
 	request_create(request, type);
 	request->space_id = lua_tointeger(L, 1);
 	if (key > 0) {
@@ -263,7 +248,6 @@ lbox_request_create(struct lua_State *L, enum iproto_request_type type,
 		if (mp_typeof(*request->tuple) != MP_ARRAY)
 			tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
 	}
-	return request;
 }
 
 static void
@@ -331,10 +315,11 @@ lbox_insert(lua_State *L)
 	if (lua_gettop(L) != 2 || !lua_isnumber(L, 1))
 		return luaL_error(L, "Usage space:insert(tuple)");
 
-	RegionGuard region_guard(&fiber()->gc);
-	struct request *request = lbox_request_create(L, IPROTO_INSERT,
-						      -1, 2);
-	box_process(port_lua_create(L), request);
+	struct request request;
+	struct port_lua port;
+	lbox_request_create(&request, L, IPROTO_INSERT, -1, 2);
+	port_lua_create(&port, L);
+	box_process((struct port *) &port, &request);
 	return lua_gettop(L) - 2;
 }
 
@@ -344,10 +329,11 @@ lbox_replace(lua_State *L)
 	if (lua_gettop(L) != 2 || !lua_isnumber(L, 1))
 		return luaL_error(L, "Usage space:replace(tuple)");
 
-	RegionGuard region_guard(&fiber()->gc);
-	struct request *request = lbox_request_create(L, IPROTO_REPLACE,
-						      -1, 2);
-	box_process(port_lua_create(L), request);
+	struct request request;
+	struct port_lua port;
+	lbox_request_create(&request, L, IPROTO_REPLACE, -1, 2);
+	port_lua_create(&port, L);
+	box_process((struct port *) &port, &request);
 	return lua_gettop(L) - 2;
 }
 
@@ -357,12 +343,13 @@ lbox_update(lua_State *L)
 	if (lua_gettop(L) != 4 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2))
 		return luaL_error(L, "Usage space:update(key, ops)");
 
-	RegionGuard region_guard(&fiber()->gc);
-	struct request *request = lbox_request_create(L, IPROTO_UPDATE,
-						      3, 4);
-	request->field_base = 1; /* field ids are one-indexed */
+	struct request request;
+	struct port_lua port;
+	lbox_request_create(&request, L, IPROTO_UPDATE, 3, 4);
+	request.field_base = 1; /* field ids are one-indexed */
+	port_lua_create(&port, L);
 	/* Ignore index_id for now */
-	box_process(port_lua_create(L), request);
+	box_process((struct port *) &port, &request);
 	return lua_gettop(L) - 4;
 }
 
@@ -372,11 +359,12 @@ lbox_delete(lua_State *L)
 	if (lua_gettop(L) != 3 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2))
 		return luaL_error(L, "Usage space:delete(key)");
 
-	RegionGuard region_guard(&fiber()->gc);
-	struct request *request = lbox_request_create(L, IPROTO_DELETE,
-						      3, -1);
+	struct request request;
+	struct port_lua port;
+	lbox_request_create(&request, L, IPROTO_DELETE, 3, -1);
+	port_lua_create(&port, L);
 	/* Ignore index_id for now */
-	box_process(port_lua_create(L), request);
+	box_process((struct port *) &port, &request);
 	return lua_gettop(L) - 3;
 }
 
@@ -503,10 +491,9 @@ access_check_func(const char *name, uint32_t name_len,
  * (implementation of 'CALL' command code).
  */
 void
-box_lua_call(struct request *request, struct txn *txn, struct port *port)
+box_lua_call(struct request *request, struct port *port)
 {
 	struct user *user = user();
-	(void) txn;
 	lua_State *L = lua_newthread(tarantool_L);
 	LuarefGuard coro_ref(tarantool_L);
 	const char *name = request->key;
diff --git a/src/box/lua/call.h b/src/box/lua/call.h
index c5ad2c4bab..44ea45bd4a 100644
--- a/src/box/lua/call.h
+++ b/src/box/lua/call.h
@@ -31,7 +31,6 @@
 
 #include <stdint.h>
 
-struct txn;
 struct request;
 struct port;
 
@@ -40,7 +39,7 @@ struct port;
  * (implementation of 'CALL' command code).
  */
 void
-box_lua_call(struct request *request, struct txn *txn, struct port *port);
+box_lua_call(struct request *request, struct port *port);
 
 extern "C" {
 struct port_ffi
diff --git a/src/box/recovery.cc b/src/box/recovery.cc
index 70b2990f21..25074974cd 100644
--- a/src/box/recovery.cc
+++ b/src/box/recovery.cc
@@ -47,6 +47,7 @@
 #include "scoped_guard.h"
 #include "box/cluster.h"
 #include "vclock.h"
+#include "session.h"
 
 /*
  * Recovery subsystem
@@ -531,6 +532,7 @@ recovery_finalize(struct recovery_state *r)
  * locally or send to the replica.
  */
 struct wal_watcher {
+	struct session *session;
 	/**
 	 * Rescan the WAL directory in search for new WAL files
 	 * every wal_dir_rescan_delay seconds.
@@ -572,7 +574,10 @@ recovery_rescan_dir(ev_loop * loop, ev_timer *w, int /* revents */)
 	struct wal_watcher *watcher = r->watcher;
 	struct log_io *save_current_wal = r->current_wal;
 
+	/** To process transactions, we need a working session. */
+	fiber_set_session(fiber(), r->watcher->session);
 	int result = recover_remaining_wals(r);
+	fiber_set_session(fiber(), NULL);
 	if (result < 0)
 		panic("recover failed: %i", result);
 	if (save_current_wal != r->current_wal) {
@@ -588,7 +593,9 @@ recovery_rescan_file(ev_loop * loop, ev_stat *w, int /* revents */)
 {
 	struct recovery_state *r = (struct recovery_state *) w->data;
 	struct wal_watcher *watcher = r->watcher;
+	fiber_set_session(fiber(), r->watcher->session);
 	int result = recover_wal(r, r->current_wal);
+	fiber_set_session(fiber(), NULL);
 	if (result < 0)
 		panic("recover failed");
 	if (result == LOG_EOF) {
@@ -609,6 +616,8 @@ recovery_follow_local(struct recovery_state *r, ev_tstamp wal_dir_rescan_delay)
 
 	struct wal_watcher  *watcher = r->watcher= &wal_watcher;
 
+	r->watcher->session = session_create(-1, 0);
+
 	ev_timer_init(&watcher->dir_timer, recovery_rescan_dir,
 		      wal_dir_rescan_delay, wal_dir_rescan_delay);
 	watcher->dir_timer.data = watcher->stat.data = r;
@@ -629,6 +638,8 @@ recovery_stop_local(struct recovery_state *r)
 	ev_timer_stop(loop(), &watcher->dir_timer);
 	if (ev_is_active(&watcher->stat))
 		ev_stat_stop(loop(), &watcher->stat);
+	session_destroy(watcher->session);
+	watcher->session = NULL;
 
 	r->watcher = NULL;
 }
diff --git a/src/box/request.cc b/src/box/request.cc
index 759f3bbb75..5c8deca376 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -41,38 +41,6 @@
 #include "authentication.h"
 #include "access.h"
 
-#if 0
-static const char *
-read_tuple(const char **reqpos, const char *reqend)
-{
-	const char *tuple = *reqpos;
-	if (unlikely(mp_check(reqpos, reqend))) {
-		say_error("\n"
-			  "********************************************\n"
-			  "* Found a corrupted tuple in a request!    *\n"
-			  "* This can be either due to a memory       *\n"
-			  "* corruption or a bug in the server.       *\n"
-			  "* The tuple can not be loaded.             *\n"
-			  "********************************************\n"
-			  "Request tail, BASE64 encoded:               \n");
-
-		uint32_t tuple_len = reqend - tuple;
-		int base64_buflen = base64_bufsize(tuple_len);
-		char *base64_buf = (char *) malloc(base64_buflen);
-		int len = base64_encode(tuple, tuple_len,
-					base64_buf, base64_buflen);
-		write(STDERR_FILENO, base64_buf, len);
-		free(base64_buf);
-		tnt_raise(ClientError, ER_INVALID_MSGPACK, "tuple");
-	}
-
-	if (unlikely(mp_typeof(*tuple) != MP_ARRAY))
-		tnt_raise(ClientError, ER_TUPLE_NOT_ARRAY);
-
-	return tuple;
-}
-#endif
-
 enum dup_replace_mode
 dup_replace_mode(uint32_t op)
 {
@@ -80,9 +48,9 @@ dup_replace_mode(uint32_t op)
 }
 
 static void
-execute_replace(struct request *request, struct txn *txn, struct port *port)
+execute_replace(struct request *request, struct port *port)
 {
-	(void) port;
+	struct txn *txn = txn_begin();
 	struct space *space = space_cache_find(request->space_id);
 
 	space_check_access(space, PRIV_W);
@@ -94,13 +62,13 @@ execute_replace(struct request *request, struct txn *txn, struct port *port)
 
 	txn_add_redo(txn, request);
 	txn_replace(txn, space, NULL, new_tuple, mode);
+	txn_commit(txn, port);
 }
 
 static void
-execute_update(struct request *request, struct txn *txn,
-	       struct port *port)
+execute_update(struct request *request, struct port *port)
 {
-	(void) port;
+	struct txn *txn = txn_begin();
 
 	/* Parse UPDATE request. */
 	/** Search key  and key part count. */
@@ -115,8 +83,10 @@ execute_update(struct request *request, struct txn *txn,
 	struct tuple *old_tuple = pk->findByKey(key, part_count);
 
 	txn_add_redo(txn, request);
-	if (old_tuple == NULL)
+	if (old_tuple == NULL) {
+		txn_commit(txn, port);
 		return;
+	}
 
 	/* Update the tuple. */
 	struct tuple *new_tuple = tuple_update(space->format,
@@ -128,11 +98,13 @@ execute_update(struct request *request, struct txn *txn,
 	TupleGuard guard(new_tuple);
 	space_validate_tuple(space, new_tuple);
 	txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT);
+	txn_commit(txn, port);
 }
 
 static void
-execute_delete(struct request *request, struct txn *txn, struct port *port)
+execute_delete(struct request *request, struct port *port)
 {
+	struct txn *txn = txn_begin();
 	(void) port;
 	struct space *space = space_cache_find(request->space_id);
 	space_check_access(space, PRIV_W);
@@ -145,16 +117,16 @@ execute_delete(struct request *request, struct txn *txn, struct port *port)
 	struct tuple *old_tuple = pk->findByKey(key, part_count);
 
 	txn_add_redo(txn, request);
-	if (old_tuple == NULL)
-		return;
-	txn_replace(txn, space, old_tuple, NULL, DUP_REPLACE_OR_INSERT);
+	if (old_tuple != NULL)
+		txn_replace(txn, space, old_tuple, NULL, DUP_REPLACE_OR_INSERT);
+	txn_commit(txn, port);
 }
 
 
 static void
-execute_select(struct request *request, struct txn *txn, struct port *port)
+execute_select(struct request *request, struct port *port)
 {
-	(void) txn;
+	struct txn *txn = txn_begin();
 	struct space *space = space_cache_find(request->space_id);
 	space_check_access(space, PRIV_R);
 	Index *index = index_find(space, request->index_id);
@@ -187,11 +159,11 @@ execute_select(struct request *request, struct txn *txn, struct port *port)
 			break;
 		port_add_tuple(port, tuple);
 	}
+	txn_commit(txn, port);
 }
 
 void
-execute_auth(struct request *request, struct txn * /* txn */,
-	     struct port * /* port */)
+execute_auth(struct request *request, struct port * /* port */)
 {
 	const char *user = request->key;
 	uint32_t len = mp_decode_strl(&user);
diff --git a/src/box/request.h b/src/box/request.h
index 6a188098f7..a6d2dc8426 100644
--- a/src/box/request.h
+++ b/src/box/request.h
@@ -34,7 +34,7 @@
 struct txn;
 struct port;
 
-typedef void (*request_execute_f)(struct request *, struct txn *, struct port *);
+typedef void (*request_execute_f)(struct request *, struct port *);
 enum { REQUEST_IOVMAX = IPROTO_PACKET_BODY_IOVMAX };
 
 struct request
diff --git a/src/box/txn.cc b/src/box/txn.cc
index 6ab21cae40..52ab7b7a42 100644
--- a/src/box/txn.cc
+++ b/src/box/txn.cc
@@ -34,9 +34,20 @@
 #include "recovery.h"
 #include <fiber.h>
 #include "request.h" /* for request_name */
+#include "session.h"
+#include "port.h"
 
 double too_long_threshold;
 
+void
+port_send_tuple(struct port *port, struct txn *txn)
+{
+	struct tuple *tuple;
+	if ((tuple = txn->new_tuple) || (tuple = txn->old_tuple))
+		port_add_tuple(port, tuple);
+}
+
+
 void
 txn_add_redo(struct txn *txn, struct request *request)
 {
@@ -80,16 +91,39 @@ txn_replace(struct txn *txn, struct space *space,
 struct txn *
 txn_begin()
 {
+	assert(! in_txn());
 	struct txn *txn = (struct txn *)
 		region_alloc0(&fiber()->gc, sizeof(*txn));
 	rlist_create(&txn->on_commit);
 	rlist_create(&txn->on_rollback);
+	in_txn() = txn;
 	return txn;
 }
 
+/**
+ * txn_finish() follows txn_commit() on success.
+ * It's moved to a separate call to be able to send
+ * old tuple to the user before it's deleted.
+ */
 void
-txn_commit(struct txn *txn)
+txn_finish(struct txn *txn)
 {
+	assert(txn == in_txn());
+	if (txn->old_tuple)
+		tuple_ref(txn->old_tuple, -1);
+	if (txn->space)
+		txn->space->engine->factory->txnFinish(txn);
+	TRASH(txn);
+	/** Free volatile txn memory. */
+	fiber_gc();
+	in_txn() = NULL;
+}
+
+
+void
+txn_commit(struct txn *txn, struct port *port)
+{
+	assert(txn == in_txn());
 	if ((txn->old_tuple || txn->new_tuple) &&
 	    !space_is_temporary(txn->space)) {
 		int res = 0;
@@ -110,26 +144,16 @@ txn_commit(struct txn *txn)
 			tnt_raise(LoggedError, ER_WAL_IO);
 	}
 	trigger_run(&txn->on_commit, txn); /* must not throw. */
-}
-
-/**
- * txn_finish() follows txn_commit() on success.
- * It's moved to a separate call to be able to send
- * old tuple to the user before it's deleted.
- */
-void
-txn_finish(struct txn *txn)
-{
-	if (txn->old_tuple)
-		tuple_ref(txn->old_tuple, -1);
-	if (txn->space)
-		txn->space->engine->factory->txnFinish(txn);
-	TRASH(txn);
+	port_send_tuple(port, txn);
+	txn_finish(txn);
 }
 
 void
-txn_rollback(struct txn *txn)
+txn_rollback()
 {
+	struct txn *txn = in_txn();
+	if (txn == NULL)
+		return;
 	if (txn->old_tuple || txn->new_tuple) {
 		space_replace(txn->space, txn->new_tuple,
 			      txn->old_tuple, DUP_INSERT);
@@ -138,4 +162,7 @@ txn_rollback(struct txn *txn)
 			tuple_ref(txn->new_tuple, -1);
 	}
 	TRASH(txn);
+	/** Free volatile txn memory. */
+	fiber_gc();
+	in_txn() = NULL;
 }
diff --git a/src/box/txn.h b/src/box/txn.h
index e46b085fd2..73a4db31f0 100644
--- a/src/box/txn.h
+++ b/src/box/txn.h
@@ -48,10 +48,14 @@ struct txn {
 	struct iproto_header *row;
 };
 
+/* pointer to the current multithreaded transaction (if any) */
+#define in_txn() (fiber()->session->txn)
+
 struct txn *txn_begin();
-void txn_commit(struct txn *txn);
+void txn_commit(struct txn *txn, struct port *port);
 void txn_finish(struct txn *txn);
-void txn_rollback(struct txn *txn);
+void txn_rollback();
+
 void txn_replace(struct txn *txn, struct space *space,
 		 struct tuple *old_tuple, struct tuple *new_tuple,
 		 enum dup_replace_mode mode);
diff --git a/src/iproto.cc b/src/iproto.cc
index ad52bc05ad..1ac623a71c 100644
--- a/src/iproto.cc
+++ b/src/iproto.cc
@@ -212,7 +212,6 @@ iproto_queue_handler(va_list ap)
 		request->process(request);
 	}
 	/** Put the current fiber into a queue fiber cache. */
-	fiber_gc();
 	rlist_add_entry(&i_queue->fiber_cache, fiber(), state);
 	fiber_yield();
 	goto restart;
@@ -345,7 +344,10 @@ iproto_connection_delete(struct iproto_connection *con)
 {
 	assert(iproto_connection_is_idle(con));
 	assert(!evio_is_active(&con->output));
-	session_destroy(con->session); /* Never throws. No-op if sid is 0. */
+	if (con->session) {
+		session_run_on_disconnect_triggers(con->session);
+		session_destroy(con->session);
+	}
 	iobuf_delete(con->iobuf[0]);
 	iobuf_delete(con->iobuf[1]);
 	if (con->disconnect)
@@ -772,7 +774,9 @@ iproto_process_connect(struct iproto_request *request)
 		con->session = session_create(fd, con->cookie);
 		coio_write(&con->input, iproto_greeting(con->session->salt),
 			   IPROTO_GREETING_SIZE);
-		trigger_run(&session_on_connect, NULL);
+		session_run_on_connect_triggers(con->session);
+		/* Set session user to guest, until it is authenticated. */
+		session_set_user(con->session, GUEST, GUEST);
 	} catch (ClientError *e) {
 		iproto_reply_error(&iobuf->out, e, request->header.type);
 		try {
diff --git a/src/lib/small/mempool.h b/src/lib/small/mempool.h
index 03e10b807f..55fa00a0c3 100644
--- a/src/lib/small/mempool.h
+++ b/src/lib/small/mempool.h
@@ -31,6 +31,7 @@
 #include <stddef.h>
 #include <stdbool.h>
 #include <inttypes.h>
+#include <string.h>
 #include "small/slab_cache.h"
 #define RB_COMPACT 1
 #include "third_party/rb.h"
@@ -247,6 +248,9 @@ mslab_free(struct mempool *pool, struct mslab *slab, void *ptr);
 static inline void
 mempool_free(struct mempool *pool, void *ptr)
 {
+#ifndef NDEBUG
+	memset(ptr, '#', pool->objsize);
+#endif
 	struct mslab *slab = (struct mslab *)
 		slab_from_ptr(pool->cache, ptr, pool->slab_order);
 	pool->slabs.stats.used -= pool->objsize;
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 85e57d201d..8ebca87f45 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -411,11 +411,6 @@ run_script(va_list ap)
 
 	/* clear the stack from return values. */
 	lua_settop(L, 0);
-	/*
-	 * The file doesn't exist. It's OK, tarantool may
-	 * have no init file.
-	 */
-
 	/*
 	 * Lua script finished. Stop the auxiliary event loop and
 	 * return control back to tarantool_lua_run_script.
diff --git a/src/session.cc b/src/session.cc
index 41d9b78be5..2151f4c808 100644
--- a/src/session.cc
+++ b/src/session.cc
@@ -63,11 +63,6 @@ session_create(int fd, uint64_t cookie)
 	session->cookie = cookie;
 	session->delim[0] = '\0';
 	session->txn = NULL;
-
-	/*
-	 * At first the session user is a superuser,
-	 * to make sure triggers run correctly.
-	 */
 	session_set_user(session, ADMIN, ADMIN);
 	random_bytes(session->salt, SESSION_SEED_SIZE);
 	struct mh_i32ptr_node_t node;
@@ -81,23 +76,12 @@ session_create(int fd, uint64_t cookie)
 		tnt_raise(ClientError, ER_MEMORY_ISSUE,
 			  "session hash", "new session");
 	}
-	/*
-	 * Run the trigger *after* setting the current
-	 * fiber sid.
-	 */
-	fiber_set_session(fiber(), session);
-
-	/* Set session user to guest, until it is authenticated. */
-	session_set_user(session, GUEST, GUEST);
 	return session;
 }
 
 void
-session_destroy(struct session *session)
+session_run_on_disconnect_triggers(struct session *session)
 {
-	if (session == NULL) /* no-op for a dead session. */
-		return;
-	fiber_set_session(fiber(), session);
 	/* For triggers. */
 	session_set_user(session, ADMIN, ADMIN);
 	try {
@@ -108,6 +92,17 @@ session_destroy(struct session *session)
 		/* catch all. */
 	}
 	session_storage_cleanup(session->id);
+}
+
+void
+session_run_on_connect_triggers(struct session *session)
+{
+	trigger_run(&session_on_connect, NULL);
+}
+
+void
+session_destroy(struct session *session)
+{
 	assert(session->txn == NULL);
 	struct mh_i32ptr_node_t node = { session->id, NULL };
 	mh_i32ptr_remove(session_registry, &node, NULL);
@@ -143,11 +138,23 @@ session_free()
 
 SessionGuard::SessionGuard(int fd, uint64_t cookie)
 {
-	session_set_user(session_create(fd, cookie), ADMIN, ADMIN);
+	session = session_create(fd, cookie);
+	fiber_set_session(fiber(), session);
 }
 
 SessionGuard::~SessionGuard()
 {
-	session_destroy(fiber()->session);
+	assert(session == fiber()->session);
+	session_destroy(session);
 }
 
+SessionGuardWithTriggers::SessionGuardWithTriggers(int fd, uint64_t cookie)
+	:SessionGuard(fd, cookie)
+{
+	session_run_on_connect_triggers(session);
+}
+
+SessionGuardWithTriggers::~SessionGuardWithTriggers()
+{
+	session_run_on_disconnect_triggers(session);
+}
diff --git a/src/session.h b/src/session.h
index e15670c817..8595c505bb 100644
--- a/src/session.h
+++ b/src/session.h
@@ -115,11 +115,20 @@ session_set_user(struct session *session, uint8_t auth_token, uint32_t uid)
 	session->uid = uid;
 }
 
-/* The global on-connect trigger. */
+/** Global on-connect triggers. */
 extern struct rlist session_on_connect;
-/* The global on-disconnect trigger. */
+
+/** Run on-connect triggers */
+void
+session_run_on_connect_triggers(struct session *session);
+
+/** Global on-disconnect triggers. */
 extern struct rlist session_on_disconnect;
 
+/** Run on-disconnect triggers */
+void
+session_run_on_disconnect_triggers(struct session *session);
+
 void
 session_init();
 
@@ -132,8 +141,15 @@ session_storage_cleanup(int sid);
 /** A helper class to create and set session in single-session fibers. */
 struct SessionGuard
 {
+	struct session *session;
 	SessionGuard(int fd, uint64_t cookie);
         ~SessionGuard();
 };
 
+struct SessionGuardWithTriggers: public SessionGuard
+{
+	SessionGuardWithTriggers(int fd, uint64_t cookie);
+	~SessionGuardWithTriggers();
+};
+
 #endif /* INCLUDES_TARANTOOL_SESSION_H */
diff --git a/test/box/session.storage.result b/test/box/session.storage.result
index 703271f17a..6547c6866a 100644
--- a/test/box/session.storage.result
+++ b/test/box/session.storage.result
@@ -31,7 +31,7 @@ all = getmetatable(session).aggregate_storage
 ...
 dump(all)
 ---
-- '''[null,null,{"abc":"cde"}]'''
+- '''[null,null,null,{"abc":"cde"}]'''
 ...
 --# create connection second to default
 --# set connection second
-- 
GitLab