diff --git a/CMakeLists.txt b/CMakeLists.txt
index c3a473f11b97c38b332e45585f3bdad62ef305d7..ecab9d6a7057ae6a88b65cad1ce385a846015fc2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -342,14 +342,6 @@ include(BuildLibCORO)
 libcoro_build()
 add_dependencies(build_bundled_libs coro)
 
-#
-# LibGOPT
-#
-
-include(BuildLibGOPT)
-libgopt_build()
-add_dependencies(build_bundled_libs gopt)
-
 #
 # MsgPuck
 #
diff --git a/cmake/BuildLibGOPT.cmake b/cmake/BuildLibGOPT.cmake
deleted file mode 100644
index ef2c13d8558e0d5cddaf8833ac2e95dea8c53b63..0000000000000000000000000000000000000000
--- a/cmake/BuildLibGOPT.cmake
+++ /dev/null
@@ -1,13 +0,0 @@
-#
-# A macro to build the bundled libgopt
-macro(libgopt_build)
-
-    set(gopt_src ${PROJECT_SOURCE_DIR}/third_party/gopt/gopt.c)
-
-    add_library(gopt STATIC ${gopt_src})
-
-    set(LIBGOPT_INCLUDE_DIR ${PROJECT_BINARY_DIR}/third_party/gopt)
-    set(LIBGOPT_LIBRARIES gopt)
-
-    unset(gopt_src)
-endmacro(libgopt_build)
diff --git a/extra/exports b/extra/exports
index 00d3eb058f966d1d27db5065c3b59762bd5b1ca1..0289df03c43f9a5f5623b894db414f570d2eb82c 100644
--- a/extra/exports
+++ b/extra/exports
@@ -13,6 +13,8 @@ guava
 random_bytes
 fiber_time
 fiber_time64
+fiber_clock
+fiber_clock64
 tarantool_lua_slab_cache
 ibuf_create
 ibuf_reinit
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 97aa76130875ef03de70bad2b42fd1e4285d7218..58f44d71bf75de4a48040021c563863109029d29 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -6,7 +6,6 @@ enable_tnt_compile_flags()
 include_directories(${LIBEV_INCLUDE_DIR})
 include_directories(${LIBEIO_INCLUDE_DIR})
 include_directories(${LIBCORO_INCLUDE_DIR})
-include_directories(${LIBGOPT_INCLUDE_DIR})
 include_directories(${LUAJIT_INCLUDE_DIRS})
 include_directories(${READLINE_INCLUDE_DIRS})
 include_directories(${LIBYAML_INCLUDE_DIRS})
@@ -140,7 +139,6 @@ set (server_sources
      iobuf.cc
      httpc.c
      pickle.c
-     opts.c
      cfg.c
      cpu_feature.c
      title.c
@@ -180,10 +178,11 @@ set(api_headers
     ${CMAKE_SOURCE_DIR}/src/lua/utils.h
     ${CMAKE_SOURCE_DIR}/src/box/txn.h
     ${CMAKE_SOURCE_DIR}/src/box/key_def.h
+    ${CMAKE_SOURCE_DIR}/src/box/field_def.h
     ${CMAKE_SOURCE_DIR}/src/box/tuple.h
     ${CMAKE_SOURCE_DIR}/src/box/tuple_format.h
     ${CMAKE_SOURCE_DIR}/src/box/tuple_compare.h
-    ${CMAKE_SOURCE_DIR}/src/box/schema.h
+    ${CMAKE_SOURCE_DIR}/src/box/schema_def.h
     ${CMAKE_SOURCE_DIR}/src/box/box.h
     ${CMAKE_SOURCE_DIR}/src/box/index.h
     ${CMAKE_SOURCE_DIR}/src/box/iterator_type.h
@@ -214,7 +213,6 @@ set (reexport_libraries server core misc bitset csv
 
 set (common_libraries
     ${reexport_libraries}
-    ${LIBGOPT_LIBRARIES}
     ${LIBYAML_LIBRARIES}
     ${READLINE_LIBRARIES}
     ${OPENSSL_LIBRARIES}
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index d08bb3dcdb48e368060345f8ef91df22e1925305..60f9665207ffea638f50bd89b8171b684e6dff52 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -38,6 +38,8 @@ add_library(tuple STATIC
     tuple_compare.cc
     tuple_hash.cc
     key_def.cc
+    field_def.c
+    opt_def.c
 )
 target_link_libraries(tuple box_error core ${MSGPUCK_LIBRARIES} misc)
 
@@ -50,6 +52,7 @@ add_library(box STATIC
     xrow_io.cc
     tuple_convert.c
     index.cc
+    index_def.c
     iterator_type.c
     memtx_index.cc
     memtx_hash.cc
@@ -79,9 +82,12 @@ add_library(box STATIC
     vy_log.c
     vy_upsert.c
     space.cc
+    space_def.c
     func.c
+    func_def.c
     alter.cc
     schema.cc
+    schema_def.c
     session.cc
     port.c
     txn.cc
diff --git a/src/box/alter.cc b/src/box/alter.cc
index e9a3ab0dfa082a4baba805487dcbb4e3a75b39f2..343ff1dd7e3e54cfa9bdca696d0e1e69a7991990 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -169,6 +169,33 @@ opt_set(void *opts, const struct opt_def *def, const char **val)
 	return 0;
 }
 
+static void
+opts_parse_key(void *opts, const struct opt_def *reg, const char *key,
+	       uint32_t key_len, const char **data, uint32_t errcode,
+	       uint32_t field_no)
+{
+	char errmsg[DIAG_ERRMSG_MAX];
+	bool found = false;
+	for (const struct opt_def *def = reg; def->name != NULL; def++) {
+		if (key_len != strlen(def->name) ||
+		    memcmp(key, def->name, key_len) != 0)
+			continue;
+
+		if (opt_set(opts, def, data) != 0) {
+			snprintf(errmsg, sizeof(errmsg), "'%.*s' must be %s",
+				 key_len, key, opt_type_strs[def->type]);
+			tnt_raise(ClientError, errcode, field_no, errmsg);
+		}
+		found = true;
+		break;
+	}
+	if (!found) {
+		snprintf(errmsg, sizeof(errmsg), "unexpected option '%.*s'",
+			 key_len, key);
+		tnt_raise(ClientError, errcode, field_no, errmsg);
+	}
+}
+
 /**
  * Populate key options from their msgpack-encoded representation
  * (msgpack map)
@@ -179,8 +206,6 @@ static const char *
 opts_create_from_field(void *opts, const struct opt_def *reg, const char *map,
 		       uint32_t errcode, uint32_t field_no)
 {
-	char errmsg[DIAG_ERRMSG_MAX];
-
 	if (mp_typeof(*map) == MP_NIL)
 		return map;
 	if (mp_typeof(*map) != MP_MAP)
@@ -199,29 +224,8 @@ opts_create_from_field(void *opts, const struct opt_def *reg, const char *map,
 		}
 		uint32_t key_len;
 		const char *key = mp_decode_str(&map, &key_len);
-		bool found = false;
-		for (const struct opt_def *def = reg; def->name != NULL; def++) {
-			if (key_len != strlen(def->name) ||
-			    memcmp(key, def->name, key_len) != 0)
-				continue;
-
-			if (opt_set(opts, def, &map) != 0) {
-				snprintf(errmsg, sizeof(errmsg),
-					"'%.*s' must be %s", key_len, key,
-					opt_type_strs[def->type]);
-				tnt_raise(ClientError, errcode, field_no,
-					  errmsg);
-			}
-
-			found = true;
-			break;
-		}
-		if (!found) {
-			snprintf(errmsg, sizeof(errmsg),
-				"unexpected option '%.*s'",
-				key_len, key);
-			tnt_raise(ClientError, errcode, field_no, errmsg);
-		}
+		opts_parse_key(opts, reg, key, key_len, &map, errcode,
+			       field_no);
 	}
 	return map;
 }
@@ -321,7 +325,7 @@ index_def_new_from_tuple(struct tuple *tuple, struct space *old_space)
 	if (index_def == NULL)
 		diag_raise();
 	auto index_def_guard = make_scoped_guard([=] { index_def_delete(index_def); });
-	index_def_check(index_def, space_name(old_space));
+	index_def_check_xc(index_def, space_name(old_space));
 	old_space->handler->checkIndexDef(old_space, index_def);
 	index_def_guard.is_active = false;
 	return index_def;
@@ -366,7 +370,7 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode)
 	auto def_guard = make_scoped_guard([=] { space_def_delete(def); });
 	memcpy(def->name, name, name_len);
 	def->name[name_len] = 0;
-	identifier_check(def->name);
+	identifier_check_xc(def->name);
 	def->id = tuple_field_u32_xc(tuple, BOX_SPACE_FIELD_ID);
 	if (def->id > BOX_SPACE_MAX) {
 		tnt_raise(ClientError, errcode,
@@ -390,7 +394,7 @@ space_def_new_from_tuple(struct tuple *tuple, uint32_t errcode)
 	}
 	memcpy(def->engine_name, engine_name, name_len);
 	def->engine_name[name_len] = 0;
-	identifier_check(def->engine_name);
+	identifier_check_xc(def->engine_name);
 	space_opts_create(&def->opts, tuple);
 	Engine *engine = engine_find(def->engine_name);
 	engine->checkSpaceDef(def);
@@ -464,7 +468,7 @@ alter_space_new(struct space *old_space)
 		region_calloc_object_xc(&fiber()->gc, struct alter_space);
 	rlist_create(&alter->ops);
 	alter->old_space = old_space;
-	alter->space_def = space_def_dup(alter->old_space->def);
+	alter->space_def = space_def_dup_xc(alter->old_space->def);
 	return alter;
 }
 
@@ -1625,11 +1629,11 @@ user_def_fill_auth_data(struct user_def *user, const char *auth_data)
 			tnt_raise(ClientError, ER_CREATE_USER,
 				  user->name, "invalid user password");
 		}
-                if (user->uid == GUEST) {
-                    /** Guest user is permitted to have empty password */
-                    if (strncmp(hash2_base64, CHAP_SHA1_EMPTY_PASSWORD, len))
-                        tnt_raise(ClientError, ER_GUEST_USER_PASSWORD);
-                }
+		if (user->uid == GUEST) {
+		    /** Guest user is permitted to have empty password */
+		    if (strncmp(hash2_base64, CHAP_SHA1_EMPTY_PASSWORD, len))
+			tnt_raise(ClientError, ER_GUEST_USER_PASSWORD);
+		}
 
 		base64_decode(hash2_base64, len, user->hash2,
 			      sizeof(user->hash2));
@@ -1665,7 +1669,7 @@ user_def_new_from_tuple(struct tuple *tuple)
 		tnt_raise(ClientError, ER_CREATE_USER,
 			  user->name, "unknown user type");
 	}
-	identifier_check(user->name);
+	identifier_check_xc(user->name);
 	access_check_ddl(user->owner, SC_USER);
 	/*
 	 * AUTH_DATA field in _user space should contain
diff --git a/src/box/applier.cc b/src/box/applier.cc
index ab8c060893c6aafcf2c5f71a8688e4800a18dc1d..247dcb2569093572641b53b4b4e08f1a7faac858 100644
--- a/src/box/applier.cc
+++ b/src/box/applier.cc
@@ -164,7 +164,7 @@ applier_connect(struct applier *applier)
 	coio_connect(coio, uri, &applier->addr, &applier->addr_len);
 	assert(coio->fd >= 0);
 	coio_readn(coio, greetingbuf, IPROTO_GREETING_SIZE);
-	applier->last_row_time = ev_now(loop());
+	applier->last_row_time = ev_monotonic_now(loop());
 
 	/* Decode instance version and name from greeting */
 	struct greeting greeting;
@@ -221,7 +221,7 @@ applier_connect(struct applier *applier)
 			    uri->login_len, uri->password, uri->password_len);
 	coio_write_xrow(coio, &row);
 	coio_read_xrow(coio, &iobuf->in, &row);
-	applier->last_row_time = ev_now(loop());
+	applier->last_row_time = ev_monotonic_now(loop());
 	if (row.type != IPROTO_OK)
 		xrow_decode_error_xc(&row); /* auth failed */
 
@@ -287,7 +287,7 @@ applier_join(struct applier *applier)
 	assert(applier->join_stream != NULL);
 	while (true) {
 		coio_read_xrow(coio, &iobuf->in, &row);
-		applier->last_row_time = ev_now(loop());
+		applier->last_row_time = ev_monotonic_now(loop());
 		if (iproto_type_is_dml(row.type)) {
 			xstream_write_xc(applier->join_stream, &row);
 		} else if (row.type == IPROTO_OK) {
@@ -324,7 +324,7 @@ applier_join(struct applier *applier)
 	 */
 	while (true) {
 		coio_read_xrow(coio, &iobuf->in, &row);
-		applier->last_row_time = ev_now(loop());
+		applier->last_row_time = ev_monotonic_now(loop());
 		if (iproto_type_is_dml(row.type)) {
 			vclock_follow(&replicaset_vclock, row.replica_id,
 				      row.lsn);
@@ -403,7 +403,7 @@ applier_subscribe(struct applier *applier)
 	while (true) {
 		coio_read_xrow(coio, &iobuf->in, &row);
 		applier->lag = ev_now(loop()) - row.tm;
-		applier->last_row_time = ev_now(loop());
+		applier->last_row_time = ev_monotonic_now(loop());
 
 		if (iproto_type_is_error(row.type))
 			xrow_decode_error_xc(&row);  /* error */
@@ -494,6 +494,10 @@ applier_f(va_list ap)
 				/* Invalid configuration */
 				applier_log_error(applier, e);
 				goto reconnect;
+			} else if (e->errcode() == ER_SYSTEM) {
+				/* System error from master instance. */
+				applier_log_error(applier, e);
+				goto reconnect;
 			} else {
 				/* Unrecoverable errors */
 				applier_log_error(applier, e);
@@ -580,7 +584,7 @@ applier_new(const char *uri, struct xstream *join_stream,
 
 	applier->join_stream = join_stream;
 	applier->subscribe_stream = subscribe_stream;
-	applier->last_row_time = ev_now(loop());
+	applier->last_row_time = ev_monotonic_now(loop());
 	rlist_create(&applier->on_state);
 	fiber_channel_create(&applier->pause, 0);
 	fiber_cond_create(&applier->writer_cond);
@@ -709,7 +713,7 @@ applier_connect_all(struct applier **appliers, int count,
 	/* Memory for on_state triggers registered in appliers */
 	struct applier_on_state triggers[VCLOCK_MAX];
 	/* Wait results until this time */
-	double deadline = fiber_time() + timeout;
+	double deadline = ev_monotonic_now(loop()) + timeout;
 
 	/* Add triggers and start simulations connection to remote peers */
 	for (int i = 0; i < count; i++) {
@@ -722,7 +726,7 @@ applier_connect_all(struct applier **appliers, int count,
 
 	/* Wait for all appliers */
 	for (int i = 0; i < count; i++) {
-		double wait = deadline - fiber_time();
+		double wait = deadline - ev_monotonic_now(loop());
 		if (wait < 0.0 ||
 		    applier_wait_for_state(&triggers[i], wait) != 0) {
 			goto error;
diff --git a/src/box/authentication.cc b/src/box/authentication.cc
index 802d1a64cb637b43249870713ce4b12a57dae00d..d2c96c254d80aabc15078c658401ad421d2d6aa9 100644
--- a/src/box/authentication.cc
+++ b/src/box/authentication.cc
@@ -31,6 +31,7 @@
 #include "authentication.h"
 #include "user.h"
 #include "session.h"
+#include "msgpuck.h"
 
 static char zero_hash[SCRAMBLE_SIZE];
 
diff --git a/src/box/engine.cc b/src/box/engine.cc
index 8ec6deb800a670fb52126e8eafcee1e7114ba44b..4375685a8a9da255dd0c35d1dcf824d3245e26c5 100644
--- a/src/box/engine.cc
+++ b/src/box/engine.cc
@@ -35,9 +35,7 @@
 #include "port.h"
 #include "space.h"
 #include "exception.h"
-#include "schema.h"
 #include "small/rlist.h"
-#include "scoped_guard.h"
 #include "vclock.h"
 #include <stdlib.h>
 #include <string.h>
diff --git a/src/box/error.cc b/src/box/error.cc
index 0cda20bd11ebccf887e74cffa5ae0f900a9b7595..fb09e2e33f8a54fa3809377b7d5832ce40634eeb 100644
--- a/src/box/error.cc
+++ b/src/box/error.cc
@@ -34,9 +34,6 @@
 #include <fiber.h>
 #include <rmean.h>
 
-static struct error *
-BuildClientError(const char *file, unsigned line, uint32_t errcode, ...);
-
 /* {{{ public API */
 
 const char *
@@ -115,7 +112,7 @@ ClientError::ClientError(const char *file, unsigned line,
 		rmean_collect(rmean_error, RMEAN_ERROR, 1);
 }
 
-static struct error *
+struct error *
 BuildClientError(const char *file, unsigned line, uint32_t errcode, ...)
 {
 	try {
@@ -159,7 +156,7 @@ ErrorInjection::ErrorInjection(const char *file, unsigned line, const char *msg)
 
 const struct type_info type_XlogError = make_type("XlogError", &type_Exception);
 
-static struct error *
+struct error *
 BuildXlogError(const char *file, unsigned line, const char *format, ...)
 {
 	try {
@@ -173,9 +170,3 @@ BuildXlogError(const char *file, unsigned line, const char *format, ...)
 	}
 }
 
-void
-box_error_init(void)
-{
-	error_factory->ClientError = BuildClientError;
-	error_factory->XlogError = BuildXlogError;
-}
diff --git a/src/box/error.h b/src/box/error.h
index 0d7ff222bf56ed4cd3275a5ea38af8c3a906ed05..582cc9d3ddaf218428a1b75259d60cd8d74f4675 100644
--- a/src/box/error.h
+++ b/src/box/error.h
@@ -36,6 +36,9 @@
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+struct error *
+BuildClientError(const char *file, unsigned line, uint32_t errcode, ...);
+
 /** \cond public */
 
 struct error;
@@ -206,9 +209,6 @@ struct XlogError: public Exception
 	virtual void raise() { throw this; }
 };
 
-void
-box_error_init(void);
-
 #endif /* defined(__cplusplus) */
 
 #endif /* TARANTOOL_BOX_ERROR_H_INCLUDED */
diff --git a/src/box/field_def.c b/src/box/field_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..53c2b60beaa6de080f8800b296bc0ce32b6f6bd0
--- /dev/null
+++ b/src/box/field_def.c
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "field_def.h"
+#include "trivia/util.h"
+
+const char *field_type_strs[] = {
+	/* [FIELD_TYPE_ANY]      = */ "any",
+	/* [FIELD_TYPE_UNSIGNED] = */ "unsigned",
+	/* [FIELD_TYPE_STRING]   = */ "string",
+	/* [FIELD_TYPE_ARRAY]    = */ "array",
+	/* [FIELD_TYPE_NUMBER]   = */ "number",
+	/* [FIELD_TYPE_INTEGER]  = */ "integer",
+	/* [FIELD_TYPE_SCALAR]   = */ "scalar",
+	/* [FIELD_TYPE_MAP]      = */ "map",
+};
+
+enum field_type
+field_type_by_name(const char *name)
+{
+	enum field_type field_type = STR2ENUM(field_type, name);
+	/*
+	 * FIELD_TYPE_ANY can't be used as type of indexed field,
+	 * because it is internal type used only for filling
+	 * struct tuple_format.fields array.
+	 */
+	if (field_type != field_type_MAX && field_type != FIELD_TYPE_ANY)
+		return field_type;
+	/* 'num' and 'str' in _index are deprecated since Tarantool 1.7 */
+	if (strcasecmp(name, "num") == 0)
+		return FIELD_TYPE_UNSIGNED;
+	else if (strcasecmp(name, "str") == 0)
+		return FIELD_TYPE_STRING;
+	return field_type_MAX;
+}
diff --git a/src/box/field_def.h b/src/box/field_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..f2cd57e12584102d714beb6aa495741cfd8b2105
--- /dev/null
+++ b/src/box/field_def.h
@@ -0,0 +1,68 @@
+#ifndef TARANTOOL_BOX_FIELD_DEF_H_INCLUDED
+#define TARANTOOL_BOX_FIELD_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/** \cond public */
+
+/*
+ * Possible field data types. Can't use STRS/ENUM macros for them,
+ * since there is a mismatch between enum name (STRING) and type
+ * name literal ("STR"). STR is already used as Objective C type.
+ */
+enum field_type {
+	FIELD_TYPE_ANY = 0,
+	FIELD_TYPE_UNSIGNED,
+	FIELD_TYPE_STRING,
+	FIELD_TYPE_ARRAY,
+	FIELD_TYPE_NUMBER,
+	FIELD_TYPE_INTEGER,
+	FIELD_TYPE_SCALAR,
+	FIELD_TYPE_MAP,
+	field_type_MAX
+};
+
+/** \endcond public */
+
+extern const char *field_type_strs[];
+
+enum field_type
+field_type_by_name(const char *name);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_FIELD_DEF_H_INCLUDED */
diff --git a/src/box/func.c b/src/box/func.c
index 1a4283802604e43995390aaa79b4a0e41a3c232a..e33783e4d92779b19ef653c820cd3ae1c9a90e90 100644
--- a/src/box/func.c
+++ b/src/box/func.c
@@ -29,14 +29,12 @@
  * SUCH DAMAGE.
  */
 #include "func.h"
-
 #include "trivia/config.h"
-
-#include <dlfcn.h>
-
 #include "assoc.h"
-
 #include "lua/utils.h"
+#include "error.h"
+#include "diag.h"
+#include <dlfcn.h>
 
 /**
  * Parsed symbol and package names.
diff --git a/src/box/func.h b/src/box/func.h
index b152a2285b15ef7b419de60ccc59b54a45ba2716..0957546c62e4d2639ca08542365d12a74d1bffbf 100644
--- a/src/box/func.h
+++ b/src/box/func.h
@@ -34,10 +34,9 @@
 #include <stddef.h>
 #include <stdint.h>
 #include <stdbool.h>
-
-#include <small/rlist.h>
-
-#include "key_def.h"
+#include "small/rlist.h"
+#include "func_def.h"
+#include "user_def.h"
 
 #if defined(__cplusplus)
 extern "C" {
diff --git a/src/box/func_def.c b/src/box/func_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..76ed77b24b7c30ae5e1bdd0e48dde4de79194a57
--- /dev/null
+++ b/src/box/func_def.c
@@ -0,0 +1,3 @@
+#include "func_def.h"
+
+const char *func_language_strs[] = {"LUA", "C"};
diff --git a/src/box/func_def.h b/src/box/func_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..5b52ab498f7fb933a482b67219a9fe9935a154a9
--- /dev/null
+++ b/src/box/func_def.h
@@ -0,0 +1,89 @@
+#ifndef TARANTOOL_BOX_FUNC_DEF_H_INCLUDED
+#define TARANTOOL_BOX_FUNC_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "trivia/util.h"
+#include <stdbool.h>
+
+/**
+ * The supported language of the stored function.
+ */
+enum func_language {
+	FUNC_LANGUAGE_LUA,
+	FUNC_LANGUAGE_C,
+	func_language_MAX,
+};
+
+extern const char *func_language_strs[];
+
+/**
+ * Definition of a function. Function body is not stored
+ * or replicated (yet).
+ */
+struct func_def {
+	/** Function id. */
+	uint32_t fid;
+	/** Owner of the function. */
+	uint32_t uid;
+	/**
+	 * True if the function requires change of user id before
+	 * invocation.
+	 */
+	bool setuid;
+	/**
+	 * The language of the stored function.
+	 */
+	enum func_language language;
+	/** Function name. */
+	char name[0];
+};
+
+/**
+ * @param name_len length of func_def->name
+ * @returns size in bytes needed to allocate for struct func_def
+ * for a function of length @a a name_len.
+ */
+static inline size_t
+func_def_sizeof(uint32_t name_len)
+{
+	/* +1 for '\0' name terminating. */
+	return sizeof(struct func_def) + name_len + 1;
+}
+
+/**
+ * API of C stored function.
+ */
+typedef struct box_function_ctx box_function_ctx_t;
+typedef int (*box_function_f)(box_function_ctx_t *ctx,
+	     const char *args, const char *args_end);
+
+#endif /* TARANTOOL_BOX_FUNC_DEF_H_INCLUDED */
diff --git a/src/box/index.h b/src/box/index.h
index fa6cc242884bb65fc3e5815eaa0a5f3a1f62c21e..28ada8e5cd73047188922d79d4131485c404c0b7 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -218,7 +218,7 @@ box_index_info(uint32_t space_id, uint32_t index_id,
 
 #if defined(__cplusplus)
 } /* extern "C" */
-#include "key_def.h"
+#include "index_def.h"
 
 struct iterator {
 	struct tuple *(*next)(struct iterator *);
diff --git a/src/box/index_def.c b/src/box/index_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..14956cb00f38e40b1b512454397c122e76ccaee5
--- /dev/null
+++ b/src/box/index_def.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "index_def.h"
+#include "schema_def.h"
+
+const char *index_type_strs[] = { "HASH", "TREE", "BITSET", "RTREE" };
+
+const char *rtree_index_distance_type_strs[] = { "EUCLID", "MANHATTAN" };
+
+const struct index_opts index_opts_default = {
+	/* .unique              = */ true,
+	/* .dimension           = */ 2,
+	/* .distancebuf         = */ { '\0' },
+	/* .distance            = */ RTREE_INDEX_DISTANCE_TYPE_EUCLID,
+	/* .range_size          = */ 0,
+	/* .page_size           = */ 0,
+	/* .run_count_per_level = */ 2,
+	/* .run_size_ratio      = */ 3.5,
+	/* .bloom_fpr           = */ 0.05,
+	/* .lsn                 = */ 0,
+	/* .sql                 = */ NULL,
+};
+
+const struct opt_def index_opts_reg[] = {
+	OPT_DEF("unique", OPT_BOOL, struct index_opts, is_unique),
+	OPT_DEF("dimension", OPT_INT, struct index_opts, dimension),
+	OPT_DEF("distance", OPT_STR, struct index_opts, distancebuf),
+	OPT_DEF("range_size", OPT_INT, struct index_opts, range_size),
+	OPT_DEF("page_size", OPT_INT, struct index_opts, page_size),
+	OPT_DEF("run_count_per_level", OPT_INT, struct index_opts, run_count_per_level),
+	OPT_DEF("run_size_ratio", OPT_FLOAT, struct index_opts, run_size_ratio),
+	OPT_DEF("bloom_fpr", OPT_FLOAT, struct index_opts, bloom_fpr),
+	OPT_DEF("lsn", OPT_INT, struct index_opts, lsn),
+	OPT_DEF("sql", OPT_STRPTR, struct index_opts, sql),
+	{ NULL, opt_type_MAX, 0, 0 },
+};
+
+/**
+ * Destructor for struct index_opts.
+ * The only relevant action so far is to free sql field if not-null.
+ */
+static void
+index_opts_destroy(struct index_opts *opts)
+{
+	if (opts->sql) {
+		free(opts->sql);
+		opts->sql = 0;
+	}
+}
+
+static void
+index_opts_dup(struct index_opts *dst, const struct index_opts *src)
+{
+	*dst = *src;
+	if (src->sql) {
+		dst->sql = (char*)strdup(src->sql);
+		if (dst->sql == NULL)
+			diag_set(OutOfMemory, strlen(src->sql), "sql", "char *");
+	}
+}
+
+struct index_def *
+index_def_new(uint32_t space_id, uint32_t iid, const char *name,
+	      uint32_t name_len, enum index_type type,
+	      const struct index_opts *opts,
+	      struct key_def *key_def, struct key_def *pk_def)
+{
+	assert(name_len <= BOX_NAME_MAX);
+	/* Use calloc to make index_def_delete() safe at all times. */
+	struct index_def *def = (struct index_def *) calloc(1, sizeof(*def));
+	if (def == NULL) {
+		diag_set(OutOfMemory, sizeof(*def), "malloc", "struct index_def");
+		return NULL;
+	}
+	def->name = strndup(name, name_len);
+	if (def->name == NULL) {
+		index_def_delete(def);
+		diag_set(OutOfMemory, name_len + 1, "malloc", "index_def name");
+		return NULL;
+	}
+	if (!identifier_is_valid(def->name)) {
+		diag_set(ClientError, ER_IDENTIFIER, def->name);
+		index_def_delete(def);
+		return NULL;
+	}
+	def->key_def = key_def_dup(key_def);
+	if (pk_def)
+		def->cmp_def = key_def_merge(key_def, pk_def);
+	else
+		def->cmp_def = key_def_dup(key_def);
+	if (def->key_def == NULL || def->cmp_def == NULL) {
+		index_def_delete(def);
+		return NULL;
+	}
+	def->type = type;
+	def->space_id = space_id;
+	def->iid = iid;
+	def->opts = *opts;
+	return def;
+}
+
+struct index_def *
+index_def_dup(const struct index_def *def)
+{
+	struct index_def *dup = (struct index_def *) malloc(sizeof(*dup));
+	if (dup == NULL) {
+		diag_set(OutOfMemory, sizeof(*dup), "malloc",
+			 "struct index_def");
+		return NULL;
+	}
+	*dup = *def;
+	dup->name = strdup(def->name);
+	if (dup->name == NULL) {
+		free(dup);
+		diag_set(OutOfMemory, strlen(def->name) + 1, "malloc",
+			 "index_def name");
+		return NULL;
+	}
+	dup->key_def = key_def_dup(def->key_def);
+	dup->cmp_def = key_def_dup(def->cmp_def);
+	if (dup->key_def == NULL || dup->cmp_def == NULL) {
+		index_def_delete(dup);
+		return NULL;
+	}
+	rlist_create(&dup->link);
+	index_opts_dup(&dup->opts, &def->opts);
+	return dup;
+}
+
+void
+index_def_swap(struct index_def *def1, struct index_def *def2)
+{
+	/*
+	 * Swap const-size items and name. Keep the original key
+	 * definitions, they are used in the engines.
+	 */
+	struct index_def tmp_def = *def1;
+	memcpy(def1, def2, offsetof(struct index_def, key_def));
+	memcpy(def2, &tmp_def, offsetof(struct index_def, key_def));
+	/*
+	 * index_def_swap() is used only during alter to modify
+	 * index metadata.
+	 */
+}
+
+/** Free a key definition. */
+void
+index_def_delete(struct index_def *index_def)
+{
+	index_opts_destroy(&index_def->opts);
+	free(index_def->name);
+
+	if (index_def->key_def) {
+		TRASH(index_def->key_def);
+		free(index_def->key_def);
+	}
+
+	if (index_def->cmp_def) {
+		TRASH(index_def->cmp_def);
+		free(index_def->cmp_def);
+	}
+
+	TRASH(index_def);
+	free(index_def);
+}
+
+bool
+index_def_change_requires_rebuild(struct index_def *old_index_def,
+				  struct index_def *new_index_def)
+{
+	if (old_index_def->iid != new_index_def->iid ||
+	    old_index_def->type != new_index_def->type ||
+	    old_index_def->opts.is_unique != new_index_def->opts.is_unique ||
+	    key_part_cmp(old_index_def->key_def->parts,
+			 old_index_def->key_def->part_count,
+			 new_index_def->key_def->parts,
+			 new_index_def->key_def->part_count) != 0) {
+		return true;
+	}
+	if (old_index_def->type == RTREE) {
+		if (old_index_def->opts.dimension != new_index_def->opts.dimension
+		    || old_index_def->opts.distance != new_index_def->opts.distance)
+			return true;
+	}
+	return false;
+}
+
+int
+index_def_cmp(const struct index_def *key1, const struct index_def *key2)
+{
+	assert(key1->space_id == key2->space_id);
+	if (key1->iid != key2->iid)
+		return key1->iid < key2->iid ? -1 : 1;
+	if (strcmp(key1->name, key2->name))
+		return strcmp(key1->name, key2->name);
+	if (key1->type != key2->type)
+		return (int) key1->type < (int) key2->type ? -1 : 1;
+	if (index_opts_cmp(&key1->opts, &key2->opts))
+		return index_opts_cmp(&key1->opts, &key2->opts);
+
+	return key_part_cmp(key1->key_def->parts, key1->key_def->part_count,
+			    key2->key_def->parts, key2->key_def->part_count);
+}
+
+bool
+index_def_is_valid(struct index_def *index_def, const char *space_name)
+
+{
+	if (index_def->iid >= BOX_INDEX_MAX) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "index id too big");
+		return false;
+	}
+	if (index_def->iid == 0 && index_def->opts.is_unique == false) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "primary key must be unique");
+		return false;
+	}
+	if (index_def->key_def->part_count == 0) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "part count must be positive");
+		return false;
+	}
+	if (index_def->key_def->part_count > BOX_INDEX_PART_MAX) {
+		diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+			 space_name, "too many key parts");
+		return false;
+	}
+	for (uint32_t i = 0; i < index_def->key_def->part_count; i++) {
+		assert(index_def->key_def->parts[i].type > FIELD_TYPE_ANY &&
+		       index_def->key_def->parts[i].type < field_type_MAX);
+		if (index_def->key_def->parts[i].fieldno > BOX_INDEX_FIELD_MAX) {
+			diag_set(ClientError, ER_MODIFY_INDEX, index_def->name,
+				 space_name, "field no is too big");
+			return false;
+		}
+		for (uint32_t j = 0; j < i; j++) {
+			/*
+			 * Courtesy to a user who could have made
+			 * a typo.
+			 */
+			if (index_def->key_def->parts[i].fieldno ==
+			    index_def->key_def->parts[j].fieldno) {
+				diag_set(ClientError, ER_MODIFY_INDEX,
+					 index_def->name, space_name,
+					 "same key part is indexed twice");
+				return false;
+			}
+		}
+	}
+	return true;
+}
diff --git a/src/box/index_def.h b/src/box/index_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..ed466aed2fdb1a644ac74f3f9956e5c6bb5ffe24
--- /dev/null
+++ b/src/box/index_def.h
@@ -0,0 +1,257 @@
+#ifndef TARANTOOL_BOX_INDEX_DEF_H_INCLUDED
+#define TARANTOOL_BOX_INDEX_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "key_def.h"
+#include "opt_def.h"
+#include "small/rlist.h"
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+enum index_type {
+	HASH = 0, /* HASH Index */
+	TREE,     /* TREE Index */
+	BITSET,   /* BITSET Index */
+	RTREE,    /* R-Tree Index */
+	index_type_MAX,
+};
+
+extern const char *index_type_strs[];
+
+enum rtree_index_distance_type {
+	 /* Euclid distance, sqrt(dx*dx + dy*dy) */
+	RTREE_INDEX_DISTANCE_TYPE_EUCLID,
+	/* Manhattan distance, fabs(dx) + fabs(dy) */
+	RTREE_INDEX_DISTANCE_TYPE_MANHATTAN,
+	rtree_index_distance_type_MAX
+};
+extern const char *rtree_index_distance_type_strs[];
+
+/** Index options */
+struct index_opts {
+	/**
+	 * Is this index unique or not - relevant to HASH/TREE
+	 * index
+	 */
+	bool is_unique;
+	/**
+	 * RTREE index dimension.
+	 */
+	int64_t dimension;
+	/**
+	 * RTREE distance type.
+	 */
+	char distancebuf[16];
+	enum rtree_index_distance_type distance;
+	/**
+	 * Vinyl index options.
+	 */
+	int64_t range_size;
+	int64_t page_size;
+	/**
+	 * Maximal number of runs that can be created in a level
+	 * of the LSM tree before triggering compaction.
+	 */
+	int64_t run_count_per_level;
+	/**
+	 * The LSM tree multiplier. Each subsequent level of
+	 * the LSM tree is run_size_ratio times larger than
+	 * previous one.
+	 */
+	double run_size_ratio;
+	/* Bloom filter false positive rate. */
+	double bloom_fpr;
+	/**
+	 * LSN from the time of index creation.
+	 */
+	int64_t lsn;
+	/**
+	 * SQL statement that produced this index.
+	 */
+	char *sql;
+};
+
+extern const struct index_opts index_opts_default;
+extern const struct opt_def index_opts_reg[];
+
+static inline int
+index_opts_cmp(const struct index_opts *o1, const struct index_opts *o2)
+{
+	if (o1->is_unique != o2->is_unique)
+		return o1->is_unique < o2->is_unique ? -1 : 1;
+	if (o1->dimension != o2->dimension)
+		return o1->dimension < o2->dimension ? -1 : 1;
+	if (o1->distance != o2->distance)
+		return o1->distance < o2->distance ? -1 : 1;
+	if (o1->range_size != o2->range_size)
+		return o1->range_size < o2->range_size ? -1 : 1;
+	if (o1->page_size != o2->page_size)
+		return o1->page_size < o2->page_size ? -1 : 1;
+	if (o1->run_count_per_level != o2->run_count_per_level)
+		return o1->run_count_per_level < o2->run_count_per_level ?
+		       -1 : 1;
+	if (o1->run_size_ratio != o2->run_size_ratio)
+		return o1->run_size_ratio < o2->run_size_ratio ? -1 : 1;
+	if (o1->bloom_fpr != o2->bloom_fpr)
+		return o1->bloom_fpr < o2->bloom_fpr ? -1 : 1;
+	return 0;
+}
+
+/* Definition of an index. */
+struct index_def {
+	/* A link in key list. */
+	struct rlist link;
+	/** Ordinal index number in the index array. */
+	uint32_t iid;
+	/* Space id. */
+	uint32_t space_id;
+	/** Index name. */
+	char *name;
+	/** Index type. */
+	enum index_type type;
+	struct index_opts opts;
+
+	/* Index key definition. */
+	struct key_def *key_def;
+	/**
+	 * User-defined key definition, merged with the primary
+	 * key parts. Used by non-unique keys to uniquely identify
+	 * iterator position.
+	 */
+	struct key_def *cmp_def;
+};
+
+struct index_def *
+index_def_dup(const struct index_def *def);
+
+void
+index_def_swap(struct index_def *def1, struct index_def *def2);
+
+/* Destroy and free an index_def. */
+void
+index_def_delete(struct index_def *def);
+
+/**
+ * Add an index definition to a list, preserving the
+ * first position of the primary key.
+ *
+ * In non-unique indexes, secondary keys must contain key parts
+ * of the primary key. This is necessary to make ordered
+ * retrieval from a secondary key useful to SQL
+ * optimizer and make iterators over secondary keys stable
+ * in presence of concurrent updates.
+ * Thus we always create the primary key first, and put
+ * the primary key key_def first in the index_def list.
+ */
+static inline void
+index_def_list_add(struct rlist *index_def_list, struct index_def *index_def)
+{
+	/** Preserve the position of the primary key */
+	if (index_def->iid == 0)
+		rlist_add_entry(index_def_list, index_def, link);
+	else
+		rlist_add_tail_entry(index_def_list, index_def, link);
+}
+
+/**
+ * True, if the index change by alter requires an index rebuild.
+ *
+ * Some changes, such as a new page size or bloom_fpr do not
+ * take effect immediately, so do not require a rebuild.
+ *
+ * Others, such as index name change, do not change the data, only
+ * metadata, so do not require a rebuild either.
+ *
+ * Finally, changing index type or number of parts always requires
+ * a rebuild.
+ */
+bool
+index_def_change_requires_rebuild(struct index_def *old_index_def,
+				  struct index_def *new_index_def);
+
+/**
+ * Create a new index definition definition.
+ *
+ * @param key_def  key definition, must be fully built
+ * @param pk_def   primary key definition, pass non-NULL
+ *                 for secondary keys to construct
+ *                 index_def::cmp_def
+ * @retval not NULL Success.
+ * @retval NULL     Memory error.
+ */
+struct index_def *
+index_def_new(uint32_t space_id, uint32_t iid, const char *name,
+	      uint32_t name_len, enum index_type type,
+	      const struct index_opts *opts,
+	      struct key_def *key_def, struct key_def *pk_def);
+
+/**
+ * One key definition is greater than the other if it's id is
+ * greater, it's name is greater,  it's index type is greater
+ * (HASH < TREE < BITSET) or its key part array is greater.
+ */
+int
+index_def_cmp(const struct index_def *key1, const struct index_def *key2);
+
+/**
+ * Check a key definition for violation of various limits.
+ *
+ * @param index_def   index definition
+ * @param old_space   space definition
+ */
+bool
+index_def_is_valid(struct index_def *index_def, const char *space_name);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+
+static inline struct index_def *
+index_def_dup_xc(const struct index_def *def)
+{
+	struct index_def *ret = index_def_dup(def);
+	if (ret == NULL)
+		diag_raise();
+	return ret;
+}
+
+static inline void
+index_def_check_xc(struct index_def *index_def, const char *space_name)
+{
+	if (! index_def_is_valid(index_def, space_name))
+		diag_raise();
+}
+
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_INDEX_DEF_H_INCLUDED */
diff --git a/src/box/key_def.cc b/src/box/key_def.cc
index 35ae6740421718cb663cd725e1b43abb51cd0588..1c616ad1a9833d8101b357a33fdd37b76e6a49d7 100644
--- a/src/box/key_def.cc
+++ b/src/box/key_def.cc
@@ -29,48 +29,10 @@
  * SUCH DAMAGE.
  */
 #include "key_def.h"
-
-#include <stdlib.h>
-#include <stdio.h>
-
-#include <msgpuck/msgpuck.h>
-
-#include "trivia/util.h"
-#include "scoped_guard.h"
-
 #include "tuple_compare.h"
 #include "tuple_hash.h"
 #include "column_mask.h"
-
-const char *field_type_strs[] = {
-	/* [FIELD_TYPE_ANY]      = */ "any",
-	/* [FIELD_TYPE_UNSIGNED] = */ "unsigned",
-	/* [FIELD_TYPE_STRING]   = */ "string",
-	/* [FIELD_TYPE_ARRAY]    = */ "array",
-	/* [FIELD_TYPE_NUMBER]   = */ "number",
-	/* [FIELD_TYPE_INTEGER]  = */ "integer",
-	/* [FIELD_TYPE_SCALAR]   = */ "scalar",
-	/* [FIELD_TYPE_MAP]      = */ "map",
-};
-
-enum field_type
-field_type_by_name(const char *name)
-{
-	enum field_type field_type = STR2ENUM(field_type, name);
-	/*
-	 * FIELD_TYPE_ANY can't be used as type of indexed field,
-	 * because it is internal type used only for filling
-	 * struct tuple_format.fields array.
-	 */
-	if (field_type != field_type_MAX && field_type != FIELD_TYPE_ANY)
-		return field_type;
-	/* 'num' and 'str' in _index are deprecated since Tarantool 1.7 */
-	if (strcasecmp(name, "num") == 0)
-		return FIELD_TYPE_UNSIGNED;
-	else if (strcasecmp(name, "str") == 0)
-		return FIELD_TYPE_STRING;
-	return field_type_MAX;
-}
+#include "schema_def.h"
 
 const char *mp_type_strs[] = {
 	/* .MP_NIL    = */ "nil",
@@ -86,12 +48,6 @@ const char *mp_type_strs[] = {
 	/* .MP_EXT    = */ "extension",
 };
 
-const char *index_type_strs[] = { "HASH", "TREE", "BITSET", "RTREE" };
-
-const char *rtree_index_distance_type_strs[] = { "EUCLID", "MANHATTAN" };
-
-const char *func_language_strs[] = {"LUA", "C"};
-
 const uint32_t key_mp_type[] = {
 	/* [FIELD_TYPE_ANY]      =  */ UINT32_MAX,
 	/* [FIELD_TYPE_UNSIGNED] =  */ 1U << MP_UINT,
@@ -105,76 +61,6 @@ const uint32_t key_mp_type[] = {
 		(1U << MP_BIN) | (1U << MP_BOOL) | (1U << MP_NIL),
 };
 
-const char *opt_type_strs[] = {
-	/* [OPT_BOOL]	= */ "boolean",
-	/* [OPT_INT]	= */ "integer",
-	/* [OPT_FLOAT]	= */ "float",
-	/* [OPT_STR]	= */ "string",
-};
-
-const struct index_opts index_opts_default = {
-	/* .unique              = */ true,
-	/* .dimension           = */ 2,
-	/* .distancebuf         = */ { '\0' },
-	/* .distance            = */ RTREE_INDEX_DISTANCE_TYPE_EUCLID,
-	/* .range_size          = */ 0,
-	/* .page_size           = */ 0,
-	/* .run_count_per_level = */ 2,
-	/* .run_size_ratio      = */ 3.5,
-	/* .bloom_fpr           = */ 0.05,
-	/* .lsn                 = */ 0,
-	/* .sql                 = */ NULL,
-};
-
-const struct opt_def index_opts_reg[] = {
-	OPT_DEF("unique", OPT_BOOL, struct index_opts, is_unique),
-	OPT_DEF("dimension", OPT_INT, struct index_opts, dimension),
-	OPT_DEF("distance", OPT_STR, struct index_opts, distancebuf),
-	OPT_DEF("range_size", OPT_INT, struct index_opts, range_size),
-	OPT_DEF("page_size", OPT_INT, struct index_opts, page_size),
-	OPT_DEF("run_count_per_level", OPT_INT, struct index_opts, run_count_per_level),
-	OPT_DEF("run_size_ratio", OPT_FLOAT, struct index_opts, run_size_ratio),
-	OPT_DEF("bloom_fpr", OPT_FLOAT, struct index_opts, bloom_fpr),
-	OPT_DEF("lsn", OPT_INT, struct index_opts, lsn),
-	OPT_DEF("sql", OPT_STRPTR, struct index_opts, sql),
-	{ NULL, opt_type_MAX, 0, 0 },
-};
-
-/**
- * Destructor for struct index_opts.
- * The only relevant action so far is to free sql field if not-null.
- */
-static void
-index_opts_destroy(struct index_opts *opts)
-{
-	if (opts->sql) {
-		free(opts->sql);
-		opts->sql = 0;
-	}
-}
-
-static const char *object_type_strs[] = {
-	"unknown", "universe", "space", "function", "user", "role" };
-
-enum schema_object_type
-schema_object_type(const char *name)
-{
-	/**
-	 * There may be other places in which we look object type by
-	 * name, and they are case-sensitive, so be case-sensitive
-	 * here too.
-	 */
-	int n_strs = sizeof(object_type_strs)/sizeof(*object_type_strs);
-	int index = strindex(object_type_strs, name, n_strs);
-	return (enum schema_object_type) (index == n_strs ? 0 : index);
-}
-
-const char *
-schema_object_name(enum schema_object_type type)
-{
-	return object_type_strs[type];
-}
-
 struct key_def *
 key_def_dup(const struct key_def *src)
 {
@@ -230,181 +116,6 @@ box_key_def_delete(box_key_def_t *key_def)
 	free(key_def);
 }
 
-struct space_def *
-space_def_dup(const struct space_def *src)
-{
-	size_t size = space_def_sizeof(strlen(src->name));
-	struct space_def *ret = (struct space_def *) malloc(size);
-	if (ret == NULL)
-		tnt_raise(OutOfMemory, size, "malloc", "ret");
-	memcpy(ret, src, size);
-	return ret;
-}
-
-struct space_def *
-space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
-	      const char *name, uint32_t name_len,
-	      const char *engine_name, uint32_t engine_len,
-	      const struct space_opts *opts)
-{
-	size_t size = space_def_sizeof(name_len);
-	struct space_def *def = (struct space_def *) malloc(size);
-	if (def == NULL)
-		tnt_raise(OutOfMemory, size, "malloc", "def");
-	assert(name_len <= BOX_NAME_MAX);
-	assert(engine_len <= ENGINE_NAME_MAX);
-	def->id = id;
-	def->uid = uid;
-	def->exact_field_count = exact_field_count;
-	memcpy(def->name, name, name_len);
-	def->name[name_len] = 0;
-	memcpy(def->engine_name, engine_name, engine_len);
-	def->engine_name[engine_len] = 0;
-	def->opts = *opts;
-	return def;
-}
-
-struct index_def *
-index_def_new(uint32_t space_id, uint32_t iid, const char *name,
-	      uint32_t name_len, enum index_type type,
-	      const struct index_opts *opts,
-	      struct key_def *key_def, struct key_def *pk_def)
-{
-	assert(name_len <= BOX_NAME_MAX);
-	/* Use calloc to make index_def_delete() safe at all times. */
-	struct index_def *def = (struct index_def *) calloc(1, sizeof(*def));
-	if (def == NULL) {
-		diag_set(OutOfMemory, sizeof(*def), "malloc", "struct index_def");
-		return NULL;
-	}
-	def->name = strndup(name, name_len);
-	if (def->name == NULL) {
-		index_def_delete(def);
-		diag_set(OutOfMemory, name_len + 1, "malloc", "index_def name");
-		return NULL;
-	}
-	if (!identifier_is_valid(def->name)) {
-		diag_set(ClientError, ER_IDENTIFIER, def->name);
-		index_def_delete(def);
-		return NULL;
-	}
-	def->key_def = key_def_dup(key_def);
-	if (pk_def)
-		def->cmp_def = key_def_merge(key_def, pk_def);
-	else
-		def->cmp_def = key_def_dup(key_def);
-	if (def->key_def == NULL || def->cmp_def == NULL) {
-		index_def_delete(def);
-		return NULL;
-	}
-	def->type = type;
-	def->space_id = space_id;
-	def->iid = iid;
-	def->opts = *opts;
-	return def;
-}
-
-void
-index_opts_dup(struct index_opts *dst, const struct index_opts *src)
-{
-	*dst = *src;
-	if (src->sql) {
-		dst->sql = (char*)strdup(src->sql);
-		if (dst->sql == NULL)
-			diag_set(OutOfMemory, strlen(src->sql), "sql", "char *");
-	}
-}
-
-struct index_def *
-index_def_dup(const struct index_def *def)
-{
-	struct index_def *dup = (struct index_def *) malloc(sizeof(*dup));
-	if (dup == NULL) {
-		diag_set(OutOfMemory, sizeof(*dup), "malloc",
-			 "struct index_def");
-		return NULL;
-	}
-	*dup = *def;
-	dup->name = strdup(def->name);
-	size_t sz = key_def_sizeof(def->key_def->part_count);
-	dup->key_def = (struct key_def *) malloc(sz);
-	if (dup->name == NULL || dup->key_def == NULL) {
-		free(dup->name);
-		free(dup->key_def);
-		free(dup);
-		diag_set(OutOfMemory, strlen(def->name) + 1, "malloc",
-			 "index_def name");
-		return NULL;
-	}
-	dup->key_def = key_def_dup(def->key_def);
-	dup->cmp_def = key_def_dup(def->cmp_def);
-	if (dup->key_def == NULL || dup->cmp_def == NULL) {
-		index_def_delete(dup);
-		return NULL;
-	}
-	rlist_create(&dup->link);
-	index_opts_dup(&dup->opts, &def->opts);
-	return dup;
-}
-
-void
-index_def_swap(struct index_def *def1, struct index_def *def2)
-{
-	/*
-	 * Swap const-size items and name. Keep the original key
-	 * definitions, they are used in the engines.
-	 */
-	struct index_def tmp_def = *def1;
-	memcpy(def1, def2, offsetof(struct index_def, key_def));
-	memcpy(def2, &tmp_def, offsetof(struct index_def, key_def));
-	/*
-	 * index_def_swap() is used only during alter to modify
-	 * index metadata.
-	 */
-}
-
-/** Free a key definition. */
-void
-index_def_delete(struct index_def *index_def)
-{
-	index_opts_destroy(&index_def->opts);
-	free(index_def->name);
-
-	if (index_def->key_def) {
-		TRASH(index_def->key_def);
-		free(index_def->key_def);
-	}
-
-	if (index_def->cmp_def) {
-		TRASH(index_def->cmp_def);
-		free(index_def->cmp_def);
-	}
-
-	TRASH(index_def);
-	free(index_def);
-}
-
-bool
-index_def_change_requires_rebuild(struct index_def *old_index_def,
-				  struct index_def *new_index_def)
-{
-	if (old_index_def->iid != new_index_def->iid ||
-	    old_index_def->type != new_index_def->type ||
-	    old_index_def->opts.is_unique != new_index_def->opts.is_unique ||
-	    key_part_cmp(old_index_def->key_def->parts,
-			 old_index_def->key_def->part_count,
-			 new_index_def->key_def->parts,
-			 new_index_def->key_def->part_count) != 0) {
-		return true;
-	}
-	if (old_index_def->type == RTREE) {
-		if (old_index_def->opts.dimension != new_index_def->opts.dimension
-		    || old_index_def->opts.distance != new_index_def->opts.distance)
-			return true;
-	}
-	return false;
-}
-
 int
 key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 	     const struct key_part *parts2, uint32_t part_count2)
@@ -422,75 +133,6 @@ key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 	return part_count1 < part_count2 ? -1 : part_count1 > part_count2;
 }
 
-int
-index_def_cmp(const struct index_def *key1, const struct index_def *key2)
-{
-	assert(key1->space_id == key2->space_id);
-	if (key1->iid != key2->iid)
-		return key1->iid < key2->iid ? -1 : 1;
-	if (strcmp(key1->name, key2->name))
-		return strcmp(key1->name, key2->name);
-	if (key1->type != key2->type)
-		return (int) key1->type < (int) key2->type ? -1 : 1;
-	if (index_opts_cmp(&key1->opts, &key2->opts))
-		return index_opts_cmp(&key1->opts, &key2->opts);
-
-	return key_part_cmp(key1->key_def->parts, key1->key_def->part_count,
-			    key2->key_def->parts, key2->key_def->part_count);
-}
-
-void
-index_def_check(struct index_def *index_def, const char *space_name)
-
-{
-	if (index_def->iid >= BOX_INDEX_MAX) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name, space_name,
-			  "index id too big");
-	}
-	if (index_def->iid == 0 && index_def->opts.is_unique == false) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name,
-			  space_name,
-			  "primary key must be unique");
-	}
-	if (index_def->key_def->part_count == 0) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name,
-			  space_name,
-			  "part count must be positive");
-	}
-	if (index_def->key_def->part_count > BOX_INDEX_PART_MAX) {
-		tnt_raise(ClientError, ER_MODIFY_INDEX,
-			  index_def->name,
-			  space_name,
-			  "too many key parts");
-	}
-	for (uint32_t i = 0; i < index_def->key_def->part_count; i++) {
-		assert(index_def->key_def->parts[i].type > FIELD_TYPE_ANY &&
-		       index_def->key_def->parts[i].type < field_type_MAX);
-		if (index_def->key_def->parts[i].fieldno > BOX_INDEX_FIELD_MAX) {
-			tnt_raise(ClientError, ER_MODIFY_INDEX,
-				  index_def->name,
-				  space_name,
-				  "field no is too big");
-		}
-		for (uint32_t j = 0; j < i; j++) {
-			/*
-			 * Courtesy to a user who could have made
-			 * a typo.
-			 */
-			if (index_def->key_def->parts[i].fieldno ==
-			    index_def->key_def->parts[j].fieldno) {
-				tnt_raise(ClientError, ER_MODIFY_INDEX,
-					  index_def->name,
-					  space_name,
-					  "same key part is indexed twice");
-			}
-		}
-	}
-}
-
 void
 key_def_set_part(struct key_def *def, uint32_t part_no,
 		 uint32_t fieldno, enum field_type type)
@@ -675,46 +317,3 @@ key_validate_parts(struct key_def *key_def, const char *key,
 	}
 	return 0;
 }
-
-const struct space_opts space_opts_default = {
-	/* .temporary  = */ false,
-	/* .sql        = */ NULL,
-};
-
-const struct opt_def space_opts_reg[] = {
-	OPT_DEF("temporary", OPT_BOOL, struct space_opts, temporary),
-	OPT_DEF("sql", OPT_STRPTR, struct space_opts, sql),
-	{ NULL, opt_type_MAX, 0, 0 }
-};
-
-bool
-identifier_is_valid(const char *str)
-{
-	mbstate_t state;
-	memset(&state, 0, sizeof(state));
-	wchar_t w;
-	ssize_t len = mbrtowc(&w, str, MB_CUR_MAX, &state);
-	if (len <= 0)
-		return false; /* invalid character or zero-length string */
-	if (!iswalpha(w) && w != L'_')
-		return false; /* fail to match [a-zA-Z_] */
-
-	while ((len = mbrtowc(&w, str, MB_CUR_MAX, &state)) > 0) {
-		if (!iswalnum(w) && w != L'_')
-			return false; /* fail to match [a-zA-Z0-9_]* */
-		str += len;
-	}
-
-	if (len < 0)
-		return false; /* invalid character  */
-
-	return true;
-}
-
-void
-identifier_check(const char *str)
-{
-	if (! identifier_is_valid(str))
-		tnt_raise(ClientError, ER_IDENTIFIER, str);
-}
-
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 5c4067f322d31c874ff026f764f8df5f0c2ed392..0695451994cb6127c943b36214921e1143e0c51f 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -31,226 +31,25 @@
  * SUCH DAMAGE.
  */
 #include "trivia/util.h"
-#include "small/rlist.h"
 #include "error.h"
 #include "diag.h"
 #include <msgpuck.h>
-#define RB_COMPACT 1
-#include "small/rb.h"
 #include <limits.h>
-#include <wchar.h>
-#include <wctype.h>
+#include "field_def.h"
 
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
-enum {
-	BOX_ENGINE_MAX = 3, /* + 1 to the actual number of engines */
-	BOX_SPACE_MAX = INT32_MAX,
-	BOX_FUNCTION_MAX = 32000,
-	BOX_INDEX_MAX = 128,
-	BOX_NAME_MAX = 65000,
-	BOX_INVALID_NAME_MAX = 64,
-	ENGINE_NAME_MAX = 16,
-	FIELD_TYPE_NAME_MAX = 16,
-	GRANT_NAME_MAX = 16,
-	BOX_FIELD_MAX = INT32_MAX,
-	BOX_USER_MAX = 32,
-	/**
-	 * A fairly arbitrary limit which is still necessary
-	 * to keep tuple_format object small.
-	 */
-	BOX_INDEX_FIELD_MAX = INT16_MAX,
-	/** Yet another arbitrary limit which simply needs to
-	 * exist.
-	 */
-	BOX_INDEX_PART_MAX = UINT8_MAX
-};
-static_assert(BOX_INVALID_NAME_MAX <= BOX_NAME_MAX,
-	      "invalid name max is less than name max");
-
-/*
- * Different objects which can be subject to access
- * control.
- *
- * Use 0 for unknown to use the same index consistently
- * even when there are more object types in the future.
- */
-enum schema_object_type {
-	SC_UNKNOWN = 0, SC_UNIVERSE = 1, SC_SPACE = 2, SC_FUNCTION = 3,
-	SC_USER = 4, SC_ROLE = 5
-};
-
-enum schema_object_type
-schema_object_type(const char *name);
-
-const char *
-schema_object_name(enum schema_object_type type);
-
-/** \cond public */
-
-/*
- * Possible field data types. Can't use STRS/ENUM macros for them,
- * since there is a mismatch between enum name (STRING) and type
- * name literal ("STR"). STR is already used as Objective C type.
- */
-enum field_type {
-	FIELD_TYPE_ANY = 0,
-	FIELD_TYPE_UNSIGNED,
-	FIELD_TYPE_STRING,
-	FIELD_TYPE_ARRAY,
-	FIELD_TYPE_NUMBER,
-	FIELD_TYPE_INTEGER,
-	FIELD_TYPE_SCALAR,
-	FIELD_TYPE_MAP,
-	field_type_MAX
-};
-
-/** \endcond public */
-
-extern const char *field_type_strs[];
-
 /* MsgPack type names */
 extern const char *mp_type_strs[];
 
-/**
- * The supported language of the stored function.
- */
-enum func_language {
-	FUNC_LANGUAGE_LUA,
-	FUNC_LANGUAGE_C,
-	func_language_MAX,
-};
-extern const char *func_language_strs[];
-
-static inline uint32_t
-field_type_maxlen(enum field_type type)
-{
-	static const uint32_t maxlen[] =
-		{ UINT32_MAX, 8, UINT32_MAX, UINT32_MAX, UINT32_MAX };
-	return maxlen[type];
-}
-
-enum field_type
-field_type_by_name(const char *name);
-
-enum index_type {
-	HASH = 0, /* HASH Index */
-	TREE,     /* TREE Index */
-	BITSET,   /* BITSET Index */
-	RTREE,    /* R-Tree Index */
-	index_type_MAX,
-};
-
-extern const char *index_type_strs[];
-
-enum opt_type {
-	OPT_BOOL,	/* bool */
-	OPT_INT,	/* int64_t */
-	OPT_FLOAT,	/* double */
-	OPT_STR,	/* char[] */
-	OPT_STRPTR,	/* char*, size_t */
-	opt_type_MAX,
-};
-
-extern const char *opt_type_strs[];
-
-struct opt_def {
-	const char *name;
-	enum opt_type type;
-	ptrdiff_t offset;
-	uint32_t len;
-};
-
-#define OPT_DEF(key, type, opts, field) \
-	{ key, type, offsetof(opts, field), sizeof(((opts *)0)->field) }
-
-enum rtree_index_distance_type {
-	 /* Euclid distance, sqrt(dx*dx + dy*dy) */
-	RTREE_INDEX_DISTANCE_TYPE_EUCLID,
-	/* Manhattan distance, fabs(dx) + fabs(dy) */
-	RTREE_INDEX_DISTANCE_TYPE_MANHATTAN,
-	rtree_index_distance_type_MAX
-};
-extern const char *rtree_index_distance_type_strs[];
-
 /** Descriptor of a single part in a multipart key. */
 struct key_part {
 	uint32_t fieldno;
 	enum field_type type;
 };
 
-/** Index options */
-struct index_opts {
-	/**
-	 * Is this index unique or not - relevant to HASH/TREE
-	 * index
-	 */
-	bool is_unique;
-	/**
-	 * RTREE index dimension.
-	 */
-	int64_t dimension;
-	/**
-	 * RTREE distance type.
-	 */
-	char distancebuf[16];
-	enum rtree_index_distance_type distance;
-	/**
-	 * Vinyl index options.
-	 */
-	int64_t range_size;
-	int64_t page_size;
-	/**
-	 * Maximal number of runs that can be created in a level
-	 * of the LSM tree before triggering compaction.
-	 */
-	int64_t run_count_per_level;
-	/**
-	 * The LSM tree multiplier. Each subsequent level of
-	 * the LSM tree is run_size_ratio times larger than
-	 * previous one.
-	 */
-	double run_size_ratio;
-	/* Bloom filter false positive rate. */
-	double bloom_fpr;
-	/**
-	 * LSN from the time of index creation.
-	 */
-	int64_t lsn;
-	/**
-	 * SQL statement that produced this index.
-	 */
-	char *sql;
-};
-
-extern const struct index_opts index_opts_default;
-extern const struct opt_def index_opts_reg[];
-
-static inline int
-index_opts_cmp(const struct index_opts *o1, const struct index_opts *o2)
-{
-	if (o1->is_unique != o2->is_unique)
-		return o1->is_unique < o2->is_unique ? -1 : 1;
-	if (o1->dimension != o2->dimension)
-		return o1->dimension < o2->dimension ? -1 : 1;
-	if (o1->distance != o2->distance)
-		return o1->distance < o2->distance ? -1 : 1;
-	if (o1->range_size != o2->range_size)
-		return o1->range_size < o2->range_size ? -1 : 1;
-	if (o1->page_size != o2->page_size)
-		return o1->page_size < o2->page_size ? -1 : 1;
-	if (o1->run_count_per_level != o2->run_count_per_level)
-		return o1->run_count_per_level < o2->run_count_per_level ?
-		       -1 : 1;
-	if (o1->run_size_ratio != o2->run_size_ratio)
-		return o1->run_size_ratio < o2->run_size_ratio ? -1 : 1;
-	if (o1->bloom_fpr != o2->bloom_fpr)
-		return o1->bloom_fpr < o2->bloom_fpr ? -1 : 1;
-	return 0;
-}
-
 struct key_def;
 struct tuple;
 
@@ -337,235 +136,6 @@ box_key_def_delete(box_key_def_t *key_def);
 
 /** \endcond public */
 
-/* Definition of an index. */
-struct index_def {
-	/* A link in key list. */
-	struct rlist link;
-	/** Ordinal index number in the index array. */
-	uint32_t iid;
-	/* Space id. */
-	uint32_t space_id;
-	/** Index name. */
-	char *name;
-	/** Index type. */
-	enum index_type type;
-	struct index_opts opts;
-
-	/* Index key definition. */
-	struct key_def *key_def;
-	/**
-	 * User-defined key definition, merged with the primary
-	 * key parts. Used by non-unique keys to uniquely identify
-	 * iterator position.
-	 */
-	struct key_def *cmp_def;
-};
-
-struct index_def *
-index_def_dup(const struct index_def *def);
-
-void
-index_def_swap(struct index_def *def1, struct index_def *def2);
-
-/* Destroy and free an index_def. */
-void
-index_def_delete(struct index_def *def);
-
-/**
- * Add an index definition to a list, preserving the
- * first position of the primary key.
- *
- * In non-unique indexes, secondary keys must contain key parts
- * of the primary key. This is necessary to make ordered
- * retrieval from a secondary key useful to SQL
- * optimizer and make iterators over secondary keys stable
- * in presence of concurrent updates.
- * Thus we always create the primary key first, and put
- * the primary key key_def first in the index_def list.
- */
-static inline void
-index_def_list_add(struct rlist *index_def_list, struct index_def *index_def)
-{
-	/** Preserve the position of the primary key */
-	if (index_def->iid == 0)
-		rlist_add_entry(index_def_list, index_def, link);
-	else
-		rlist_add_tail_entry(index_def_list, index_def, link);
-}
-
-/**
- * True, if the index change by alter requires an index rebuild.
- *
- * Some changes, such as a new page size or bloom_fpr do not
- * take effect immediately, so do not require a rebuild.
- *
- * Others, such as index name change, do not change the data, only
- * metadata, so do not require a rebuild either.
- *
- * Finally, changing index type or number of parts always requires
- * a rebuild.
- */
-bool
-index_def_change_requires_rebuild(struct index_def *old_index_def,
-				  struct index_def *new_index_def);
-
-/**
- * Encapsulates privileges of a user on an object.
- * I.e. "space" object has an instance of this
- * structure for each user.
- */
-struct access {
-	/**
-	 * Granted access has been given to a user explicitly
-	 * via some form of a grant.
-	 */
-	uint8_t granted;
-	/**
-	 * Effective access is a sum of granted access and
-	 * all privileges inherited by a user on this object
-	 * via some role. Since roles may be granted to other
-	 * roles, this may include indirect grants.
-	 */
-	uint8_t effective;
-};
-
-/**
- * Effective session user. A cache of user data
- * and access stored in session and fiber local storage.
- * Differs from the authenticated user when executing
- * setuid functions.
- */
-struct credentials {
-	/** A look up key to quickly find session user. */
-	uint8_t auth_token;
-	/**
-	 * Cached global grants, to avoid an extra look up
-	 * when checking global grants.
-	 */
-	uint8_t universal_access;
-	/** User id of the authenticated user. */
-	uint32_t uid;
-};
-
-/**
- * Definition of a function. Function body is not stored
- * or replicated (yet).
- */
-struct func_def {
-	/** Function id. */
-	uint32_t fid;
-	/** Owner of the function. */
-	uint32_t uid;
-	/**
-	 * True if the function requires change of user id before
-	 * invocation.
-	 */
-	bool setuid;
-	/**
-	 * The language of the stored function.
-	 */
-	enum func_language language;
-	/** Function name. */
-	char name[0];
-};
-
-/**
- * @param name_len length of func_def->name
- * @returns size in bytes needed to allocate for struct func_def
- * for a function of length @a a name_len.
- */
-static inline size_t
-func_def_sizeof(uint32_t name_len)
-{
-	/* +1 for '\0' name terminating. */
-	return sizeof(struct func_def) + name_len + 1;
-}
-
-/**
- * Definition of a privilege
- */
-struct priv_def {
-	/** Who grants the privilege. */
-	uint32_t grantor_id;
-	/** Whom the privilege is granted. */
-	uint32_t grantee_id;
-	/* Object id - is only defined for object type */
-	uint32_t object_id;
-	/* Object type - function, space, universe */
-	enum schema_object_type object_type;
-	/**
-	 * What is being granted, has been granted, or is being
-	 * revoked.
-	 */
-	uint8_t access;
-	/** To maintain a set of effective privileges. */
-	rb_node(struct priv_def) link;
-};
-
-/** Space options */
-struct space_opts {
-        /**
-	 * The space is a temporary:
-	 * - it is empty at server start
-	 * - changes are not written to WAL
-	 * - changes are not part of a snapshot
-	 */
-	bool temporary;
-	/**
-	 * SQL statement that produced this space.
-	 */
-	const char *sql;
-};
-
-extern const struct space_opts space_opts_default;
-extern const struct opt_def space_opts_reg[];
-
-/** Space metadata. */
-struct space_def {
-	/** Space id. */
-	uint32_t id;
-	/** User id of the creator of the space */
-	uint32_t uid;
-	/**
-	 * If not set (is 0), any tuple in the
-	 * space can have any number of fields.
-	 * If set, each tuple
-	 * must have exactly this many fields.
-	 */
-	uint32_t exact_field_count;
-	char engine_name[ENGINE_NAME_MAX + 1];
-	struct space_opts opts;
-	char name[0];
-};
-
-/**
- * Size of the space_def, calculated using its name.
- * @param name_len Length of the space name.
- * @retval Size in bytes.
- */
-static inline size_t
-space_def_sizeof(uint32_t name_len)
-{
-	return sizeof(struct space_def) + name_len + 1;
-}
-
-/**
- * Delete the space_def object.
- * @param def Def to delete.
- */
-static inline void
-space_def_delete(struct space_def *def)
-{
-	free(def);
-}
-
-/**
- * API of C stored function.
- */
-typedef struct box_function_ctx box_function_ctx_t;
-typedef int (*box_function_f)(box_function_ctx_t *ctx,
-	     const char *args, const char *args_end);
-
 static inline size_t
 key_def_sizeof(uint32_t part_count)
 {
@@ -578,22 +148,6 @@ key_def_sizeof(uint32_t part_count)
 struct key_def *
 key_def_new(uint32_t part_count);
 
-/**
- * Create a new index definition definition.
- *
- * @param key_def  key definition, must be fully built
- * @param pk_def   primary key definition, pass non-NULL
- *                 for secondary keys to construct
- *                 index_def::cmp_def
- * @retval not NULL Success.
- * @retval NULL     Memory error.
- */
-struct index_def *
-index_def_new(uint32_t space_id, uint32_t iid, const char *name,
-	      uint32_t name_len, enum index_type type,
-	      const struct index_opts *opts,
-	      struct key_def *key_def, struct key_def *pk_def);
-
 /**
  * Set a single key part in a key def.
  * @pre part_no < part_count
@@ -706,37 +260,6 @@ key_mp_type_validate(enum field_type key_type, enum mp_type mp_type,
 	return 0;
 }
 
-#if defined(__cplusplus)
-} /* extern "C" */
-
-/**
- * Duplicate space_def object.
- * @param src Def to duplicate.
- * @retval Copy of the @src.
- */
-struct space_def *
-space_def_dup(const struct space_def *src); /* throws */
-
-/**
- * Create a new space definition.
- * @param id Space identifier.
- * @param uid Owner identifier.
- * @param exact_field_count Space tuples field count.
- *        0 for any count.
- * @param name Space name.
- * @param name_len Length of the @name.
- * @param engine_name Engine name.
- * @param engine_len Length of the @engine.
- * @param opts Space options.
- *
- * @retval Space definition.
- */
-struct space_def *
-space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
-	      const char *name, uint32_t name_len,
-	      const char *engine_name, uint32_t engine_len,
-	      const struct space_opts *opts); /* throws */
-
 /** Compare two key part arrays.
  *
  * This function is used to find out whether alteration
@@ -748,7 +271,6 @@ space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
  * One key part is considered to be greater than the other if:
  * - its fieldno is greater
  * - given the same fieldno, NUM < STRING
- *   (coarsely speaking, based on field_type_maxlen()).
  *
  * A key part array is considered greater than the other if all
  * its key parts are greater, or, all common key parts are equal
@@ -758,46 +280,8 @@ int
 key_part_cmp(const struct key_part *parts1, uint32_t part_count1,
 	     const struct key_part *parts2, uint32_t part_count2);
 
-/**
- * One key definition is greater than the other if it's id is
- * greater, it's name is greater,  it's index type is greater
- * (HASH < TREE < BITSET) or its key part array is greater.
- */
-int
-index_def_cmp(const struct index_def *key1, const struct index_def *key2);
-
-/**
- * Check a key definition for violation of various limits.
- *
- * @param index_def   index definition
- * @param old_space   space definition
- */
-void
-index_def_check(struct index_def *index_def, const char *space_name);
-
-/**
- * Check object identifier for invalid symbols.
- * The function checks \a str for matching [a-zA-Z_][a-zA-Z0-9_]* expression.
- * Result is locale-dependent.
- */
-bool
-identifier_is_valid(const char *str);
-
-/**
- * Throw an error if identifier is not valid.
- */
-void
-identifier_check(const char *str);
-
-static inline struct index_def *
-index_def_dup_xc(const struct index_def *def)
-{
-	struct index_def *ret = index_def_dup(def);
-	if (ret == NULL)
-		diag_raise();
-	return ret;
-}
-
+#if defined(__cplusplus)
+} /* extern "C" */
 #endif /* defined(__cplusplus) */
 
 #endif /* TARANTOOL_BOX_KEY_DEF_H_INCLUDED */
diff --git a/src/box/lua/info.c b/src/box/lua/info.c
index 9ce80c022a19ed63ffbda9b0bac1711658bfdf5e..f3343752a6daf4353ada84e7257286ee8116ccfd 100644
--- a/src/box/lua/info.c
+++ b/src/box/lua/info.c
@@ -88,7 +88,8 @@ lbox_pushapplier(lua_State *L, struct applier *applier)
 		lua_settable(L, -3);
 
 		lua_pushstring(L, "idle");
-		lua_pushnumber(L, ev_now(loop()) - applier->last_row_time);
+		lua_pushnumber(L, ev_monotonic_now(loop()) -
+			       applier->last_row_time);
 		lua_settable(L, -3);
 
 		struct error *e = diag_last_error(&applier->reader->diag);
diff --git a/src/box/lua/load_cfg.lua b/src/box/lua/load_cfg.lua
index 601ebf590b08ee5940a7172d4a10a74052bd4615..3a546cec7af9f17da1403590f878ad5e5f8d542f 100644
--- a/src/box/lua/load_cfg.lua
+++ b/src/box/lua/load_cfg.lua
@@ -51,7 +51,8 @@ local default_cfg = {
     hot_standby         = false,
     checkpoint_interval = 3600,
     checkpoint_count    = 2,
-    worker_pool_threads = 4
+    worker_pool_threads = 4,
+    replication_timeout = 10,
 }
 
 -- types of available options
@@ -100,7 +101,8 @@ local template_cfg = {
     checkpoint_count    = 'number',
     read_only           = 'boolean',
     hot_standby         = 'boolean',
-    worker_pool_threads = 'number'
+    worker_pool_threads = 'number',
+    replication_timeout = 'number',
 }
 
 local function normalize_uri(port)
@@ -165,6 +167,7 @@ local dynamic_cfg = {
         require('title').update(box.cfg.custom_proc_title)
     end,
     force_recovery          = function() end,
+    replication_timeout     = function() end,
 }
 
 local dynamic_cfg_skip_at_load = {
diff --git a/src/box/lua/net_box.c b/src/box/lua/net_box.c
index 49f880e4c889ecf942d1620347aac5304ea76c21..efb391379d0baa3bce5a3c700d92cbb9530f87fd 100644
--- a/src/box/lua/net_box.c
+++ b/src/box/lua/net_box.c
@@ -536,11 +536,11 @@ netbox_communicate(lua_State *L)
 				goto handle_error;
 		}
 
-		ev_tstamp deadline = fiber_time() + timeout;
+		ev_tstamp deadline = ev_monotonic_now(loop()) + timeout;
 		revents = coio_wait(fd, EV_READ | (ibuf_used(send_buf) != 0 ?
 				EV_WRITE : 0), timeout);
 		luaL_testcancel(L);
-		timeout = deadline - fiber_time();
+		timeout = deadline - ev_monotonic_now(loop());
 		timeout = MAX(0.0, timeout);
 		if (revents == 0 && timeout == 0.0) {
 			lua_pushinteger(L, ER_TIMEOUT);
diff --git a/src/box/lua/net_box.lua b/src/box/lua/net_box.lua
index ec00fe2dcb432256f94504b9aaa1747d5663331a..390989c69c620a88f73811f221e3c8877e5a3bc3 100644
--- a/src/box/lua/net_box.lua
+++ b/src/box/lua/net_box.lua
@@ -12,7 +12,7 @@ local trigger  = require('internal.trigger')
 
 local band          = bit.band
 local max           = math.max
-local fiber_time    = fiber.time
+local fiber_clock   = fiber.clock
 local fiber_self    = fiber.self
 local ibuf_decode   = msgpack.ibuf_decode
 
@@ -182,10 +182,10 @@ local function create_transport(host, port, user, password, callback)
 
     -- FYI: [] on a string is valid
     local function wait_state(target_state, timeout)
-        local deadline = fiber_time() + (timeout or TIMEOUT_INFINITY)
+        local deadline = fiber_clock() + (timeout or TIMEOUT_INFINITY)
         repeat until state == target_state or target_state[state] or
                      is_final_state[state] or
-                     not state_cond:wait(max(0, deadline - fiber_time()))
+                     not state_cond:wait(max(0, deadline - fiber_clock()))
         return state == target_state or target_state[state] or false
     end
 
@@ -228,7 +228,7 @@ local function create_transport(host, port, user, password, callback)
         if state ~= 'active' then
             return last_errno or E_NO_CONNECTION, last_error
         end
-        local deadline = fiber_time() + (timeout or TIMEOUT_INFINITY)
+        local deadline = fiber_clock() + (timeout or TIMEOUT_INFINITY)
         -- alert worker to notify it of the queued outgoing data;
         -- if the buffer wasn't empty, assume the worker was already alerted
         if send_buf:size() == 0 then
@@ -247,7 +247,7 @@ local function create_transport(host, port, user, password, callback)
         request.buffer = buffer
         requests[id] = request
         repeat
-            local timeout = max(0, deadline - fiber_time())
+            local timeout = max(0, deadline - fiber_clock())
             if not state_cond:wait(timeout) then
                 requests[id] = nil
                 return E_TIMEOUT, 'Timeout exceeded'
@@ -331,12 +331,12 @@ local function create_transport(host, port, user, password, callback)
                 return nil, hdr, body_rpos, body_end
             end
         end
-        local deadline = fiber_time() + (timeout or TIMEOUT_INFINITY)
+        local deadline = fiber_clock() + (timeout or TIMEOUT_INFINITY)
         local err, extra = send_and_recv(required, timeout)
         if err then
             return err, extra
         end
-        return send_and_recv_iproto(max(0, deadline - fiber_time()))
+        return send_and_recv_iproto(max(0, deadline - fiber_clock()))
     end
 
     local function send_and_recv_console(timeout)
@@ -360,13 +360,13 @@ local function create_transport(host, port, user, password, callback)
     local console_sm, iproto_auth_sm, iproto_schema_sm, iproto_sm, error_sm
 
     protocol_sm = function ()
-        local tm_begin, tm = fiber.time(), callback('fetch_connect_timeout')
+        local tm_begin, tm = fiber.clock(), callback('fetch_connect_timeout')
         connection = socket.tcp_connect(host, port, tm)
         if connection == nil then
             return error_sm(E_NO_CONNECTION, errno.strerror(errno()))
         end
         local size = IPROTO_GREETING_SIZE
-        local err, msg = send_and_recv(size, tm - (fiber.time() - tm_begin))
+        local err, msg = send_and_recv(size, tm - (fiber.clock() - tm_begin))
         if err then
             return error_sm(err, msg)
         end
@@ -690,7 +690,7 @@ end
 
 function remote_methods:is_connected()
     check_remote_arg(self, 'is_connected')
-    return self.state == 'active'
+    return self.state == 'active' or self.state == 'fetch_schema'
 end
 
 function remote_methods:wait_connected(timeout)
@@ -704,7 +704,7 @@ function remote_methods:request_timeout(request_opts)
         -- conn:timeout(timeout):ping()
         -- @deprecated since 1.7.4
         local deadline = self._deadlines[fiber_self()]
-        timeout = deadline and max(0, deadline - fiber_time())
+        timeout = deadline and max(0, deadline - fiber_clock())
                             or (request_opts and request_opts.timeout)
     end
     return timeout
@@ -718,7 +718,7 @@ function remote_methods:_request(method, opts, ...)
     local deadline = nil
     if opts and opts.timeout then
         -- conn.space:request(, { timeout = timeout })
-        deadline = fiber_time() + opts.timeout
+        deadline = fiber_clock() + opts.timeout
     else
         -- conn:timeout(timeout).space:request()
         -- @deprecated since 1.7.4
@@ -727,10 +727,10 @@ function remote_methods:_request(method, opts, ...)
     local buffer = opts and opts.buffer
     local err, res
     repeat
-        local timeout = deadline and max(0, deadline - fiber_time())
+        local timeout = deadline and max(0, deadline - fiber_clock())
         if self.state ~= 'active' then
             wait_state('active', timeout)
-            timeout = deadline and max(0, deadline - fiber_time())
+            timeout = deadline and max(0, deadline - fiber_clock())
         end
         err, res = perform_request(timeout, buffer, method,
                                    self.schema_version, ...)
@@ -838,7 +838,7 @@ function remote_methods:wait_state(state, timeout)
     check_remote_arg(self, 'wait_state')
     if timeout == nil then
         local deadline = self._deadlines[fiber_self()]
-        timeout = deadline and max(0, deadline-fiber_time())
+        timeout = deadline and max(0, deadline - fiber_clock())
     end
     return self._transport.wait_state(state, timeout)
 end
@@ -854,7 +854,7 @@ function remote_methods:timeout(timeout)
                  "please use space:<request>(..., {timeout = t}) instead.")
     end
     -- Sic: this is broken by design
-    self._deadlines[fiber_self()] = (timeout and fiber_time() + timeout)
+    self._deadlines[fiber_self()] = (timeout and fiber_clock() + timeout)
     return self
 end
 
@@ -957,9 +957,9 @@ function console_methods:eval(line, timeout)
     local transport = self._transport
     local pr = transport.perform_request
     if self.state ~= 'active' then
-        local deadline = fiber_time() + (timeout or TIMEOUT_INFINITY)
+        local deadline = fiber_clock() + (timeout or TIMEOUT_INFINITY)
         transport.wait_state('active', timeout)
-        timeout = max(0, deadline - fiber_time())
+        timeout = max(0, deadline - fiber_clock())
     end
     if self.protocol == 'Binary' then
         local loader = 'return require("console").eval(...)'
diff --git a/src/opts.c b/src/box/opt_def.c
similarity index 79%
rename from src/opts.c
rename to src/box/opt_def.c
index ee38079aa89a6ffe0839bad25db303a6b4f67e22..7296759725d183e275d0c9479598fe69648db09a 100644
--- a/src/opts.c
+++ b/src/box/opt_def.c
@@ -28,15 +28,13 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "trivia/config.h"
-#include <stddef.h>
 
-#include <third_party/gopt/gopt.h>
+#include "opt_def.h"
 
-const void *opt_def =
-	gopt_start(
-		   gopt_option('h', 0, gopt_shorts('h', '?'), gopt_longs("help"),
-			       NULL, "display this help and exit"),
-		   gopt_option('V', 0, gopt_shorts('V'), gopt_longs("version"),
-			       NULL, "print program version and exit")
-	);
+const char *opt_type_strs[] = {
+	/* [OPT_BOOL]	= */ "boolean",
+	/* [OPT_INT]	= */ "integer",
+	/* [OPT_FLOAT]	= */ "float",
+	/* [OPT_STR]	= */ "string",
+	/* [OPT_STRPTR] = */ "string*",
+};
diff --git a/src/box/opt_def.h b/src/box/opt_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..8e2d84f1a787ee575f98d39b66d161fe09ec1f95
--- /dev/null
+++ b/src/box/opt_def.h
@@ -0,0 +1,58 @@
+#ifndef TARANTOOL_BOX_OPT_DEF_H_INCLUDED
+#define TARANTOOL_BOX_OPT_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "trivia/util.h"
+#include <stddef.h>
+
+enum opt_type {
+	OPT_BOOL,	/* bool */
+	OPT_INT,	/* int64_t */
+	OPT_FLOAT,	/* double */
+	OPT_STR,	/* char[] */
+	OPT_STRPTR,	/* char*, size_t */
+	opt_type_MAX,
+};
+
+extern const char *opt_type_strs[];
+
+struct opt_def {
+	const char *name;
+	enum opt_type type;
+	ptrdiff_t offset;
+	uint32_t len;
+};
+
+#define OPT_DEF(key, type, opts, field) \
+	{ key, type, offsetof(opts, field), sizeof(((opts *)0)->field) }
+
+#endif /* TARANTOOL_BOX_OPT_DEF_H_INCLUDED */
diff --git a/src/box/relay.cc b/src/box/relay.cc
index 7ced6ab2eb0a48b7e11d03b681eda0683a347e5b..0db662be59613d303c8cfd199b763655a473ff07 100644
--- a/src/box/relay.cc
+++ b/src/box/relay.cc
@@ -108,6 +108,8 @@ struct relay {
 	struct vclock recv_vclock;
 	/** Replicatoin slave version. */
 	uint32_t version_id;
+	/** Timeout for clients ack. */
+	double timeout;
 
 	/** Relay endpoint */
 	struct cbus_endpoint endpoint;
@@ -307,7 +309,8 @@ relay_reader_f(va_list ap)
 	try {
 		while (!fiber_is_cancelled()) {
 			struct xrow_header xrow;
-			coio_read_xrow(&io, &ibuf, &xrow);
+			coio_read_xrow_timeout_xc(&io, &ibuf, &xrow,
+						  relay->timeout);
 			/* vclock is followed while decoding, zeroing it. */
 			vclock_create(&relay->recv_vclock);
 			xrow_decode_vclock_xc(&xrow, &relay->recv_vclock);
@@ -382,15 +385,19 @@ relay_subscribe_f(va_list ap)
 		 */
 		if (relay->status_msg.msg.route != NULL)
 			continue;
+		struct vclock *send_vclock;
+		if (relay->version_id < version_id(1, 7, 4))
+			send_vclock = &r->vclock;
+		else
+			send_vclock = &relay->recv_vclock;
+		if (vclock_sum(&relay->status_msg.vclock) ==
+		    vclock_sum(send_vclock))
+			continue;
 		static const struct cmsg_hop route[] = {
 			{tx_status_update, NULL}
 		};
 		cmsg_init(&relay->status_msg.msg, route);
-		if (relay->version_id < version_id(1, 7, 4))
-			vclock_copy(&relay->status_msg.vclock, &r->vclock);
-		else
-			vclock_copy(&relay->status_msg.vclock,
-				    &relay->recv_vclock);
+		vclock_copy(&relay->status_msg.vclock, send_vclock);
 		relay->status_msg.relay = relay;
 		cpipe_push(&relay->tx_pipe, &relay->status_msg.msg);
 	}
@@ -445,6 +452,7 @@ relay_subscribe(int fd, uint64_t sync, struct replica *replica,
 	vclock_copy(&relay.tx.vclock, replica_clock);
 	relay.version_id = replica_version_id;
 	relay.replica = replica;
+	relay.timeout = cfg_getd("replication_timeout");
 	replica_set_relay(replica, &relay);
 
 	int rc = cord_costart(&relay.cord, tt_sprintf("relay_%p", &relay),
diff --git a/src/box/replication.cc b/src/box/replication.cc
index 30aa6fad3d8f7af9751452488fd5f1d8920cafb0..fea8e825adfb2f6edf97fa45ea626e980ed8d166 100644
--- a/src/box/replication.cc
+++ b/src/box/replication.cc
@@ -76,6 +76,7 @@ replication_init(void)
 {
 	mempool_create(&replica_pool, &cord()->slabc,
 		       sizeof(struct replica));
+	memset(&replicaset, 0, sizeof(replicaset));
 	replicaset_new(&replicaset);
 	vclock_create(&replicaset_vclock);
 }
@@ -183,6 +184,7 @@ void
 replicaset_update(struct applier **appliers, int count)
 {
 	replicaset_t uniq;
+	memset(&uniq, 0, sizeof(uniq));
 	replicaset_new(&uniq);
 	struct replica *replica, *next;
 
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 22e128daa9d1052df639d465c3a2d4105fd352d2..01d6beca034e2deeeafa15f81131d01770241ad6 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -37,7 +37,6 @@
 #include "assoc.h"
 #include "lua/utils.h"
 #include "lua/space.h"
-#include "key_def.h"
 #include "alter.h"
 #include "scoped_guard.h"
 #include <stdio.h>
@@ -191,8 +190,8 @@ sc_space_new(uint32_t id, const char *name, struct key_def *key_def,
 	auto index_def_guard =
 		make_scoped_guard([=] { index_def_delete(index_def); });
 	struct space_def *def =
-		space_def_new(id, ADMIN, 0, name, strlen(name), "memtx",
-			      strlen("memtx"), &space_opts_default);
+		space_def_new_xc(id, ADMIN, 0, name, strlen(name), "memtx",
+				 strlen("memtx"), &space_opts_default);
 	auto def_guard = make_scoped_guard([=] { space_def_delete(def); });
 	struct rlist key_list;
 	rlist_create(&key_list);
diff --git a/src/box/schema.h b/src/box/schema.h
index 6c5e0d97d14b6bc6b810234ab6026c697914da84..091b8253302f15ec05baf298d875a46527b6f610 100644
--- a/src/box/schema.h
+++ b/src/box/schema.h
@@ -31,125 +31,20 @@
  * SUCH DAMAGE.
  */
 
-/** \cond public */
-enum {
-	/** Start of the reserved range of system spaces. */
-	BOX_SYSTEM_ID_MIN = 256,
-	/** Space id of _schema. */
-	BOX_SCHEMA_ID = 272,
-	/** Space id of _space. */
-	BOX_SPACE_ID = 280,
-	/** Space id of _vspace view. */
-	BOX_VSPACE_ID = 281,
-	/** Space id of _index. */
-	BOX_INDEX_ID = 288,
-	/** Space id of _vindex view. */
-	BOX_VINDEX_ID = 289,
-	/** Space id of _func. */
-	BOX_FUNC_ID = 296,
-	/** Space id of _vfunc view. */
-	BOX_VFUNC_ID = 297,
-	/** Space id of _user. */
-	BOX_USER_ID = 304,
-	/** Space id of _vuser view. */
-	BOX_VUSER_ID = 305,
-	/** Space id of _priv. */
-	BOX_PRIV_ID = 312,
-	/** Space id of _vpriv view. */
-	BOX_VPRIV_ID = 313,
-	/** Space id of _cluster. */
-	BOX_CLUSTER_ID = 320,
-	/** Space id of _trigger. */
-	BOX_TRIGGER_ID = 328,
-	/** Space id of _truncate. */
-	BOX_TRUNCATE_ID = 330,
-	/** End of the reserved range of system spaces. */
-	BOX_SYSTEM_ID_MAX = 511,
-	BOX_ID_NIL = 2147483647
-};
-/** \endcond public */
-
-/** _space fields. */
-enum {
-	BOX_SPACE_FIELD_ID = 0,
-	BOX_SPACE_FIELD_UID = 1,
-	BOX_SPACE_FIELD_NAME = 2,
-	BOX_SPACE_FIELD_ENGINE = 3,
-	BOX_SPACE_FIELD_FIELD_COUNT = 4,
-	BOX_SPACE_FIELD_OPTS = 5,
-};
-
-/** _index fields. */
-enum {
-	BOX_INDEX_FIELD_SPACE_ID = 0,
-	BOX_INDEX_FIELD_ID = 1,
-	BOX_INDEX_FIELD_NAME = 2,
-	BOX_INDEX_FIELD_TYPE = 3,
-	BOX_INDEX_FIELD_OPTS = 4,
-	BOX_INDEX_FIELD_PARTS = 5,
-};
-
-/** _user fields. */
-enum {
-	BOX_USER_FIELD_ID = 0,
-	BOX_USER_FIELD_UID = 1,
-	BOX_USER_FIELD_NAME = 2,
-	BOX_USER_FIELD_TYPE = 3,
-	BOX_USER_FIELD_AUTH_MECH_LIST = 4,
-};
-
-/** _priv fields. */
-enum {
-	BOX_PRIV_FIELD_ID = 0,
-	BOX_PRIV_FIELD_UID = 1,
-	BOX_PRIV_FIELD_OBJECT_TYPE = 2,
-	BOX_PRIV_FIELD_OBJECT_ID = 3,
-	BOX_PRIV_FIELD_ACCESS = 4,
-};
-
-/** _func fields. */
-enum {
-	BOX_FUNC_FIELD_ID = 0,
-	BOX_FUNC_FIELD_UID = 1,
-	BOX_FUNC_FIELD_NAME = 2,
-	BOX_FUNC_FIELD_SETUID = 3,
-	BOX_FUNC_FIELD_LANGUAGE = 4,
-};
-
-/** _schema fields. */
-enum {
-	BOX_SCHEMA_FIELD_KEY = 0,
-};
-
-/** _cluster fields. */
-enum {
-	BOX_CLUSTER_FIELD_ID = 0,
-	BOX_CLUSTER_FIELD_UUID = 1,
-};
-
-/** _truncate fields. */
-enum {
-	BOX_TRUNCATE_FIELD_SPACE_ID = 0,
-	BOX_TRUNCATE_FIELD_COUNT = 1,
-};
-
 #include <stdint.h>
-
-extern uint32_t schema_version;
-
-#if defined(__cplusplus)
-
-#include "error.h"
 #include <stdio.h> /* snprintf */
+#include "error.h"
 #include "space.h"
 #include "latch.h"
 
+extern uint32_t schema_version;
+
 /**
  * Lock of schema modification
  */
 extern struct latch schema_lock;
 
-struct space;
+#if defined(__cplusplus)
 
 /** Call a visitor function on every space in the space cache. */
 void
@@ -253,6 +148,7 @@ func_by_name(const char *name, uint32_t name_len);
  */
 bool
 schema_find_grants(const char *type, uint32_t id);
+
 #endif /* defined(__cplusplus) */
 
 #endif /* INCLUDES_TARANTOOL_BOX_SCHEMA_H */
diff --git a/src/box/schema_def.c b/src/box/schema_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..0907292669dff49cf90ac949769f38b2e4bb19cf
--- /dev/null
+++ b/src/box/schema_def.c
@@ -0,0 +1,79 @@
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "schema_def.h"
+#include <wchar.h>
+#include <wctype.h>
+
+static const char *object_type_strs[] = {
+	"unknown", "universe", "space", "function", "user", "role" };
+
+enum schema_object_type
+schema_object_type(const char *name)
+{
+	/**
+	 * There may be other places in which we look object type by
+	 * name, and they are case-sensitive, so be case-sensitive
+	 * here too.
+	 */
+	int n_strs = sizeof(object_type_strs)/sizeof(*object_type_strs);
+	int index = strindex(object_type_strs, name, n_strs);
+	return (enum schema_object_type) (index == n_strs ? 0 : index);
+}
+
+const char *
+schema_object_name(enum schema_object_type type)
+{
+	return object_type_strs[type];
+}
+
+bool
+identifier_is_valid(const char *str)
+{
+	mbstate_t state;
+	memset(&state, 0, sizeof(state));
+	wchar_t w;
+	ssize_t len = mbrtowc(&w, str, MB_CUR_MAX, &state);
+	if (len <= 0)
+		return false; /* invalid character or zero-length string */
+	if (!iswalpha(w) && w != L'_')
+		return false; /* fail to match [a-zA-Z_] */
+
+	while ((len = mbrtowc(&w, str, MB_CUR_MAX, &state)) > 0) {
+		if (!iswalnum(w) && w != L'_')
+			return false; /* fail to match [a-zA-Z0-9_]* */
+		str += len;
+	}
+
+	if (len < 0)
+		return false; /* invalid character  */
+
+	return true;
+}
diff --git a/src/box/schema_def.h b/src/box/schema_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..dd18bf8d5e5224e9120b290162917f116cf2df55
--- /dev/null
+++ b/src/box/schema_def.h
@@ -0,0 +1,213 @@
+#ifndef TARANTOOL_BOX_SCHEMA_DEF_H_INCLUDED
+#define TARANTOOL_BOX_SCHEMA_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "trivia/util.h"
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+enum {
+	BOX_ENGINE_MAX = 3, /* + 1 to the actual number of engines */
+	BOX_SPACE_MAX = INT32_MAX,
+	BOX_FUNCTION_MAX = 32000,
+	BOX_INDEX_MAX = 128,
+	BOX_NAME_MAX = 65000,
+	BOX_INVALID_NAME_MAX = 64,
+	ENGINE_NAME_MAX = 16,
+	FIELD_TYPE_NAME_MAX = 16,
+	GRANT_NAME_MAX = 16,
+	BOX_FIELD_MAX = INT32_MAX,
+	BOX_USER_MAX = 32,
+	/**
+	 * A fairly arbitrary limit which is still necessary
+	 * to keep tuple_format object small.
+	 */
+	BOX_INDEX_FIELD_MAX = INT16_MAX,
+	/** Yet another arbitrary limit which simply needs to
+	 * exist.
+	 */
+	BOX_INDEX_PART_MAX = UINT8_MAX
+};
+static_assert(BOX_INVALID_NAME_MAX <= BOX_NAME_MAX,
+	      "invalid name max is less than name max");
+
+/** \cond public */
+enum {
+	/** Start of the reserved range of system spaces. */
+	BOX_SYSTEM_ID_MIN = 256,
+	/** Space id of _schema. */
+	BOX_SCHEMA_ID = 272,
+	/** Space id of _space. */
+	BOX_SPACE_ID = 280,
+	/** Space id of _vspace view. */
+	BOX_VSPACE_ID = 281,
+	/** Space id of _index. */
+	BOX_INDEX_ID = 288,
+	/** Space id of _vindex view. */
+	BOX_VINDEX_ID = 289,
+	/** Space id of _func. */
+	BOX_FUNC_ID = 296,
+	/** Space id of _vfunc view. */
+	BOX_VFUNC_ID = 297,
+	/** Space id of _user. */
+	BOX_USER_ID = 304,
+	/** Space id of _vuser view. */
+	BOX_VUSER_ID = 305,
+	/** Space id of _priv. */
+	BOX_PRIV_ID = 312,
+	/** Space id of _vpriv view. */
+	BOX_VPRIV_ID = 313,
+	/** Space id of _cluster. */
+	BOX_CLUSTER_ID = 320,
+	/** Space id of _trigger. */
+	BOX_TRIGGER_ID = 328,
+	/** Space id of _truncate. */
+	BOX_TRUNCATE_ID = 330,
+	/** End of the reserved range of system spaces. */
+	BOX_SYSTEM_ID_MAX = 511,
+	BOX_ID_NIL = 2147483647
+};
+/** \endcond public */
+
+/** _space fields. */
+enum {
+	BOX_SPACE_FIELD_ID = 0,
+	BOX_SPACE_FIELD_UID = 1,
+	BOX_SPACE_FIELD_NAME = 2,
+	BOX_SPACE_FIELD_ENGINE = 3,
+	BOX_SPACE_FIELD_FIELD_COUNT = 4,
+	BOX_SPACE_FIELD_OPTS = 5,
+};
+
+/** _index fields. */
+enum {
+	BOX_INDEX_FIELD_SPACE_ID = 0,
+	BOX_INDEX_FIELD_ID = 1,
+	BOX_INDEX_FIELD_NAME = 2,
+	BOX_INDEX_FIELD_TYPE = 3,
+	BOX_INDEX_FIELD_OPTS = 4,
+	BOX_INDEX_FIELD_IS_UNIQUE_165 = 4,
+	BOX_INDEX_FIELD_PARTS = 5,
+	BOX_INDEX_FIELD_PART_COUNT_165 = 5,
+	BOX_INDEX_FIELD_PARTS_165 = 6,
+};
+
+/** _user fields. */
+enum {
+	BOX_USER_FIELD_ID = 0,
+	BOX_USER_FIELD_UID = 1,
+	BOX_USER_FIELD_NAME = 2,
+	BOX_USER_FIELD_TYPE = 3,
+	BOX_USER_FIELD_AUTH_MECH_LIST = 4,
+};
+
+/** _priv fields. */
+enum {
+	BOX_PRIV_FIELD_ID = 0,
+	BOX_PRIV_FIELD_UID = 1,
+	BOX_PRIV_FIELD_OBJECT_TYPE = 2,
+	BOX_PRIV_FIELD_OBJECT_ID = 3,
+	BOX_PRIV_FIELD_ACCESS = 4,
+};
+
+/** _func fields. */
+enum {
+	BOX_FUNC_FIELD_ID = 0,
+	BOX_FUNC_FIELD_UID = 1,
+	BOX_FUNC_FIELD_NAME = 2,
+	BOX_FUNC_FIELD_SETUID = 3,
+	BOX_FUNC_FIELD_LANGUAGE = 4,
+};
+
+/** _schema fields. */
+enum {
+	BOX_SCHEMA_FIELD_KEY = 0,
+};
+
+/** _cluster fields. */
+enum {
+	BOX_CLUSTER_FIELD_ID = 0,
+	BOX_CLUSTER_FIELD_UUID = 1,
+};
+
+/** _truncate fields. */
+enum {
+	BOX_TRUNCATE_FIELD_SPACE_ID = 0,
+	BOX_TRUNCATE_FIELD_COUNT = 1,
+};
+
+/*
+ * Different objects which can be subject to access
+ * control.
+ *
+ * Use 0 for unknown to use the same index consistently
+ * even when there are more object types in the future.
+ */
+enum schema_object_type {
+	SC_UNKNOWN = 0, SC_UNIVERSE = 1, SC_SPACE = 2, SC_FUNCTION = 3,
+	SC_USER = 4, SC_ROLE = 5
+};
+
+enum schema_object_type
+schema_object_type(const char *name);
+
+const char *
+schema_object_name(enum schema_object_type type);
+
+/**
+ * Check object identifier for invalid symbols.
+ * The function checks \a str for matching [a-zA-Z_][a-zA-Z0-9_]* expression.
+ * Result is locale-dependent.
+ */
+bool
+identifier_is_valid(const char *str);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+
+#include "error.h"
+
+/**
+ * Throw an error if identifier is not valid.
+ */
+static inline void
+identifier_check_xc(const char *str)
+{
+	if (! identifier_is_valid(str))
+		tnt_raise(ClientError, ER_IDENTIFIER, str);
+}
+
+#endif /* defined(__cplusplus) */
+
+#endif /* TARANTOOL_BOX_SCHEMA_DEF_H_INCLUDED */
diff --git a/src/box/space.cc b/src/box/space.cc
index 88ca232bfd7ab32fd392050d9a21643aeb6ed592..31fe296e9462d9d6c5b5ded3be32c965fa4b94da 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -83,9 +83,9 @@ space_new(struct space_def *def, struct rlist *key_list)
 	uint32_t index_id_max = 0;
 	uint32_t index_count = 0;
 	struct index_def *index_def;
-	struct index_def *pk = rlist_empty(key_list) ? NULL :
+	MAYBE_UNUSED struct index_def *pk = rlist_empty(key_list) ? NULL :
 		rlist_first_entry(key_list, struct index_def, link);
-	def = space_def_dup(def);
+	def = space_def_dup_xc(def);
 	rlist_foreach_entry(index_def, key_list, link) {
 		index_count++;
 		index_id_max = MAX(index_id_max, index_def->iid);
diff --git a/src/box/space.h b/src/box/space.h
index b7b9fc63c8038d0762dd5bc0998159d8dfad9b8d..adeb5821302fd704e109bfddf90dc4d760bcb5b1 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -30,8 +30,11 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "key_def.h"
+#include "user_def.h"
+#include "space_def.h"
 #include "small/rlist.h"
+#include "error.h"
+#include "diag.h"
 
 #if defined(__cplusplus)
 extern "C" {
diff --git a/src/box/space_def.c b/src/box/space_def.c
new file mode 100644
index 0000000000000000000000000000000000000000..5c84f20ef456a042798012c2c9ff87ce9a1f1d64
--- /dev/null
+++ b/src/box/space_def.c
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include "space_def.h"
+#include "diag.h"
+
+const struct space_opts space_opts_default = {
+	/* .temporary = */ false,
+	/* .sql        = */ NULL,
+};
+
+const struct opt_def space_opts_reg[] = {
+	OPT_DEF("temporary", OPT_BOOL, struct space_opts, temporary),
+	OPT_DEF("sql", OPT_STRPTR, struct space_opts, sql),
+	{ NULL, opt_type_MAX, 0, 0 }
+};
+
+struct space_def *
+space_def_dup(const struct space_def *src)
+{
+	size_t size = space_def_sizeof(strlen(src->name));
+	struct space_def *ret = (struct space_def *) malloc(size);
+	if (ret == NULL) {
+		diag_set(OutOfMemory, size, "malloc", "ret");
+		return NULL;
+	}
+	memcpy(ret, src, size);
+	return ret;
+}
+
+struct space_def *
+space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
+	      const char *name, uint32_t name_len,
+	      const char *engine_name, uint32_t engine_len,
+	      const struct space_opts *opts)
+{
+	size_t size = space_def_sizeof(name_len);
+	struct space_def *def = (struct space_def *) malloc(size);
+	if (def == NULL) {
+		diag_set(OutOfMemory, size, "malloc", "def");
+		return NULL;
+	}
+	assert(name_len <= BOX_NAME_MAX);
+	assert(engine_len <= ENGINE_NAME_MAX);
+	def->id = id;
+	def->uid = uid;
+	def->exact_field_count = exact_field_count;
+	memcpy(def->name, name, name_len);
+	def->name[name_len] = 0;
+	memcpy(def->engine_name, engine_name, engine_len);
+	def->engine_name[engine_len] = 0;
+	def->opts = *opts;
+	return def;
+}
diff --git a/src/box/space_def.h b/src/box/space_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..dde3f46352bb23665da4657d322ab1acd563ad16
--- /dev/null
+++ b/src/box/space_def.h
@@ -0,0 +1,157 @@
+#ifndef TARANTOOL_BOX_SPACE_DEF_H_INCLUDED
+#define TARANTOOL_BOX_SPACE_DEF_H_INCLUDED
+/*
+ * Copyright 2010-2016, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * <COPYRIGHT HOLDER> OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "trivia/util.h"
+#include "opt_def.h"
+#include "schema_def.h"
+#include <stdbool.h>
+
+#if defined(__cplusplus)
+extern "C" {
+#endif /* defined(__cplusplus) */
+
+/** Space options */
+struct space_opts {
+        /**
+	 * The space is a temporary:
+	 * - it is empty at server start
+	 * - changes are not written to WAL
+	 * - changes are not part of a snapshot
+	 */
+	bool temporary;
+	/**
+	 * SQL statement that produced this space.
+	 */
+	const char *sql;
+};
+
+extern const struct space_opts space_opts_default;
+extern const struct opt_def space_opts_reg[];
+
+/** Space metadata. */
+struct space_def {
+	/** Space id. */
+	uint32_t id;
+	/** User id of the creator of the space */
+	uint32_t uid;
+	/**
+	 * If not set (is 0), any tuple in the
+	 * space can have any number of fields.
+	 * If set, each tuple
+	 * must have exactly this many fields.
+	 */
+	uint32_t exact_field_count;
+	char engine_name[ENGINE_NAME_MAX + 1];
+	struct space_opts opts;
+	char name[0];
+};
+
+/**
+ * Size of the space_def, calculated using its name.
+ * @param name_len Length of the space name.
+ * @retval Size in bytes.
+ */
+static inline size_t
+space_def_sizeof(uint32_t name_len)
+{
+	return sizeof(struct space_def) + name_len + 1;
+}
+
+/**
+ * Delete the space_def object.
+ * @param def Def to delete.
+ */
+static inline void
+space_def_delete(struct space_def *def)
+{
+	free(def);
+}
+
+/**
+ * Duplicate space_def object.
+ * @param src Def to duplicate.
+ * @retval Copy of the @src.
+ */
+struct space_def *
+space_def_dup(const struct space_def *src);
+
+/**
+ * Create a new space definition.
+ * @param id Space identifier.
+ * @param uid Owner identifier.
+ * @param exact_field_count Space tuples field count.
+ *        0 for any count.
+ * @param name Space name.
+ * @param name_len Length of the @name.
+ * @param engine_name Engine name.
+ * @param engine_len Length of the @engine.
+ * @param opts Space options.
+ *
+ * @retval Space definition.
+ */
+struct space_def *
+space_def_new(uint32_t id, uint32_t uid, uint32_t exact_field_count,
+	      const char *name, uint32_t name_len,
+	      const char *engine_name, uint32_t engine_len,
+	      const struct space_opts *opts);
+
+#if defined(__cplusplus)
+} /* extern "C" */
+
+#include "diag.h"
+
+static inline struct space_def *
+space_def_dup_xc(const struct space_def *src)
+{
+	struct space_def *ret = space_def_dup(src);
+	if (ret == NULL)
+		diag_raise();
+	return ret;
+}
+
+static inline struct space_def *
+space_def_new_xc(uint32_t id, uint32_t uid, uint32_t exact_field_count,
+		 const char *name, uint32_t name_len,
+		 const char *engine_name, uint32_t engine_len,
+		 const struct space_opts *opts)
+{
+	struct space_def *ret = space_def_new(id, uid, exact_field_count, name,
+					      name_len, engine_name, engine_len,
+					      opts);
+	if (ret == NULL)
+		diag_raise();
+	return ret;
+}
+
+#endif /* __cplusplus */
+
+#endif /* TARANTOOL_BOX_SPACE_DEF_H_INCLUDED */
diff --git a/src/box/sql.c b/src/box/sql.c
index 38eef7dfa90a01c081c066dc06b1b4c7d34e9591..94117b10ef1cfc717cbc5311474fd1059e9eead8 100644
--- a/src/box/sql.c
+++ b/src/box/sql.c
@@ -50,7 +50,8 @@
 #include "info.h"
 #include "schema.h"
 #include "box.h"
-#include "key_def.h"
+#include "space_def.h"
+#include "index_def.h"
 #include "tuple.h"
 #include "fiber.h"
 #include "small/region.h"
diff --git a/src/box/txn.cc b/src/box/txn.cc
index 1f5529d5722c9808f4a0ef63b55bfe569bae2904..5188ea39eb58caecad898390e7e92cb7d246a93f 100644
--- a/src/box/txn.cc
+++ b/src/box/txn.cc
@@ -202,10 +202,10 @@ txn_write_to_wal(struct txn *txn)
 	}
 	assert(row == req->rows + req->n_rows);
 
-	ev_tstamp start = ev_now(loop()), stop;
+	ev_tstamp start = ev_monotonic_now(loop());
 	int64_t res = journal_write(req);
 
-	stop = ev_now(loop());
+	ev_tstamp stop = ev_monotonic_now(loop());
 	if (stop - start > too_long_threshold)
 		say_warn("too long WAL write: %.3f sec", stop - start);
 	if (res < 0) {
diff --git a/src/box/user_def.h b/src/box/user_def.h
index 67f1d68e35c7ef863d73532ce73f3e5eb29e4582..602ac512faf8c330dbaf9f1ecce198393809ae95 100644
--- a/src/box/user_def.h
+++ b/src/box/user_def.h
@@ -30,13 +30,33 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
-#include "key_def.h" /* for SCHEMA_OBJECT_TYPE */
+#include "schema_def.h" /* for SCHEMA_OBJECT_TYPE */
 #include "scramble.h" /* for SCRAMBLE_SIZE */
+#define RB_COMPACT 1
+#include "small/rb.h"
 
 #if defined(__cplusplus)
 extern "C" {
 #endif /* defined(__cplusplus) */
 
+/**
+ * Effective session user. A cache of user data
+ * and access stored in session and fiber local storage.
+ * Differs from the authenticated user when executing
+ * setuid functions.
+ */
+struct credentials {
+	/** A look up key to quickly find session user. */
+	uint8_t auth_token;
+	/**
+	 * Cached global grants, to avoid an extra look up
+	 * when checking global grants.
+	 */
+	uint8_t universal_access;
+	/** User id of the authenticated user. */
+	uint32_t uid;
+};
+
 enum {
 	/* SELECT */
 	PRIV_R = 1,
@@ -48,10 +68,51 @@ enum {
 	PRIV_ALL = PRIV_R + PRIV_W + PRIV_X
 };
 
+/**
+ * Definition of a privilege
+ */
+struct priv_def {
+	/** Who grants the privilege. */
+	uint32_t grantor_id;
+	/** Whom the privilege is granted. */
+	uint32_t grantee_id;
+	/* Object id - is only defined for object type */
+	uint32_t object_id;
+	/* Object type - function, space, universe */
+	enum schema_object_type object_type;
+	/**
+	 * What is being granted, has been granted, or is being
+	 * revoked.
+	 */
+	uint8_t access;
+	/** To maintain a set of effective privileges. */
+	rb_node(struct priv_def) link;
+};
+
 /* Privilege name for error messages */
 const char *
 priv_name(uint8_t access);
 
+/**
+ * Encapsulates privileges of a user on an object.
+ * I.e. "space" object has an instance of this
+ * structure for each user.
+ */
+struct access {
+	/**
+	 * Granted access has been given to a user explicitly
+	 * via some form of a grant.
+	 */
+	uint8_t granted;
+	/**
+	 * Effective access is a sum of granted access and
+	 * all privileges inherited by a user on this object
+	 * via some role. Since roles may be granted to other
+	 * roles, this may include indirect grants.
+	 */
+	uint8_t effective;
+};
+
 /**
  * A cache entry for an existing user. Entries for all existing
  * users are always present in the cache. The entry is maintained
diff --git a/src/box/vinyl.c b/src/box/vinyl.c
index e7de0b704059735865bda7f7a9ddb16899ce0f57..c6ed11505b15e6f5cfb009aca3ed7ccd3a6c167a 100644
--- a/src/box/vinyl.c
+++ b/src/box/vinyl.c
@@ -1277,10 +1277,10 @@ static ev_tstamp
 vy_scheduler_quota_throttled_cb(struct vy_quota *quota, ev_tstamp timeout)
 {
 	struct vy_env *env = container_of(quota, struct vy_env, quota);
-	ev_tstamp wait_start = ev_now(loop());
+	ev_tstamp wait_start = ev_monotonic_now(loop());
 	if (fiber_cond_wait_timeout(&env->scheduler->quota_cond, timeout) != 0)
 		return 0; /* timed out */
-	ev_tstamp wait_end = ev_now(loop());
+	ev_tstamp wait_end = ev_monotonic_now(loop());
 	return timeout - (wait_end - wait_start);
 }
 
@@ -1845,7 +1845,7 @@ vy_scheduler_trigger_dump(struct vy_scheduler *scheduler)
 		 * dump bandwidth when the dump round is complete
 		 * (see vy_scheduler_complete_dump()).
 		 */
-		scheduler->dump_start = ev_now(loop());
+		scheduler->dump_start = ev_monotonic_now(loop());
 	}
 	scheduler->generation++;
 }
@@ -1901,7 +1901,7 @@ vy_scheduler_complete_dump(struct vy_scheduler *scheduler)
 
 	/* Account dump bandwidth. */
 	struct vy_stat *stat = scheduler->env->stat;
-	ev_tstamp now = ev_now(loop());
+	ev_tstamp now = ev_monotonic_now(loop());
 	ev_tstamp dump_duration = now - scheduler->dump_start;
 	if (dump_duration > 0)
 		histogram_collect(stat->dump_bw, mem_dumped / dump_duration);
diff --git a/src/box/vy_cache.c b/src/box/vy_cache.c
index b68e6b674350b53a9cb30a77e18014d7befdeeba..c1bba5fcd7d8ed01f4d27f6bb5a679f226599aa1 100644
--- a/src/box/vy_cache.c
+++ b/src/box/vy_cache.c
@@ -30,6 +30,7 @@
  */
 #include "vy_cache.h"
 #include "diag.h"
+#include "schema_def.h"
 
 #ifndef CT_ASSERT_G
 #define CT_ASSERT_G(e) typedef char CONCAT(__ct_assert_, __LINE__)[(e) ? 1 :-1]
diff --git a/src/box/vy_index.c b/src/box/vy_index.c
index fce8416691a2c7ef41a69f7f0e6af2a69cfb1d83..b9472e520b62901a690914627d02c0243d736f1b 100644
--- a/src/box/vy_index.c
+++ b/src/box/vy_index.c
@@ -30,28 +30,20 @@
  */
 #include "vy_index.h"
 
-#include <assert.h>
+#include "trivia/util.h"
 #include <stdbool.h>
 #include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-
 #include <sys/stat.h>
 #include <sys/types.h>
 
-#include <small/rlist.h>
-
 #include "assoc.h"
 #include "diag.h"
 #include "errcode.h"
 #include "histogram.h"
-#include "key_def.h"
+#include "index_def.h"
 #include "say.h"
 #include "schema.h"
-#include "trivia/util.h"
 #include "tuple.h"
-#include "tuple_format.h"
 #include "vy_log.h"
 #include "vy_mem.h"
 #include "vy_range.h"
@@ -723,10 +715,23 @@ vy_index_set(struct vy_index *index, struct vy_mem *mem,
 	/* We can't free region_stmt below, so let's add it to the stats */
 	index->stat.memory.count.bytes += tuple_size(stmt);
 
-	if (vy_stmt_type(*region_stmt) != IPROTO_UPSERT)
+	uint32_t format_id = stmt->format_id;
+	if (vy_stmt_type(*region_stmt) != IPROTO_UPSERT) {
+		/* Abort transaction if format was changed by DDL */
+		if (format_id != tuple_format_id(mem->format_with_colmask) &&
+		    format_id != tuple_format_id(mem->format)) {
+			diag_set(ClientError, ER_TRANSACTION_CONFLICT);
+			return -1;
+		}
 		return vy_mem_insert(mem, *region_stmt);
-	else
+	} else {
+		/* Abort transaction if format was changed by DDL */
+		if (format_id != tuple_format_id(mem->upsert_format)) {
+			diag_set(ClientError, ER_TRANSACTION_CONFLICT);
+			return -1;
+		}
 		return vy_mem_insert_upsert(mem, *region_stmt);
+	}
 }
 
 /**
diff --git a/src/box/vy_index.h b/src/box/vy_index.h
index 4a85393b70fb5a5d83efb5ff00d5319d3fd36548..f2b7c3f6c7a1a0ee301f4e4a69ba8b2aaca5e5c3 100644
--- a/src/box/vy_index.h
+++ b/src/box/vy_index.h
@@ -37,7 +37,7 @@
 
 #include <small/rlist.h>
 
-#include "key_def.h"
+#include "index_def.h"
 #define HEAP_FORWARD_DECLARATION
 #include "salad/heap.h"
 #include "vy_cache.h"
diff --git a/src/box/vy_mem.c b/src/box/vy_mem.c
index e82b6d25c7ae8dd269185c1dc787e887b238b62f..dc559e1eb2869029c335dfcd182b27501efc6caf 100644
--- a/src/box/vy_mem.c
+++ b/src/box/vy_mem.c
@@ -143,9 +143,7 @@ vy_mem_insert_upsert(struct vy_mem *mem, const struct tuple *stmt)
 {
 	assert(vy_stmt_type(stmt) == IPROTO_UPSERT);
 	/* Check if the statement can be inserted in the vy_mem. */
-	assert(stmt->format_id == tuple_format_id(mem->format_with_colmask) ||
-	       stmt->format_id == tuple_format_id(mem->format) ||
-	       stmt->format_id == tuple_format_id(mem->upsert_format));
+	assert(stmt->format_id == tuple_format_id(mem->upsert_format));
 	/* The statement must be from a lsregion. */
 	assert(!vy_stmt_is_refable(stmt));
 	size_t size = tuple_size(stmt);
@@ -205,8 +203,7 @@ vy_mem_insert(struct vy_mem *mem, const struct tuple *stmt)
 	assert(vy_stmt_type(stmt) != IPROTO_UPSERT);
 	/* Check if the statement can be inserted in the vy_mem. */
 	assert(stmt->format_id == tuple_format_id(mem->format_with_colmask) ||
-	       stmt->format_id == tuple_format_id(mem->format) ||
-	       stmt->format_id == tuple_format_id(mem->upsert_format));
+	       stmt->format_id == tuple_format_id(mem->format));
 	/* The statement must be from a lsregion. */
 	assert(!vy_stmt_is_refable(stmt));
 	size_t size = tuple_size(stmt);
diff --git a/src/box/vy_read_iterator.c b/src/box/vy_read_iterator.c
index 745e2b383d182d183f76f09a43a1cd9519ab0eb3..4be826b052b8ed214ef1fdf622971cc0e4f162c1 100644
--- a/src/box/vy_read_iterator.c
+++ b/src/box/vy_read_iterator.c
@@ -808,7 +808,7 @@ vy_read_iterator_next_range(struct vy_read_iterator *itr, struct tuple **ret)
 NODISCARD int
 vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result)
 {
-	ev_tstamp start_time = ev_now(loop());
+	ev_tstamp start_time = ev_monotonic_now(loop());
 
 	/* The key might be set to NULL during previous call, that means
 	 * that there's no more data */
@@ -964,7 +964,8 @@ vy_read_iterator_next(struct vy_read_iterator *itr, struct tuple **result)
 	if (prev_key != NULL)
 		tuple_unref(prev_key);
 
-	latency_collect(&index->stat.latency, ev_now(loop()) - start_time);
+	latency_collect(&index->stat.latency,
+			ev_monotonic_now(loop()) - start_time);
 	return rc;
 }
 
diff --git a/src/box/vy_run.h b/src/box/vy_run.h
index 772b69ab2f885ddebd68ffb95355147c737dd588..dad1092189541d5ad78aa88c81c812fa78fc9264 100644
--- a/src/box/vy_run.h
+++ b/src/box/vy_run.h
@@ -39,6 +39,7 @@
 #include "vy_stmt.h" /* for comparators */
 #include "vy_stmt_iterator.h" /* struct vy_stmt_iterator */
 #include "vy_stat.h"
+#include "index_def.h"
 
 #include "small/mempool.h"
 #include "salad/bloom.h"
diff --git a/src/box/vy_tx.c b/src/box/vy_tx.c
index f50231f864d625112d4779a53c708241f4381d9a..1a41e54620ef5bfa64dcdeedfd4b4e35dbea74b8 100644
--- a/src/box/vy_tx.c
+++ b/src/box/vy_tx.c
@@ -231,6 +231,7 @@ txv_new(struct vy_tx *tx, struct vy_index *index, struct tuple *stmt)
 		return NULL;
 	}
 	v->index = index;
+	vy_index_ref(v->index);
 	v->mem = NULL;
 	v->stmt = stmt;
 	tuple_ref(stmt);
@@ -245,6 +246,7 @@ static void
 txv_delete(struct txv *v)
 {
 	tuple_unref(v->stmt);
+	vy_index_unref(v->index);
 	mempool_free(&v->tx->xm->txv_mempool, v);
 }
 
@@ -688,7 +690,7 @@ vy_tx_track(struct vy_tx *tx, struct vy_index *index,
 	    struct tuple *key, bool is_gap)
 {
 	struct txv *v;
-	uint32_t part_count = tuple_field_count(key);
+	MAYBE_UNUSED uint32_t part_count = tuple_field_count(key);
 
 	/* We do not support tracking range requests. */
 	assert(part_count >= index->cmp_def->part_count);
diff --git a/src/box/wal.cc b/src/box/wal.cc
index 960c9d048ef816229045439bb5208d499651ec49..1e625643700cb4c27041e0f578151cf1b170606f 100644
--- a/src/box/wal.cc
+++ b/src/box/wal.cc
@@ -767,11 +767,21 @@ wal_write(struct journal *journal, struct journal_entry *entry)
 	bool cancellable = fiber_set_cancellable(false);
 	fiber_yield(); /* Request was inserted. */
 	fiber_set_cancellable(cancellable);
-	/* All rows in request have the same replica id. */
-	struct xrow_header *last = entry->rows[entry->n_rows - 1];
-	/* Promote replica set vclock with local writes. */
-	if (entry->res > 0 && last->replica_id == instance_id)
-		vclock_follow(&replicaset_vclock, instance_id, last->lsn);
+	if (entry->res > 0) {
+		struct xrow_header **last = entry->rows + entry->n_rows - 1;
+		while (last >= entry->rows) {
+			/*
+			 * Find last row from local instance id
+			 * and promote vclock.
+			 */
+			if ((*last)->replica_id == instance_id) {
+				vclock_follow(&replicaset_vclock, instance_id,
+					      (*last)->lsn);
+				break;
+			}
+			--last;
+		}
+	}
 	return entry->res;
 }
 
@@ -780,22 +790,11 @@ wal_write_in_wal_mode_none(struct journal *journal,
 			   struct journal_entry *entry)
 {
 	struct wal_writer *writer = (struct wal_writer *) journal;
-	/** All rows in a request have the same replica id. */
-	struct xrow_header *last = entry->rows[entry->n_rows - 1];
-	if (last->replica_id != 0) {
-		/*
-		 * All rows in a transaction have the same
-		 * replica_id
-		 */
-		vclock_follow(&writer->vclock, last->replica_id, last->lsn);
-	} else {
-		/*
-		 * Fake the local WAL by assigning an LSN to each
-		 * row.
-		 */
-		int64_t new_lsn = vclock_get(&writer->vclock, instance_id) +
-			entry->n_rows;
-		vclock_follow(&writer->vclock, instance_id, new_lsn);
+	wal_assign_lsn(writer, entry->rows, entry->rows + entry->n_rows);
+	int64_t old_lsn = vclock_get(&replicaset_vclock, instance_id);
+	int64_t new_lsn = vclock_get(&writer->vclock, instance_id);
+	if (new_lsn > old_lsn) {
+		/* There were local writes, promote vclock. */
 		vclock_follow(&replicaset_vclock, instance_id, new_lsn);
 	}
 	return vclock_sum(&writer->vclock);
diff --git a/src/box/xlog.c b/src/box/xlog.c
index 14764f7ccdf7495c58365f2123d413d436dba045..11bce2d6035e570ca0d0e03513e9fae891e571b1 100644
--- a/src/box/xlog.c
+++ b/src/box/xlog.c
@@ -658,7 +658,7 @@ xlog_init(struct xlog *xlog)
 {
 	memset(xlog, 0, sizeof(*xlog));
 	xlog->sync_interval = SNAP_SYNC_INTERVAL;
-	xlog->sync_time = ev_time();
+	xlog->sync_time = ev_monotonic_time();
 	xlog->is_autocommit = true;
 	obuf_create(&xlog->obuf, &cord()->slabc, XLOG_TX_AUTOCOMMIT_THRESHOLD);
 	obuf_create(&xlog->zbuf, &cord()->slabc, XLOG_TX_AUTOCOMMIT_THRESHOLD);
@@ -1090,7 +1090,7 @@ xlog_tx_write(struct xlog *log)
 		if (log->rate_limit > 0) {
 			double throttle_time;
 			throttle_time = (double)sync_len / log->rate_limit -
-					(ev_time() - log->sync_time);
+					(ev_monotonic_time() - log->sync_time);
 			if (throttle_time > 0)
 				fiber_sleep(throttle_time);
 		}
@@ -1103,7 +1103,7 @@ xlog_tx_write(struct xlog *log)
 #else
 		fdatasync(log->fd);
 #endif /* HAVE_SYNC_FILE_RANGE */
-		log->sync_time = ev_time();
+		log->sync_time = ev_monotonic_time();
 		if (log->free_cache) {
 #ifdef HAVE_POSIX_FADVISE
 			/** free page cache */
diff --git a/src/box/xrow_io.cc b/src/box/xrow_io.cc
index f2fc772598dd9a6727170013c95402b7b365c5f5..e9ee6b0c85987d69163f712cf2a65630a3711b71 100644
--- a/src/box/xrow_io.cc
+++ b/src/box/xrow_io.cc
@@ -61,6 +61,38 @@ coio_read_xrow(struct ev_io *coio, struct ibuf *in, struct xrow_header *row)
 	xrow_header_decode_xc(row, (const char **) &in->rpos, in->rpos + len);
 }
 
+void
+coio_read_xrow_timeout_xc(struct ev_io *coio, struct ibuf *in,
+			  struct xrow_header *row, ev_tstamp timeout)
+{
+	ev_tstamp start, delay;
+	coio_timeout_init(&start, &delay, timeout);
+	/* Read fixed header */
+	if (ibuf_used(in) < 1)
+		coio_breadn_timeout(coio, in, 1, delay);
+	coio_timeout_update(start, &delay);
+
+	/* Read length */
+	if (mp_typeof(*in->rpos) != MP_UINT) {
+		tnt_raise(ClientError, ER_INVALID_MSGPACK,
+			  "packet length");
+	}
+	ssize_t to_read = mp_check_uint(in->rpos, in->wpos);
+	if (to_read > 0)
+		coio_breadn_timeout(coio, in, to_read, delay);
+	coio_timeout_update(start, &delay);
+
+	uint32_t len = mp_decode_uint((const char **) &in->rpos);
+
+	/* Read header and body */
+	to_read = len - ibuf_used(in);
+	if (to_read > 0)
+		coio_breadn_timeout(coio, in, to_read, delay);
+
+	xrow_header_decode_xc(row, (const char **) &in->rpos, in->rpos + len);
+}
+
+
 void
 coio_write_xrow(struct ev_io *coio, const struct xrow_header *row)
 {
diff --git a/src/box/xrow_io.h b/src/box/xrow_io.h
index a8764c3386ecefabafd23613ba087e40e5112e69..0eb7a8ace11b550aad4310e6642264a044849d4b 100644
--- a/src/box/xrow_io.h
+++ b/src/box/xrow_io.h
@@ -41,6 +41,10 @@ struct xrow_header;
 void
 coio_read_xrow(struct ev_io *coio, struct ibuf *in, struct xrow_header *row);
 
+void
+coio_read_xrow_timeout_xc(struct ev_io *coio, struct ibuf *in,
+			  struct xrow_header *row, double timeout);
+
 void
 coio_write_xrow(struct ev_io *coio, const struct xrow_header *row);
 
diff --git a/src/cbus.c b/src/cbus.c
index c2f7849d77602399e5ad1c4c1591294dbac3926f..4bd8d5c8f36f5bc86e7c699dfefa23180f3ae5f7 100644
--- a/src/cbus.c
+++ b/src/cbus.c
@@ -148,6 +148,7 @@ cpipe_destroy(struct cpipe *pipe)
 	tt_pthread_mutex_lock(&endpoint->mutex);
 	/* Flush input */
 	stailq_concat(&endpoint->output, &pipe->input);
+	pipe->n_input = 0;
 	/* Add the pipe shutdown message as the last one. */
 	stailq_add_tail_entry(&endpoint->output, poison, msg.fifo);
 	/* Count statistics */
@@ -182,6 +183,12 @@ cbus_create(struct cbus *bus)
 static void
 cbus_destroy(struct cbus *bus)
 {
+	/*
+	 * Lock the mutex to ensure we do not destroy a mutex
+	 * while it is locked, happens in at_exit() handler.
+	 */
+	(void) tt_pthread_mutex_lock(&bus->mutex);
+	(void) tt_pthread_mutex_unlock(&bus->mutex);
 	(void) tt_pthread_mutex_destroy(&bus->mutex);
 	(void) tt_pthread_cond_destroy(&bus->cond);
 	rmean_delete(bus->stats);
diff --git a/src/diag.h b/src/diag.h
index 114deb24c338d8c8ab69ff9924d1c49d3c95f89c..59d280c9fc7d96744d2867bff8dbd8fe61698015 100644
--- a/src/diag.h
+++ b/src/diag.h
@@ -49,8 +49,6 @@ enum {
 
 struct type_info;
 struct error;
-struct error_factory;
-extern struct error_factory *error_factory;
 
 typedef void (*error_f)(struct error *e);
 
@@ -214,27 +212,6 @@ diag_last_error(struct diag *diag)
 	return diag->last;
 }
 
-struct error_factory {
-	struct error *(*OutOfMemory)(const char *file,
-				     unsigned line, size_t amount,
-				     const char *allocator,
-				     const char *object);
-	struct error *(*FiberIsCancelled)(const char *file,
-					  unsigned line);
-	struct error *(*TimedOut)(const char *file,
-				  unsigned line);
-	struct error *(*ChannelIsClosed)(const char *file,
-					 unsigned line);
-	struct error *(*LuajitError)(const char *file,
-				     unsigned line, const char *msg);
-	struct error *(*ClientError)(const char *file, unsigned line,
-				     uint32_t errcode, ...);
-	struct error *(*SystemError)(const char *file, unsigned line,
-				     const char *format, ...);
-	struct error *(*XlogError)(const char *file, unsigned line,
-				   const char *format, ...);
-};
-
 struct diag *
 diag_get();
 
@@ -254,14 +231,28 @@ diag_log(void)
 	error_log(e);
 }
 
+struct error *
+BuildOutOfMemory(const char *file, unsigned line, size_t amount,
+		 const char *allocator,
+		 const char *object);
+struct error *
+BuildFiberIsCancelled(const char *file, unsigned line);
+struct error *
+BuildTimedOut(const char *file, unsigned line);
+struct error *
+BuildChannelIsClosed(const char *file, unsigned line);
+struct error *
+BuildLuajitError(const char *file, unsigned line, const char *msg);
+struct error *
+BuildSystemError(const char *file, unsigned line, const char *format, ...);
+struct error *
+BuildXlogError(const char *file, unsigned line, const char *format, ...);
+
 #define diag_set(class, ...) do {					\
 	say_debug("%s at %s:%i", #class, __FILE__, __LINE__);		\
-	/* No op if exception subsystem is not initialized. */		\
-	if (error_factory) {						\
-		struct error *e;					\
-		e = error_factory->class(__FILE__, __LINE__, ##__VA_ARGS__);\
-		diag_add_error(diag_get(), e);				\
-	}								\
+	struct error *e;						\
+	e = Build##class(__FILE__, __LINE__, ##__VA_ARGS__);		\
+	diag_add_error(diag_get(), e);					\
 } while (0)
 
 #if defined(__cplusplus)
diff --git a/src/evio.h b/src/evio.h
index 4ddf8c3987f81b99ffe942b0d6882b433d4e7647..e91ba11fc3a6e687cb1e3b8093dbdc4715d964e4 100644
--- a/src/evio.h
+++ b/src/evio.h
@@ -138,14 +138,14 @@ static inline void
 evio_timeout_init(ev_loop *loop, ev_tstamp *start, ev_tstamp *delay,
 		  ev_tstamp timeout)
 {
-	*start = ev_now(loop);
+	*start = ev_monotonic_now(loop);
 	*delay = timeout;
 }
 
 static inline void
 evio_timeout_update(ev_loop *loop, ev_tstamp start, ev_tstamp *delay)
 {
-	ev_tstamp elapsed = ev_now(loop) - start;
+	ev_tstamp elapsed = ev_monotonic_now(loop) - start;
 	*delay = (elapsed >= *delay) ? 0 : *delay - elapsed;
 }
 
diff --git a/src/exception.cc b/src/exception.cc
index ad85380f0d6603345d02707461c290a2a7d100d0..290fd0f4a85b5ec33f7862fac567474e382d779b 100644
--- a/src/exception.cc
+++ b/src/exception.cc
@@ -227,7 +227,7 @@ LuajitError::LuajitError(const char *file, unsigned line,
 	if (p == NULL)					\
 		return &out_of_memory;
 
-static struct error *
+struct error *
 BuildOutOfMemory(const char *file, unsigned line,
 		 size_t amount, const char *allocator,
 		 const char *object)
@@ -237,35 +237,35 @@ BuildOutOfMemory(const char *file, unsigned line,
 				   object);
 }
 
-static struct error *
+struct error *
 BuildTimedOut(const char *file, unsigned line)
 {
 	BuildAlloc(TimedOut);
 	return new (p) TimedOut(file, line);
 }
 
-static struct error *
+struct error *
 BuildChannelIsClosed(const char *file, unsigned line)
 {
 	BuildAlloc(ChannelIsClosed);
 	return new (p) ChannelIsClosed(file, line);
 }
 
-static struct error *
+struct error *
 BuildFiberIsCancelled(const char *file, unsigned line)
 {
 	BuildAlloc(FiberIsCancelled);
 	return new (p) FiberIsCancelled(file, line);
 }
 
-static struct error *
+struct error *
 BuildLuajitError(const char *file, unsigned line, const char *msg)
 {
 	BuildAlloc(LuajitError);
 	return new (p) LuajitError(file, line, msg);
 }
 
-static struct error *
+struct error *
 BuildSystemError(const char *file, unsigned line, const char *format, ...)
 {
 	BuildAlloc(SystemError);
@@ -277,23 +277,11 @@ BuildSystemError(const char *file, unsigned line, const char *format, ...)
 	return e;
 }
 
-#undef BuildAlloc
-
 void
 exception_init()
 {
-	static struct error_factory exception_error_factory;
-
-	exception_error_factory.OutOfMemory = BuildOutOfMemory;
-	exception_error_factory.FiberIsCancelled = BuildFiberIsCancelled;
-	exception_error_factory.TimedOut = BuildTimedOut;
-	exception_error_factory.ChannelIsClosed = BuildChannelIsClosed;
-	exception_error_factory.LuajitError = BuildLuajitError;
-	exception_error_factory.SystemError = BuildSystemError;
-
-	error_factory = &exception_error_factory;
-
 	/* A special workaround for out_of_memory static init */
 	out_of_memory.refs = 1;
 }
 
+#undef BuildAlloc
diff --git a/src/fiber.c b/src/fiber.c
index 5f093922214b7dc658d486d33bb046004bba6a8c..298c7ab2f2ae7dbe17231c9f1bc2d68074a22c29 100644
--- a/src/fiber.c
+++ b/src/fiber.c
@@ -354,6 +354,18 @@ fiber_time64(void)
 	return (uint64_t) ( ev_now(loop()) * 1000000 + 0.5 );
 }
 
+double
+fiber_clock(void)
+{
+	return ev_monotonic_now(loop());
+}
+
+uint64_t
+fiber_clock64(void)
+{
+	return (uint64_t) ( ev_monotonic_now(loop()) * 1000000 + 0.5 );
+}
+
 /**
  * Move current fiber to the end of ready fibers list and switch to next
  */
diff --git a/src/fiber.h b/src/fiber.h
index f1610fc39ea6652219d4f663b966986c0abadd81..0d0930cf593359799c76bfe384958652fcd0dbcb 100644
--- a/src/fiber.h
+++ b/src/fiber.h
@@ -290,16 +290,32 @@ fiber_is_cancelled();
 
 /**
  * Report loop begin time as double (cheap).
+ * Uses real time clock.
  */
 API_EXPORT double
 fiber_time(void);
 
 /**
  * Report loop begin time as 64-bit int.
+ * Uses real time clock.
  */
 API_EXPORT uint64_t
 fiber_time64(void);
 
+/**
+ * Report loop begin time as double (cheap).
+ * Uses monotonic clock.
+ */
+API_EXPORT double
+fiber_clock(void);
+
+/**
+ * Report loop begin time as 64-bit int.
+ * Uses monotonic clock.
+ */
+API_EXPORT uint64_t
+fiber_clock64(void);
+
 /**
  * Reschedule fiber to end of event loop cycle.
  */
diff --git a/src/fiber_channel.c b/src/fiber_channel.c
index 6f1afda653eb564ace353c7357eb2a44ddc9fe0c..99e51a47e7e0bdb0e72ed00bb8564b27a39355ba 100644
--- a/src/fiber_channel.c
+++ b/src/fiber_channel.c
@@ -185,7 +185,7 @@ fiber_channel_check_wait(struct fiber_channel *ch, ev_tstamp start_time,
 		diag_set(FiberIsCancelled);
 		return -1;
 	}
-	if (timeout == 0 || ev_now(loop()) > start_time + timeout) {
+	if (timeout == 0 || ev_monotonic_now(loop()) > start_time + timeout) {
 		diag_set(TimedOut);
 		return -1;
 	}
@@ -291,7 +291,7 @@ fiber_channel_put_msg_timeout(struct fiber_channel *ch,
 {
 	/** Ensure delivery fairness in case of prolonged wait. */
 	bool first_try = true;
-	ev_tstamp start_time = ev_now(loop());
+	ev_tstamp start_time = ev_monotonic_now(loop());
 
 	while (true) {
 		/*
@@ -383,7 +383,7 @@ fiber_channel_put_msg_timeout(struct fiber_channel *ch,
 		}
 		if (pad.status == FIBER_CHANNEL_WAIT_DONE)
 			return 0;  /* OK, someone took the message. */
-		timeout -= ev_now(loop()) - start_time;
+		timeout -= ev_monotonic_now(loop()) - start_time;
 	}
 }
 
@@ -394,7 +394,7 @@ fiber_channel_get_msg_timeout(struct fiber_channel *ch,
 {
 	/** Ensure delivery fairness in case of prolonged wait. */
 	bool first_try = true;
-	ev_tstamp start_time = ev_now(loop());
+	ev_tstamp start_time = ev_monotonic_now(loop());
 
 	while (true) {
 		struct fiber *f;
@@ -484,6 +484,6 @@ fiber_channel_get_msg_timeout(struct fiber_channel *ch,
 			*msg = pad.msg;
 			return 0;
 		}
-		timeout -= ev_now(loop()) - start_time;
+		timeout -= ev_monotonic_now(loop()) - start_time;
 	}
 }
diff --git a/src/fiber_pool.c b/src/fiber_pool.c
index 4e34e625afaff109524b393c7cb2a68db636f39d..0a81c006983a1f2062170d58ea2ef8fb12b9c027 100644
--- a/src/fiber_pool.c
+++ b/src/fiber_pool.c
@@ -42,7 +42,7 @@ fiber_pool_f(va_list ap)
 	struct ev_loop *loop = pool->consumer;
 	struct stailq *output = &pool->output;
 	struct cmsg *msg;
-	ev_tstamp last_active_at = ev_now(loop);
+	ev_tstamp last_active_at = ev_monotonic_now(loop);
 	pool->size++;
 restart:
 	msg = NULL;
@@ -64,9 +64,10 @@ fiber_pool_f(va_list ap)
 		cmsg_deliver(msg);
 	}
 	/** Put the current fiber into a fiber cache. */
-	if (msg != NULL || ev_now(loop) - last_active_at < pool->idle_timeout) {
+	if (msg != NULL ||
+	    ev_monotonic_now(loop) - last_active_at < pool->idle_timeout) {
 		if (msg != NULL)
-			last_active_at = ev_now(loop);
+			last_active_at = ev_monotonic_now(loop);
 		/*
 		 * Add the fiber to the front of the list, so that
 		 * it is most likely to get scheduled again.
diff --git a/src/latch.h b/src/latch.h
index 5a4d1ecb1eb683d1fda3fbd0c919623c9516b797..cbdff7182d8ec32ef3f589529a297a07e589f14f 100644
--- a/src/latch.h
+++ b/src/latch.h
@@ -126,7 +126,7 @@ latch_lock_timeout(struct latch *l, ev_tstamp timeout)
 			l->owner = fiber();
 			break;
 		}
-		timeout -= ev_now(loop()) - start;
+		timeout -= ev_monotonic_now(loop()) - start;
 		if (timeout <= 0) {
 			result = 1;
 			break;
diff --git a/src/lua/fiber.lua b/src/lua/fiber.lua
index 20bdbbfead71402f372f797de133e27d405228cb..8712ee0d682aab4eda2be2cc7bcbff830c86987d 100644
--- a/src/lua/fiber.lua
+++ b/src/lua/fiber.lua
@@ -7,6 +7,10 @@ double
 fiber_time(void);
 uint64_t
 fiber_time64(void);
+double
+fiber_clock(void);
+uint64_t
+fiber_clock64(void);
 ]]
 local C = ffi.C
 
@@ -18,6 +22,16 @@ local function fiber_time64()
     return C.fiber_time64()
 end
 
+local function fiber_clock()
+    return tonumber(C.fiber_clock())
+end
+
+local function fiber_clock64()
+    return C.fiber_clock64()
+end
+
 fiber.time = fiber_time
 fiber.time64 = fiber_time64
+fiber.clock = fiber_clock
+fiber.clock64 = fiber_clock64
 return fiber
diff --git a/src/lua/init.c b/src/lua/init.c
index 8945f5d6b6a522e93d86bad7e6f8d4177c2f8138..646b7f2f03b46f3d8dd084e6b4792d2292740ba4 100644
--- a/src/lua/init.c
+++ b/src/lua/init.c
@@ -465,6 +465,24 @@ tarantool_lua_slab_cache()
 	return &cord()->slabc;
 }
 
+/**
+ * Push argument and call a function on the top of Lua stack
+ */
+static void
+lua_main(lua_State *L, int argc, char **argv)
+{
+	assert(lua_isfunction(L, -1));
+	lua_checkstack(L, argc - 1);
+	for (int i = 1; i < argc; i++)
+		lua_pushstring(L, argv[i]);
+	if (luaT_call(L, lua_gettop(L) - 1, 0) != 0) {
+		struct error *e = diag_last_error(&fiber()->diag);
+		panic("%s", e->errmsg);
+	}
+	/* clear the stack from return values. */
+	lua_settop(L, 0);
+}
+
 /**
  * Execute start-up script.
  */
@@ -473,8 +491,52 @@ run_script_f(va_list ap)
 {
 	struct lua_State *L = va_arg(ap, struct lua_State *);
 	const char *path = va_arg(ap, const char *);
+	bool interactive = va_arg(ap, int);
+	int optc = va_arg(ap, int);
+	char **optv = va_arg(ap, char **);
 	int argc = va_arg(ap, int);
 	char **argv = va_arg(ap, char **);
+	struct diag *diag = &fiber()->diag;
+
+	/*
+	 * Load libraries and execute chunks passed by -l and -e
+	 * command line options
+	 */
+	for (int i = 0; i < optc; i += 2) {
+		assert(optv[i][0] == '-' && optv[i][2] == '\0');
+		switch (optv[i][1]) {
+		case 'l':
+			/*
+			 * Load library
+			 */
+			lua_getglobal(L, "require");
+			lua_pushstring(L, optv[i + 1]);
+			if (luaT_call(L, 1, 1) != 0) {
+				struct error *e = diag_last_error(diag);
+				panic("%s", e->errmsg);
+			}
+			/* Non-standard: set name = require('name') */
+			lua_setglobal(L, optv[i + 1]);
+			lua_settop(L, 0);
+			break;
+		case 'e':
+			/*
+			 * Execute chunk
+			 */
+			if (luaL_loadbuffer(L, optv[i + 1], strlen(optv[i + 1]),
+					    "=(command line)") != 0) {
+				panic("%s", lua_tostring(L, -1));
+			}
+			if (luaT_call(L, 0, 0) != 0) {
+				struct error *e = diag_last_error(diag);
+				panic("%s", e->errmsg);
+			}
+			lua_settop(L, 0);
+			break;
+		default:
+			unreachable(); /* checked by getopt() in main() */
+		}
+	}
 
 	/*
 	 * Return control to tarantool_lua_run_script.
@@ -483,15 +545,25 @@ run_script_f(va_list ap)
 	 */
 	fiber_sleep(0.0);
 
-	if (path && access(path, F_OK) == 0) {
+	if (path && strcmp(path, "-") != 0 && access(path, F_OK) == 0) {
 		/* Execute script. */
 		if (luaL_loadfile(L, path) != 0)
 			panic("%s", lua_tostring(L, -1));
-	} else if (!isatty(STDIN_FILENO)) {
+		lua_main(L, argc, argv);
+	} else if (!isatty(STDIN_FILENO) || (path && strcmp(path, "-") == 0)) {
 		/* Execute stdin */
 		if (luaL_loadfile(L, NULL) != 0)
 			panic("%s", lua_tostring(L, -1));
+		lua_main(L, argc, argv);
 	} else {
+		interactive = true;
+	}
+
+	/*
+	 * Start interactive mode when it was explicitly requested
+	 * by "-i" option or stdin is TTY or there are no script.
+	 */
+	if (interactive) {
 		say_crit("version %s\ntype 'help' for interactive help",
 			 tarantool_version());
 		/* get console.start from package.loaded */
@@ -501,17 +573,9 @@ run_script_f(va_list ap)
 		lua_remove(L, -2); /* remove package.loaded.console */
 		lua_remove(L, -2); /* remove package.loaded */
 		start_loop = false;
-	}
-	lua_checkstack(L, argc - 1);
-	for (int i = 1; i < argc; i++)
-		lua_pushstring(L, argv[i]);
-	if (luaT_call(L, lua_gettop(L) - 1, 0) != 0) {
-		struct error *e = diag_last_error(&fiber()->diag);
-		panic("%s", e->errmsg);
+		lua_main(L, argc, argv);
 	}
 
-	/* clear the stack from return values. */
-	lua_settop(L, 0);
 	/*
 	 * Lua script finished. Stop the auxiliary event loop and
 	 * return control back to tarantool_lua_run_script.
@@ -521,7 +585,8 @@ run_script_f(va_list ap)
 }
 
 void
-tarantool_lua_run_script(char *path, int argc, char **argv)
+tarantool_lua_run_script(char *path, bool interactive,
+			 int optc, char **optv, int argc, char **argv)
 {
 	const char *title = path ? basename(path) : "interactive";
 	/*
@@ -534,7 +599,8 @@ tarantool_lua_run_script(char *path, int argc, char **argv)
 	script_fiber = fiber_new(title, run_script_f);
 	if (script_fiber == NULL)
 		panic("%s", diag_last_error(diag_get())->errmsg);
-	fiber_start(script_fiber, tarantool_L, path, argc, argv);
+	fiber_start(script_fiber, tarantool_L, path, interactive,
+		    optc, optv, argc, argv);
 
 	/*
 	 * Run an auxiliary event loop to re-schedule run_script fiber.
diff --git a/src/lua/init.h b/src/lua/init.h
index 8330081dd550c0889773fc5c888991c57cd410be..ddf3e9195ac1d235d1fb109ec8214e62304e2d29 100644
--- a/src/lua/init.h
+++ b/src/lua/init.h
@@ -70,10 +70,14 @@ tarantool_lua_tostring(struct lua_State *L, int index);
 /**
  * Load and execute start-up file
  *
- * @param L is a Lua State.
+ * @param interactive force interactive mode
+ * @param argc argc the number of command line arguments
+ * @param argv argv command line arguments
  */
 void
-tarantool_lua_run_script(char *path, int argc, char **argv);
+tarantool_lua_run_script(char *path, bool force_interactive,
+			 int optc, char **optv,
+			 int argc, char **argv);
 
 extern char *history;
 
diff --git a/src/lua/socket.lua b/src/lua/socket.lua
index 84343ee4bc41dc297ae931b2c8ed152443953500..9270ada98f4f142fc0613d8893fd297b192ab9b6 100644
--- a/src/lua/socket.lua
+++ b/src/lua/socket.lua
@@ -600,9 +600,8 @@ local function read(self, limit, timeout, check, ...)
         return data
     end
 
-    local started = fiber.time()
     while timeout > 0 do
-        local started = fiber.time()
+        local started = fiber.clock()
 
         assert(rbuf:size() < limit)
         local to_read = math.min(limit - rbuf:size(), buffer.READAHEAD)
@@ -635,7 +634,7 @@ local function read(self, limit, timeout, check, ...)
         if timeout <= 0 then
             break
         end
-        timeout = timeout - ( fiber.time() - started )
+        timeout = timeout - ( fiber.clock() - started )
     end
     self._errno = boxerrno.ETIMEDOUT
     return nil
@@ -675,7 +674,7 @@ socket_methods.write = function(self, octets, timeout)
         return 0
     end
 
-    local started = fiber.time()
+    local started = fiber.clock()
     while true do
         local written = syswrite(self, p, e - p)
         if written == 0 then
@@ -690,7 +689,7 @@ socket_methods.write = function(self, octets, timeout)
             return nil
         end
 
-        timeout = timeout - (fiber.time() - started)
+        timeout = timeout - (fiber.clock() - started)
         if timeout <= 0 or not self:writable(timeout) then
             break
         end
@@ -957,7 +956,7 @@ local function tcp_connect(host, port, timeout)
             family = 'PF_UNIX', type = 'SOCK_STREAM' }, timeout)
     end
     local timeout = timeout or TIMEOUT_INFINITY
-    local stop = fiber.time() + timeout
+    local stop = fiber.clock() + timeout
     local dns = getaddrinfo(host, port, timeout, { type = 'SOCK_STREAM',
         protocol = 'tcp' })
     if dns == nil or #dns == 0 then
@@ -965,7 +964,7 @@ local function tcp_connect(host, port, timeout)
         return nil
     end
     for i, remote in pairs(dns) do
-        timeout = stop - fiber.time()
+        timeout = stop - fiber.clock()
         if timeout <= 0 then
             boxerrno(boxerrno.ETIMEDOUT)
             return nil
diff --git a/src/main.cc b/src/main.cc
index d82fe0f586cd18bedf90f7612632993ba29bc96e..9912b0e92a177660061e98ac4c8eedae90fca297 100644
--- a/src/main.cc
+++ b/src/main.cc
@@ -36,6 +36,7 @@
 #include <string.h>
 #include <unistd.h>
 #include <grp.h>
+#include <getopt.h>
 #include <sys/types.h>
 #include <sys/wait.h>
 #include <sys/types.h>
@@ -67,7 +68,6 @@
 #include "random.h"
 #include "tt_uuid.h"
 #include "iobuf.h"
-#include <third_party/gopt/gopt.h>
 #include "cfg.h"
 #include "version.h"
 #include <readline/readline.h>
@@ -87,14 +87,12 @@ static int main_argc;
 static ev_signal ev_sigs[4];
 static const int ev_sig_count = sizeof(ev_sigs)/sizeof(*ev_sigs);
 
-extern const void *opt_def;
-
 static double start_time;
 
 double
 tarantool_uptime(void)
 {
-	return ev_now(loop()) - start_time;
+	return ev_monotonic_now(loop()) - start_time;
 }
 
 /**
@@ -483,8 +481,6 @@ tarantool_free(void)
 #ifdef ENABLE_GCOV
 	__gcov_flush();
 #endif
-	if (script)
-		free(script);
 	/* tarantool_lua_free() was formerly reponsible for terminal reset,
 	 * but it is no longer called
 	 */
@@ -512,10 +508,41 @@ tarantool_free(void)
 	say_logger_free();
 }
 
+static void
+print_version(void)
+{
+	printf("Tarantool %s\n", tarantool_version());
+	printf("Target: %s\n", BUILD_INFO);
+	printf("Build options: %s\n", BUILD_OPTIONS);
+	printf("Compiler: %s\n", COMPILER_INFO);
+	printf("C_FLAGS:%s\n", TARANTOOL_C_FLAGS);
+	printf("CXX_FLAGS:%s\n", TARANTOOL_CXX_FLAGS);
+}
+
+static void
+print_help(const char *program)
+{
+	puts("Tarantool - a Lua application server");
+	puts("");
+	printf("Usage: %s script.lua [OPTIONS] [SCRIPT [ARGS]]\n", program);
+	puts("");
+	puts("All command line options are passed to the interpreted script.");
+	puts("When no script name is provided, the server responds to:");
+	puts("  -h, --help\t\t\tdisplay this help and exit");
+	puts("  -v, --version\t\t\tprint program version and exit");
+	puts("  -e EXPR\t\t\texecute string 'EXPR'");
+	puts("  -l NAME\t\t\trequire library 'NAME'");
+	puts("  -i\t\t\t\tenter interactive mode after executing 'SCRIPT'");
+	puts("  --\t\t\t\tstop handling options");
+	puts("  -\t\t\t\texecute stdin and stop handling options");
+	puts("");
+	puts("Please visit project home page at http://tarantool.org");
+	puts("to see online documentation, submit bugs or contribute a patch.");
+}
+
 int
 main(int argc, char **argv)
 {
-	start_time = ev_time();
 	/* set locale to make iswXXXX function work */
 	if (setlocale(LC_CTYPE, "C.UTF-8") == NULL &&
 	    setlocale(LC_CTYPE, "en_US.UTF-8") == NULL &&
@@ -523,48 +550,74 @@ main(int argc, char **argv)
 		fprintf(stderr, "Failed to set locale to C.UTF-8\n");
 	fpconv_check();
 
-	if (argc > 1 && access(argv[1], R_OK) != 0) {
-		if (argc == 2 && argv[1][0] != '-') {
+	/* Enter interactive mode after executing 'script' */
+	bool interactive = false;
+	/* Lua interpeter options, e.g. -e and -l */
+	int optc = 0;
+	char **optv = NULL;
+	auto guard = make_scoped_guard([=]{ if (optc) free(optv); });
+
+	static struct option longopts[] = {
+		{"help", no_argument, 0, 'h'},
+		{"version", no_argument, 0, 'v'},
+		{NULL, 0, 0, 0},
+	};
+	static const char *opts = "+hVvie:l:";
+
+	int ch;
+	while ((ch = getopt_long(argc, argv, opts, longopts, NULL)) != -1) {
+		switch (ch) {
+		case 'V':
+		case 'v':
+			print_version();
+			return 0;
+		case 'h':
+			print_help(basename(argv[0]));
+			return 0;
+		case 'i':
+			/* Force interactive mode */
+			interactive = true;
+			break;
+		case 'l':
+		case 'e':
+			/* Save Lua interepter options to optv as is */
+			if (optc == 0) {
+				optv = (char **) calloc(argc, sizeof(char *));
+				if (optv == NULL)
+					panic_syserror("No enough memory for arguments");
+			}
 			/*
-			 * Somebody made a mistake in the file
-			 * name. Be nice: open the file to set
-			 * errno.
+			 * The variable optind is the index of the next
+			 * element to be processed in argv.
 			 */
-			int fd = open(argv[1], O_RDONLY);
-			int save_errno = errno;
-			if (fd >= 0)
-				close(fd);
-			printf("Can't open script %s: %s\n", argv[1], strerror(save_errno));
-			return save_errno;
-		}
-		void *opt = gopt_sort(&argc, (const char **)argv, opt_def);
-		if (gopt(opt, 'V')) {
-			printf("Tarantool %s\n", tarantool_version());
-			printf("Target: %s\n", BUILD_INFO);
-			printf("Build options: %s\n", BUILD_OPTIONS);
-			printf("Compiler: %s\n", COMPILER_INFO);
-			printf("C_FLAGS:%s\n", TARANTOOL_C_FLAGS);
-			printf("CXX_FLAGS:%s\n", TARANTOOL_CXX_FLAGS);
-			return 0;
+			optv[optc++] = argv[optind - 2];
+			optv[optc++] = argv[optind - 1];
+			break;
+		default:
+			/* "invalid option" is printed by getopt */
+			return EX_USAGE;
 		}
+	}
 
-		if (gopt(opt, 'h') || argc == 1) {
-			puts("Tarantool - a Lua application server");
-			puts("");
-			printf("Usage: %s script.lua [OPTIONS]\n",
-			       basename(argv[0]));
-			puts("");
-			puts("All command line options are passed to the interpreted script.");
-			puts("When no script name is provided, the server responds to:");
-			gopt_help(opt_def);
-			puts("");
-			puts("Please visit project home page at http://tarantool.org");
-			puts("to see online documentation, submit bugs or contribute a patch.");
-			return 0;
-		}
-		fprintf(stderr, "Can't parse command line: try --help or -h for help.\n");
-		exit(EX_USAGE);
+	/* Shift arguments */
+	argc = 1 + (argc - optind);
+	for (int i = 1; i < argc; i++)
+		argv[i] = argv[optind + i - 1];
+
+	if (argc > 1 && strcmp(argv[1], "-") && access(argv[1], R_OK) != 0) {
+		/*
+		 * Somebody made a mistake in the file
+		 * name. Be nice: open the file to set
+		 * errno.
+		 */
+		int fd = open(argv[1], O_RDONLY);
+		int save_errno = errno;
+		if (fd >= 0)
+			close(fd);
+		printf("Can't open script %s: %s\n", argv[1], strerror(save_errno));
+		return save_errno;
 	}
+
 	argv = title_init(argc, argv);
 	/*
 	 * Support only #!/usr/bin/tarantol but not
@@ -583,7 +636,7 @@ main(int argc, char **argv)
 	if (argc > 1) {
 		argv++;
 		argc--;
-		script = abspath(argv[0]);
+		script = argv[0];
 		title_set_script_name(argv[0]);
 	}
 
@@ -596,7 +649,6 @@ main(int argc, char **argv)
 	main_argv = argv;
 
 	exception_init();
-	box_error_init();
 
 	fiber_init(fiber_cxx_invoke);
 	/* Init iobuf library with default readahead */
@@ -607,6 +659,8 @@ main(int argc, char **argv)
 	cbus_init();
 	tarantool_lua_init(tarantool_bin, main_argc, main_argv);
 
+	start_time = ev_monotonic_time();
+
 	try {
 		box_init();
 		box_lua_init(tarantool_L);
@@ -624,7 +678,8 @@ main(int argc, char **argv)
 		 * is why script must run only after the server was fully
 		 * initialized.
 		 */
-		tarantool_lua_run_script(script, main_argc, main_argv);
+		tarantool_lua_run_script(script, interactive, optc, optv,
+					 main_argc, main_argv);
 		/*
 		 * Start event loop after executing Lua script if signal_cb()
 		 * wasn't triggered and there is some new events. Initial value
diff --git a/src/rmean.c b/src/rmean.c
index 2752a37960fcd0d33d04ddc672233a4941de5be1..222441f754e50e7b6941cd366b8eec36d7e68dda 100644
--- a/src/rmean.c
+++ b/src/rmean.c
@@ -90,7 +90,7 @@ rmean_age(ev_loop *loop,
 	struct rmean *rmean = (struct rmean *) timer->data;
 
 	double dt = rmean->prev_ts;
-	rmean->prev_ts = ev_now(loop);
+	rmean->prev_ts = ev_monotonic_now(loop);
 	dt = rmean->prev_ts - dt;
 	for (size_t i = 0; i < rmean->stats_n; i++) {
 		if (rmean->stats[i].name == NULL)
@@ -114,7 +114,7 @@ rmean_new(const char **name, size_t n)
 	for (size_t i = 0; i < n; i++, name++) {
 		rmean->stats[i].name = *name;
 	}
-	rmean->prev_ts = ev_now(loop());
+	rmean->prev_ts = ev_monotonic_now(loop());
 	ev_timer_init(&rmean->timer, rmean_age, 0, 1.);
 	ev_timer_again(loop(), &rmean->timer);
 	return rmean;
diff --git a/test/app-tap/init_script.result b/test/app-tap/init_script.result
index afa2d26f13a87eceb76b1ce4014e689785f4dbcc..94a6feab01186776b78524d0b2a7fe2038915a91 100644
--- a/test/app-tap/init_script.result
+++ b/test/app-tap/init_script.result
@@ -20,26 +20,27 @@ box.cfg
 15	pid_file:box.pid
 16	read_only:false
 17	readahead:16320
-18	rows_per_wal:500000
-19	slab_alloc_factor:1.05
-20	too_long_threshold:0.5
-21	vinyl_bloom_fpr:0.05
-22	vinyl_cache:134217728
-23	vinyl_dir:.
-24	vinyl_max_tuple_size:1048576
-25	vinyl_memory:134217728
-26	vinyl_page_size:8192
-27	vinyl_range_size:1073741824
-28	vinyl_read_threads:1
-29	vinyl_run_count_per_level:2
-30	vinyl_run_size_ratio:3.5
-31	vinyl_timeout:60
-32	vinyl_write_threads:2
-33	wal_dir:.
-34	wal_dir_rescan_delay:2
-35	wal_max_size:268435456
-36	wal_mode:write
-37	worker_pool_threads:4
+18	replication_timeout:10
+19	rows_per_wal:500000
+20	slab_alloc_factor:1.05
+21	too_long_threshold:0.5
+22	vinyl_bloom_fpr:0.05
+23	vinyl_cache:134217728
+24	vinyl_dir:.
+25	vinyl_max_tuple_size:1048576
+26	vinyl_memory:134217728
+27	vinyl_page_size:8192
+28	vinyl_range_size:1073741824
+29	vinyl_read_threads:1
+30	vinyl_run_count_per_level:2
+31	vinyl_run_size_ratio:3.5
+32	vinyl_timeout:60
+33	vinyl_write_threads:2
+34	wal_dir:.
+35	wal_dir_rescan_delay:2
+36	wal_max_size:268435456
+37	wal_mode:write
+38	worker_pool_threads:4
 --
 -- Test insert from detached fiber
 --
diff --git a/test/box-py/args.lua b/test/box-py/args.lua
new file mode 100755
index 0000000000000000000000000000000000000000..54956647ee99505bc048ee28913e6d2186679502
--- /dev/null
+++ b/test/box-py/args.lua
@@ -0,0 +1,5 @@
+#!/usr/bin/env tarantool
+
+for i=-1,#arg do
+    print(string.format("arg[%d] => %s", i, arg[i]))
+end
diff --git a/test/box-py/args.result b/test/box-py/args.result
index a05ebb7ed2a9e7a3b111059ba61a9801ce87f816..df58d7adf512ccb9c5fd36d5f19fcaf24ba2a0b4 100644
--- a/test/box-py/args.result
+++ b/test/box-py/args.result
@@ -1,12 +1,17 @@
 tarantool --help
 Tarantool - a Lua application server
 
-Usage: tarantool script.lua [OPTIONS]
+Usage: tarantool script.lua [OPTIONS] [SCRIPT [ARGS]]
 
 All command line options are passed to the interpreted script.
 When no script name is provided, the server responds to:
-  -h, --help                       display this help and exit
-  -V, --version                    print program version and exit
+  -h, --help			display this help and exit
+  -v, --version			print program version and exit
+  -e EXPR			execute string 'EXPR'
+  -l NAME			require library 'NAME'
+  -i				enter interactive mode after executing 'SCRIPT'
+  --				stop handling options
+  -				execute stdin and stop handling options
 
 Please visit project home page at http://tarantool.org
 to see online documentation, submit bugs or contribute a patch.
@@ -14,24 +19,29 @@ to see online documentation, submit bugs or contribute a patch.
 tarantool -h
 Tarantool - a Lua application server
 
-Usage: tarantool script.lua [OPTIONS]
+Usage: tarantool script.lua [OPTIONS] [SCRIPT [ARGS]]
 
 All command line options are passed to the interpreted script.
 When no script name is provided, the server responds to:
-  -h, --help                       display this help and exit
-  -V, --version                    print program version and exit
+  -h, --help			display this help and exit
+  -v, --version			print program version and exit
+  -e EXPR			execute string 'EXPR'
+  -l NAME			require library 'NAME'
+  -i				enter interactive mode after executing 'SCRIPT'
+  --				stop handling options
+  -				execute stdin and stop handling options
 
 Please visit project home page at http://tarantool.org
 to see online documentation, submit bugs or contribute a patch.
 
 tarantool -Z
-tarantool: -Z: unknown option
+tarantool: invalid option
 
 tarantool --no-such-option
-tarantool: --no-such-option: unknown option
+tarantool: unrecognized option
 
-tarantool --version --no-such-option
-tarantool: --no-such-option: unknown option
+tarantool --no-such-option --version
+tarantool: unrecognized option
 
 tarantool --version
 Tarantool 1.minor.patch-<rev>-<commit>
@@ -41,6 +51,14 @@ Compiler: cc
 C_FLAGS: flags
 CXX_FLAGS: flags
 
+tarantool -v
+Tarantool 1.minor.patch-<rev>-<commit>
+Target: platform <build>
+Build options: flags
+Compiler: cc
+C_FLAGS: flags
+CXX_FLAGS: flags
+
 tarantool -V
 Tarantool 1.minor.patch-<rev>-<commit>
 Target: platform <build>
@@ -49,3 +67,82 @@ Compiler: cc
 C_FLAGS: flags
 CXX_FLAGS: flags
 
+tarantool ${SOURCEDIR}/test/box-py/args.lua
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+
+tarantool ${SOURCEDIR}/test/box-py/args.lua 1 2 3
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => 1
+arg[2] => 2
+arg[3] => 3
+
+tarantool ${SOURCEDIR}/test/box-py/args.lua 1 2 3 -V
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => 1
+arg[2] => 2
+arg[3] => 3
+arg[4] => -V
+
+tarantool ${SOURCEDIR}/test/box-py/args.lua -V 1 2 3
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => -V
+arg[2] => 1
+arg[3] => 2
+arg[4] => 3
+
+tarantool ${SOURCEDIR}/test/box-py/args.lua 1 2 3 --help
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => 1
+arg[2] => 2
+arg[3] => 3
+arg[4] => --help
+
+tarantool ${SOURCEDIR}/test/box-py/args.lua --help 1 2 3
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => --help
+arg[2] => 1
+arg[3] => 2
+arg[4] => 3
+
+tarantool -V ${SOURCEDIR}/test/box-py/args.lua 1 2 3
+Tarantool 1.minor.patch-<rev>-<commit>
+Target: platform <build>
+Build options: flags
+Compiler: cc
+C_FLAGS: flags
+CXX_FLAGS: flags
+
+tarantool -e print('Hello') ${SOURCEDIR}/test/box-py/args.lua 1 2 3
+Hello
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => 1
+arg[2] => 2
+arg[3] => 3
+
+tarantool -e a = 10 -e print(a) ${SOURCEDIR}/test/box-py/args.lua 1 2 3 --help
+10
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => 1
+arg[2] => 2
+arg[3] => 3
+arg[4] => --help
+
+tarantool -e print(rawget(_G, 'log') == nil) -e io.flush() -l log -e print(log.info('Hello')) ${SOURCEDIR}/test/box-py/args.lua 1 2 3 --help
+true
+Hello
+
+arg[-1] => tarantool
+arg[0] => ${SOURCEDIR}/test/box-py/args.lua
+arg[1] => 1
+arg[2] => 2
+arg[3] => 3
+arg[4] => --help
+
diff --git a/test/box-py/args.test.py b/test/box-py/args.test.py
index e5fd0d641e5ccc861c9319dc9020e073a9f271a1..4d1c9cf7353613c91f7512d416f2e631a9fbd1c1 100644
--- a/test/box-py/args.test.py
+++ b/test/box-py/args.test.py
@@ -1,14 +1,21 @@
 import sys
 import os
+import re
 
 # mask BFD warnings: https://bugs.launchpad.net/tarantool/+bug/1018356
 sys.stdout.push_filter("unable to read unknown load command 0x2\d+", "")
 server.test_option("--help")
 server.test_option("-h")
-sys.stdout.push_filter("(/\S+)+/tarantool", "tarantool")
+# Replace with the same value for case when builddir inside source dir
+sys.stdout.push_filter(re.escape(os.getenv("BUILDDIR")+'/src/tarantool'), "tarantool")
+sys.stdout.push_filter(re.escape(os.getenv("BUILDDIR")), "${SOURCEDIR}")
+sys.stdout.push_filter(re.escape(os.getenv("SOURCEDIR")+'/src/tarantool'), "tarantool")
+sys.stdout.push_filter(re.escape(os.getenv("SOURCEDIR")), "${SOURCEDIR}")
+sys.stdout.push_filter("invalid option.*", "invalid option")
+sys.stdout.push_filter("unrecognized option.*", "unrecognized option")
 server.test_option("-Z")
 server.test_option("--no-such-option")
-server.test_option("--version --no-such-option")
+server.test_option("--no-such-option --version")
 sys.stdout.push_filter("(\d)\.\d\.\d(-\d+-\w+)?", "\\1.minor.patch-<rev>-<commit>")
 sys.stdout.push_filter("Target: .*", "Target: platform <build>")
 sys.stdout.push_filter(".*Disable shared arena since.*\n", "")
@@ -18,8 +25,30 @@ sys.stdout.push_filter("CXX_FLAGS:.*", "CXX_FLAGS: flags")
 sys.stdout.push_filter("Compiler: .*", "Compiler: cc")
 
 server.test_option("--version")
+server.test_option("-v")
 server.test_option("-V          ")
-sys.stdout.clear_all_filters()
 
+script = os.getenv("SOURCEDIR") + "/test/box-py/args.lua"
+server.test_option(script)
+server.test_option(script + " 1 2 3")
+server.test_option(script + " 1 2 3 -V")
+server.test_option(script + " -V 1 2 3")
+server.test_option(script + " 1 2 3 --help")
+server.test_option(script + " --help 1 2 3")
+server.test_option("-V " + script + " 1 2 3")
+
+server.test_option("-e \"print('Hello')\" " + script + " 1 2 3")
+server.test_option("-e \"a = 10\" " + \
+                   "-e print(a) " + \
+                   script + \
+                   " 1 2 3 --help")
+server.test_option("-e \"print(rawget(_G, 'log') == nil)\" " + \
+                   "-e io.flush() " + \
+                   "-l log " + \
+                   "-e \"print(log.info('Hello'))\" " + \
+                   script + \
+                   " 1 2 3 --help")
+
+sys.stdout.clear_all_filters()
 # Args filter cleanup
 # vim: syntax=python
diff --git a/test/box/admin.result b/test/box/admin.result
index 94d087b5cbd80c97d104bea7ca45eb6be010c569..b088c0b6c37a4a9774801f1e8a71dc05d533e057 100644
--- a/test/box/admin.result
+++ b/test/box/admin.result
@@ -60,6 +60,8 @@ cfg_filter(box.cfg)
     - false
   - - readahead
     - 16320
+  - - replication_timeout
+    - 10
   - - rows_per_wal
     - 500000
   - - slab_alloc_factor
diff --git a/test/box/cfg.result b/test/box/cfg.result
index 9098ee7e6b517f1b5f6b721aea12518c69495066..17ed3bbd372025751320fe948be0f436ccc70a30 100644
--- a/test/box/cfg.result
+++ b/test/box/cfg.result
@@ -48,6 +48,8 @@ cfg_filter(box.cfg)
     - false
   - - readahead
     - 16320
+  - - replication_timeout
+    - 10
   - - rows_per_wal
     - 500000
   - - slab_alloc_factor
@@ -129,6 +131,8 @@ cfg_filter(box.cfg)
     - false
   - - readahead
     - 16320
+  - - replication_timeout
+    - 10
   - - rows_per_wal
     - 500000
   - - slab_alloc_factor
diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt
index d43437fcd50815258f5f70047e7a2e3466432d6c..67c5dde908fb2fa133f6247b3b5795b64d144b28 100644
--- a/test/unit/CMakeLists.txt
+++ b/test/unit/CMakeLists.txt
@@ -148,6 +148,25 @@ set(ITERATOR_TEST_LIBS core tuple xrow unit)
 add_executable(vy_mem.test vy_mem.c ${ITERATOR_TEST_SOURCES})
 target_link_libraries(vy_mem.test ${ITERATOR_TEST_LIBS})
 
+add_executable(vy_point_iterator.test
+    vy_point_iterator.c
+    vy_iterators_helper.c
+    vy_log_stub.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_point_iterator.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_write_iterator.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_stmt.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_mem.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_run.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_range.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_tx.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_upsert.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_index.c
+    ${PROJECT_SOURCE_DIR}/src/box/vy_cache.c
+    ${PROJECT_SOURCE_DIR}/src/box/index_def.c
+    ${PROJECT_SOURCE_DIR}/src/box/schema_def.c
+)
+target_link_libraries(vy_point_iterator.test core tuple xrow xlog unit)
+
 add_executable(column_mask.test
     column_mask.c)
 target_link_libraries(column_mask.test tuple unit)
diff --git a/test/unit/vy_iterators_helper.c b/test/unit/vy_iterators_helper.c
index 170cfbe265efcbf7bf2a6277f37b9b864ee68ffe..1fee0474d2aadefb7d0637273b64a2c5ea8d31a7 100644
--- a/test/unit/vy_iterators_helper.c
+++ b/test/unit/vy_iterators_helper.c
@@ -88,9 +88,15 @@ vy_new_simple_stmt(struct tuple_format *format,
 		 * important.
 		 */
 		struct iovec operations[1];
-		char tmp[16];
+		char tmp[32];
 		char *ops = mp_encode_array(tmp, 1);
-		ops = mp_encode_array(ops, 0);
+		ops = mp_encode_array(ops, 3);
+		ops = mp_encode_str(ops, "+", 1);
+		ops = mp_encode_uint(ops, templ->upsert_field);
+		if (templ->upsert_value >= 0)
+			ops = mp_encode_uint(ops, templ->upsert_value);
+		else
+			ops = mp_encode_int(ops, templ->upsert_value);
 		operations[0].iov_base = tmp;
 		operations[0].iov_len = ops - tmp;
 		fail_if(templ->optimize_update);
diff --git a/test/unit/vy_iterators_helper.h b/test/unit/vy_iterators_helper.h
index d69297f67eb1d6232ff232037b70322436475c6e..a89fc4b904ad19a8c88592b7a29dfb002d5adc84 100644
--- a/test/unit/vy_iterators_helper.h
+++ b/test/unit/vy_iterators_helper.h
@@ -43,10 +43,10 @@
 #define vyend 99999999
 #define MAX_FIELDS_COUNT 100
 #define STMT_TEMPLATE(lsn, type, ...) \
-{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, false }
+{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, false, 0, 0 }
 
 #define STMT_TEMPLATE_OPTIMIZED(lsn, type, ...) \
-{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, true }
+{ { __VA_ARGS__, vyend }, IPROTO_##type, lsn, true, 0, 0 }
 
 extern struct tuple_format_vtab vy_tuple_format_vtab;
 extern struct tuple_format *vy_key_format;
@@ -72,7 +72,7 @@ vy_iterator_C_test_finish();
 struct vy_stmt_template {
 	/** Array of statement fields, ended with 'vyend'. */
 	const int fields[MAX_FIELDS_COUNT];
-	/** Statement type: REPLACE/UPSERT/DELETE. */
+	/** Statement type: REPLACE/UPSERT/DELETE/UPSERT. */
 	enum iproto_type type;
 	/** Statement lsn. */
 	int64_t lsn;
@@ -81,6 +81,13 @@ struct vy_stmt_template {
 	 * to skip it in the write_iterator.
 	 */
 	bool optimize_update;
+	/*
+	 * In case of upsert it is possible to use only one 'add' operation.
+	 * This is the column number of the operation.
+	 */
+	uint32_t upsert_field;
+	/** And that is the value to add. */
+	int32_t upsert_value;
 };
 
 /**
diff --git a/test/unit/vy_log_stub.c b/test/unit/vy_log_stub.c
new file mode 100644
index 0000000000000000000000000000000000000000..daabf3f9ea1f94dc429a1551afe221614c45fbdd
--- /dev/null
+++ b/test/unit/vy_log_stub.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2010-2017, Tarantool AUTHORS, please see AUTHORS file.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above
+ *    copyright notice, this list of conditions and the
+ *    following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above
+ *    copyright notice, this list of conditions and the following
+ *    disclaimer in the documentation and/or other materials
+ *    provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ * AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+#include "vy_log.h"
+
+#include <trivia/util.h>
+
+int64_t
+vy_log_next_id(void)
+{
+	static int64_t id = 0;
+	return id++;
+}
+
+void
+vy_log_tx_begin(void) {}
+
+int
+vy_log_tx_commit(void)
+{
+	return 0;
+}
+
+void
+vy_log_write(const struct vy_log_record *record) {}
+
+int
+vy_recovery_load_index(struct vy_recovery *recovery,
+		       uint32_t space_id, uint32_t index_id,
+		       int64_t index_lsn, bool snapshot_recovery,
+		       vy_recovery_cb cb, void *cb_arg)
+{
+	unreachable();
+}
diff --git a/test/unit/vy_point_iterator.c b/test/unit/vy_point_iterator.c
new file mode 100644
index 0000000000000000000000000000000000000000..d9c763018e3894a5e8e24fbe2eac77cf1b2ba6ab
--- /dev/null
+++ b/test/unit/vy_point_iterator.c
@@ -0,0 +1,313 @@
+#include "trivia/util.h"
+#include "unit.h"
+#include "vy_index.h"
+#include "vy_cache.h"
+#include "vy_run.h"
+#include "fiber.h"
+#include <small/lsregion.h>
+#include <small/slab_cache.h>
+#include <bit/bit.h>
+#include <crc32.h>
+#include <box/vy_point_iterator.h>
+#include "vy_iterators_helper.h"
+#include "vy_write_iterator.h"
+
+uint64_t schema_version;
+
+static void
+test_basic()
+{
+	header();
+	plan(15);
+
+	const size_t QUOTA = 100 * 1024 * 1024;
+	int64_t generation = 0;
+	struct slab_cache *slab_cache = cord_slab_cache();
+	struct lsregion lsregion;
+	lsregion_create(&lsregion, slab_cache->arena);
+
+	int rc;
+	struct vy_index_env index_env;
+	rc = vy_index_env_create(&index_env, ".", &lsregion, &generation,
+				 NULL, NULL);
+	is(rc, 0, "vy_index_env_create");
+
+	struct vy_run_env run_env;
+	vy_run_env_create(&run_env);
+
+	struct vy_cache_env cache_env;
+	vy_cache_env_create(&cache_env, slab_cache, QUOTA);
+
+	struct vy_cache cache;
+	uint32_t fields[] = { 0 };
+	uint32_t types[] = { FIELD_TYPE_UNSIGNED };
+	struct key_def *key_def = box_key_def_new(fields, types, 1);
+	isnt(key_def, NULL, "key_def is not NULL");
+
+	vy_cache_create(&cache, &cache_env, key_def);
+
+	struct tuple_format *format =
+		tuple_format_new(&vy_tuple_format_vtab, &key_def, 1, 0);
+	isnt(format, NULL, "tuple_format_new is not NULL");
+	tuple_format_ref(format);
+
+	struct index_opts index_opts = index_opts_default;
+	struct index_def *index_def =
+		index_def_new(512, 0, "primary", sizeof("primary"), TREE,
+			      &index_opts, key_def, NULL);
+
+	struct vy_index *pk = vy_index_new(&index_env, &cache_env, index_def,
+					   format, NULL);
+	isnt(pk, NULL, "index is not NULL")
+
+	struct vy_range *range = vy_range_new(1, NULL, NULL, pk->cmp_def);
+
+	isnt(pk, NULL, "range is not NULL")
+	vy_index_add_range(pk, range);
+
+	struct rlist read_views = RLIST_HEAD_INITIALIZER(read_views);
+
+	char dir_tmpl[] = "./vy_point_test.XXXXXX";
+	char *dir_name = mkdtemp(dir_tmpl);
+	isnt(dir_name, NULL, "temp dir name is not NULL")
+	char path[PATH_MAX];
+	strcpy(path, dir_name);
+	strcat(path, "/0");
+	rc = mkdir(path, 0777);
+	is(rc, 0, "temp dir create (2)");
+	strcat(path, "/0");
+	rc = mkdir(path, 0777);
+	is(rc, 0, "temp dir create (3)");
+
+	/* Filling the index with test data */
+	/* Prepare variants */
+	const size_t num_of_keys = 100;
+	bool in_mem1[num_of_keys]; /* UPSERT value += 1, lsn 4 */
+	bool in_mem2[num_of_keys]; /* UPSERT value += 2, lsn 3 */
+	bool in_run1[num_of_keys]; /* UPSERT value += 4, lsn 2 */
+	bool in_run2[num_of_keys]; /* UPSERT value += 8, lsn 1 */
+	bool in_cache[num_of_keys];
+	uint32_t expect[num_of_keys];
+	int64_t expect_lsn[num_of_keys];
+	for (size_t i = 0; i < num_of_keys; i++) {
+		in_mem1[i] = i & 1;
+		in_mem2[i] = i & 2;
+		in_run1[i] = i & 4;
+		in_run2[i] = i & 8;
+		in_cache[i] = i & 16;
+		expect[i] = (in_mem1[i] ? 1 : 0) + (in_mem2[i] ? 2 : 0) +
+			    (in_run1[i] ? 4 : 0) + (in_run2[i] ? 8 : 0);
+		expect_lsn[i] = expect[i] == 0 ? 0 : 5 - bit_ctz_u32(expect[i]);
+	}
+
+	for (size_t i = 0; i < num_of_keys; i++) {
+		if (!in_cache[i])
+			continue;
+		if (expect[i] != 0) {
+			struct vy_stmt_template tmpl_key =
+				STMT_TEMPLATE(0, SELECT, i);
+			struct vy_stmt_template tmpl_val =
+				STMT_TEMPLATE(expect_lsn[i], REPLACE, i, expect[i]);
+			vy_cache_insert_templates_chain(&cache, format,
+							&tmpl_val, 1, &tmpl_key,
+							ITER_EQ);
+		}
+	}
+
+	/* create second mem */
+	for (size_t i = 0; i < num_of_keys; i++) {
+		if (!in_mem2[i])
+			continue;
+		struct vy_stmt_template tmpl_val =
+			STMT_TEMPLATE(3, UPSERT, i, 2);
+		tmpl_val.upsert_field = 1;
+		tmpl_val.upsert_value = 2;
+		vy_mem_insert_template(pk->mem, &tmpl_val);
+	}
+
+	rc = vy_index_rotate_mem(pk);
+	is(rc, 0, "vy_index_rotate_mem");
+
+	/* create first mem */
+	for (size_t i = 0; i < num_of_keys; i++) {
+		if (!in_mem1[i])
+			continue;
+		struct vy_stmt_template tmpl_val =
+			STMT_TEMPLATE(4, UPSERT, i, 1);
+		tmpl_val.upsert_field = 1;
+		tmpl_val.upsert_value = 1;
+		vy_mem_insert_template(pk->mem, &tmpl_val);
+	}
+
+	/* create second run */
+	struct vy_mem *run_mem =
+		vy_mem_new(pk->env->allocator, *pk->env->p_generation,
+			   pk->cmp_def, pk->mem_format,
+			   pk->mem_format_with_colmask,
+			   pk->upsert_format, 0);
+
+	for (size_t i = 0; i < num_of_keys; i++) {
+		if (!in_run2[i])
+			continue;
+		struct vy_stmt_template tmpl_val =
+			STMT_TEMPLATE(1, UPSERT, i, 8);
+		tmpl_val.upsert_field = 1;
+		tmpl_val.upsert_value = 8;
+		vy_mem_insert_template(run_mem, &tmpl_val);
+	}
+	struct vy_stmt_stream *write_stream
+		= vy_write_iterator_new(pk->cmp_def, pk->disk_format,
+					pk->upsert_format, pk->id == 0,
+					true, &read_views);
+	vy_write_iterator_new_mem(write_stream, run_mem);
+	struct vy_run *run = vy_run_new(1);
+	isnt(run, NULL, "vy_run_new");
+
+	rc = vy_run_write(run, dir_name, 0, pk->id,
+			  write_stream, 4096, pk->cmp_def, pk->key_def,
+			  100500, 0.1);
+	is(rc, 0, "vy_run_write");
+
+	write_stream->iface->close(write_stream);
+	vy_mem_delete(run_mem);
+
+	vy_index_add_run(pk, run);
+	struct vy_slice *slice = vy_slice_new(1, run, NULL, NULL, pk->cmp_def);
+	vy_range_add_slice(range, slice);
+	vy_run_unref(run);
+
+	/* create first run */
+	run_mem =
+		vy_mem_new(pk->env->allocator, *pk->env->p_generation,
+			   pk->cmp_def, pk->mem_format,
+			   pk->mem_format_with_colmask,
+			   pk->upsert_format, 0);
+
+	for (size_t i = 0; i < num_of_keys; i++) {
+		if (!in_run1[i])
+			continue;
+		struct vy_stmt_template tmpl_val =
+			STMT_TEMPLATE(2, UPSERT, i, 4);
+		tmpl_val.upsert_field = 1;
+		tmpl_val.upsert_value = 4;
+		vy_mem_insert_template(run_mem, &tmpl_val);
+	}
+	write_stream
+		= vy_write_iterator_new(pk->cmp_def, pk->disk_format,
+					pk->upsert_format, pk->id == 0,
+					true, &read_views);
+	vy_write_iterator_new_mem(write_stream, run_mem);
+	run = vy_run_new(2);
+	isnt(run, NULL, "vy_run_new");
+
+	rc = vy_run_write(run, dir_name, 0, pk->id,
+			  write_stream, 4096, pk->cmp_def, pk->key_def,
+			  100500, 0.1);
+	is(rc, 0, "vy_run_write");
+
+	write_stream->iface->close(write_stream);
+	vy_mem_delete(run_mem);
+
+	vy_index_add_run(pk, run);
+	slice = vy_slice_new(1, run, NULL, NULL, pk->cmp_def);
+	vy_range_add_slice(range, slice);
+	vy_run_unref(run);
+
+	/* Compare with expected */
+	bool results_ok = true;
+	bool has_errors = false;
+	for (int64_t vlsn = 0; vlsn <= 6; vlsn++) {
+		struct vy_read_view rv;
+		rv.vlsn = vlsn == 6 ? INT64_MAX : vlsn;
+		const struct vy_read_view *prv = &rv;
+
+		for (size_t i = 0; i < num_of_keys; i++) {
+			uint32_t expect = 0;
+			int64_t expect_lsn = 0;
+			if (in_run2[i] && vlsn >= 1) {
+				expect += 8;
+				expect_lsn = 1;
+			}
+			if (in_run1[i] && vlsn >= 2) {
+				expect += 4;
+				expect_lsn = 2;
+			}
+			if (in_mem2[i] && vlsn >= 3) {
+				expect += 2;
+				expect_lsn = 3;
+			}
+			if (in_mem1[i] && vlsn >= 4) {
+				expect += 1;
+				expect_lsn = 4;
+			}
+
+			struct vy_stmt_template tmpl_key =
+				STMT_TEMPLATE(0, SELECT, i);
+			struct tuple *key =
+				vy_new_simple_stmt(format, pk->upsert_format,
+						   pk->mem_format_with_colmask,
+						   &tmpl_key);
+			struct vy_point_iterator itr;
+			vy_point_iterator_open(&itr, &run_env, pk,
+					       NULL, &prv, key);
+			struct tuple *res;
+			rc = vy_point_iterator_get(&itr, &res);
+			tuple_unref(key);
+			if (rc != 0) {
+				has_errors = true;
+				continue;
+			}
+			if (expect == 0) {
+				/* No value expected. */
+				if (res != NULL)
+					results_ok = false;
+				continue;
+			} else {
+				if (res == NULL) {
+					results_ok = false;
+					continue;
+				}
+			}
+			uint32_t got = 0;
+			tuple_field_u32(res, 1, &got);
+			if (got != expect && expect_lsn != vy_stmt_lsn(res))
+				results_ok = false;
+		}
+	}
+
+	is(results_ok, true, "select results");
+	is(has_errors, false, "no errors happened");
+
+	vy_index_unref(pk);
+	index_def_delete(index_def);
+	tuple_format_unref(format);
+	vy_cache_destroy(&cache);
+	box_key_def_delete(key_def);
+	vy_cache_env_destroy(&cache_env);
+	vy_run_env_destroy(&run_env);
+	vy_index_env_destroy(&index_env);
+
+	lsregion_destroy(&lsregion);
+
+	strcpy(path, "rm -rf ");
+	strcat(path, dir_name);
+	system(path);
+
+	check_plan();
+	footer();
+}
+
+int
+main()
+{
+	plan(1);
+
+	vy_iterator_C_test_init(128 * 1024);
+	crc32_init();
+
+	test_basic();
+
+	vy_iterator_C_test_finish();
+
+	return check_plan();
+}
diff --git a/test/unit/vy_point_iterator.result b/test/unit/vy_point_iterator.result
new file mode 100644
index 0000000000000000000000000000000000000000..6b8dcdefce01f98da86431c33dc0f6c6b64d2924
--- /dev/null
+++ b/test/unit/vy_point_iterator.result
@@ -0,0 +1,20 @@
+1..1
+	*** test_basic ***
+    1..15
+    ok 1 - vy_index_env_create
+    ok 2 - key_def is not NULL
+    ok 3 - tuple_format_new is not NULL
+    ok 4 - index is not NULL
+    ok 5 - range is not NULL
+    ok 6 - temp dir name is not NULL
+    ok 7 - temp dir create (2)
+    ok 8 - temp dir create (3)
+    ok 9 - vy_index_rotate_mem
+    ok 10 - vy_run_new
+    ok 11 - vy_run_write
+    ok 12 - vy_run_new
+    ok 13 - vy_run_write
+    ok 14 - select results
+    ok 15 - no errors happened
+ok 1 - subtests
+	*** test_basic: done ***
diff --git a/test/vinyl/ddl.result b/test/vinyl/ddl.result
index 67a6eecad48ff8df4b97d30b22b4c5bfa6087fe0..6b67673da35445f3dcea0210d3344eafcfff14cb 100644
--- a/test/vinyl/ddl.result
+++ b/test/vinyl/ddl.result
@@ -469,3 +469,147 @@ test_run:cmd("setopt delimiter ''");
 s:drop()
 ---
 ...
+-- gh-2342 cursors after death of index
+create_iterator = require('utils').create_iterator
+---
+...
+s = box.schema.space.create('test', { engine = 'vinyl' })
+---
+...
+pk = s:create_index('primary', { parts = { 1, 'uint' } })
+---
+...
+sk = s:create_index('sec', { parts = { 2, 'uint' } })
+---
+...
+s:replace{1, 2, 3}
+---
+- [1, 2, 3]
+...
+s:replace{4, 5, 6}
+---
+- [4, 5, 6]
+...
+s:replace{7, 8, 9}
+---
+- [7, 8, 9]
+...
+itr = create_iterator(s, {})
+---
+...
+f, ctx, state = s.index.sec:pairs({5}, { iterator='LE' })
+---
+...
+itr.next()
+---
+- [1, 2, 3]
+...
+f(ctx, state)
+---
+- <iterator state>
+- [4, 5, 6]
+...
+s:drop()
+---
+...
+itr.next()
+---
+- null
+...
+f(ctx, state)
+---
+- null
+...
+f = nil
+---
+...
+ctx = nil
+---
+...
+state = nil
+---
+...
+itr = nil
+---
+...
+collectgarbage('collect')
+---
+- 0
+...
+-- gh-2342 drop space if transaction is in progress
+ch = fiber.channel(1)
+---
+...
+s = box.schema.space.create('test', { engine = 'vinyl' })
+---
+...
+pk = s:create_index('primary', { parts = { 1, 'uint' } })
+---
+...
+sk = s:create_index('sec', { parts = { 2, 'uint' } })
+---
+...
+box.begin()
+---
+...
+s:replace({1, 2, 3})
+---
+- [1, 2, 3]
+...
+s:replace({4, 5, 6})
+---
+- [4, 5, 6]
+...
+s:replace({7, 8, 9})
+---
+- [7, 8, 9]
+...
+s:upsert({10, 11, 12}, {})
+---
+...
+_ = fiber.create(function () s:drop() ch:put(true) end)
+---
+...
+box.commit()
+---
+- error: Transaction has been aborted by conflict
+...
+ch:get()
+---
+- true
+...
+s = box.schema.space.create('test', { engine = 'vinyl' })
+---
+...
+pk = s:create_index('primary', { parts = { 1, 'uint' } })
+---
+...
+sk = s:create_index('sec', { parts = { 2, 'uint' } })
+---
+...
+box.begin()
+---
+...
+s:replace{1, 2, 3}
+---
+- [1, 2, 3]
+...
+s:replace{4, 5, 6}
+---
+- [4, 5, 6]
+...
+s:replace{7, 8, 9}
+---
+- [7, 8, 9]
+...
+_ = fiber.create(function () s:drop() ch:put(true) end)
+---
+...
+ch:get()
+---
+- true
+...
+box.commit()
+---
+- error: Transaction has been aborted by conflict
+...
diff --git a/test/vinyl/ddl.test.lua b/test/vinyl/ddl.test.lua
index f3d30e38be225df2394e9aeae0835dc05f299119..47d4bd873554744fd309a8f947cc2914572d8630 100644
--- a/test/vinyl/ddl.test.lua
+++ b/test/vinyl/ddl.test.lua
@@ -196,3 +196,50 @@ end ;
 test_run:cmd("setopt delimiter ''");
 
 s:drop()
+
+-- gh-2342 cursors after death of index
+create_iterator = require('utils').create_iterator
+s = box.schema.space.create('test', { engine = 'vinyl' })
+pk = s:create_index('primary', { parts = { 1, 'uint' } })
+sk = s:create_index('sec', { parts = { 2, 'uint' } })
+s:replace{1, 2, 3}
+s:replace{4, 5, 6}
+s:replace{7, 8, 9}
+itr = create_iterator(s, {})
+f, ctx, state = s.index.sec:pairs({5}, { iterator='LE' })
+itr.next()
+f(ctx, state)
+s:drop()
+itr.next()
+f(ctx, state)
+f = nil
+ctx = nil
+state = nil
+itr = nil
+collectgarbage('collect')
+
+-- gh-2342 drop space if transaction is in progress
+ch = fiber.channel(1)
+s = box.schema.space.create('test', { engine = 'vinyl' })
+pk = s:create_index('primary', { parts = { 1, 'uint' } })
+sk = s:create_index('sec', { parts = { 2, 'uint' } })
+box.begin()
+s:replace({1, 2, 3})
+s:replace({4, 5, 6})
+s:replace({7, 8, 9})
+s:upsert({10, 11, 12}, {})
+_ = fiber.create(function () s:drop() ch:put(true) end)
+box.commit()
+ch:get()
+
+s = box.schema.space.create('test', { engine = 'vinyl' })
+pk = s:create_index('primary', { parts = { 1, 'uint' } })
+sk = s:create_index('sec', { parts = { 2, 'uint' } })
+box.begin()
+s:replace{1, 2, 3}
+s:replace{4, 5, 6}
+s:replace{7, 8, 9}
+_ = fiber.create(function () s:drop() ch:put(true) end)
+ch:get()
+box.commit()
+
diff --git a/third_party/gopt/gopt.c b/third_party/gopt/gopt.c
deleted file mode 100644
index fd916f811417ce335e7e2a55052f608b137eae1e..0000000000000000000000000000000000000000
--- a/third_party/gopt/gopt.c
+++ /dev/null
@@ -1,327 +0,0 @@
-/* gopt.c version 8.1: tom.viza@gmail.com PUBLIC DOMAIN 2003-8 */
-/*
-I, Tom Vajzovic, am the author of this software and its documentation and
-permanently abandon all copyright and other intellectual property rights in
-them, including the right to be identified as the author.
-
-I am fairly certain that this software does what the documentation says it
-does, but I cannot guarantee that it does, or that it does what you think it
-should, and I cannot guarantee that it will not have undesirable side effects.
-
-You are free to use, modify and distribute this software as you please, but
-you do so at your own risk.  If you remove or hide this warning then you are
-responsible for any problems encountered by people that you make the software
-available to.
-
-Before modifying or distributing this software I ask that you would please
-read http://www.purposeful.co.uk/tfl/
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "gopt.h"
-
-#ifdef USE_SYSEXITS
-#include <sysexits.h>
-#else
-#define EX_OSERR EXIT_FAILURE
-#define EX_USAGE EXIT_FAILURE
-#endif
-
-struct opt_spec_s {
-  int key;
-  int flags;
-  const char *shorts;
-  const char* const *longs;
-  const char *help_arg;
-  const char *help;
-};
-typedef struct opt_spec_s opt_spec_t;
-
-struct opt_s {
-  int key;
-  const char *arg;
-};
-typedef struct opt_s opt_t;
-
-void *gopt_sort( int *argc, const char **argv, const void *opt_specs ){
-  void *opts;
-  {{{
-    const char* const *arg_p= argv + 1;
-    size_t opt_count= 1;
-    for( ; *arg_p; ++arg_p )
-      if( '-' == (*arg_p)[0] && (*arg_p)[1] )
-      {
-        if( '-' == (*arg_p)[1] )
-          if( (*arg_p)[2] )
-            ++opt_count;
-          else
-            break;
-        else {
-          const opt_spec_t *opt_spec_p= opt_specs;
-          for( ; opt_spec_p-> key; ++opt_spec_p )
-            if( strchr( opt_spec_p-> shorts, (*arg_p)[1] )){
-              opt_count+= opt_spec_p-> flags & GOPT_ARG ? 1 : strlen( (*arg_p) + 1 );
-              break;
-            }
-        }
-      }
-    opts= malloc( opt_count * sizeof(opt_t) );
-  }}}
-  {
-    const char **arg_p= argv + 1;
-    const char **next_operand= arg_p;
-    opt_t *next_option= opts;
-
-    if( ! opts ){
-      perror( argv[0] );
-      exit( EX_OSERR );
-    }
-    for( ; *arg_p; ++arg_p )
-      if( '-' == (*arg_p)[0] && (*arg_p)[1] )
-        if( '-' == (*arg_p)[1] )
-          if( (*arg_p)[2] )
-          {{{
-            const opt_spec_t *opt_spec_p= opt_specs;
-            const char* const *longs= opt_spec_p-> longs;
-            next_option-> key= 0;
-            while( *longs ){
-              const char *option_cp= (*arg_p) + 2;
-              const char *name_cp= *longs;
-              while( *option_cp && *option_cp == *name_cp ){
-                ++option_cp;
-                ++name_cp;
-              }
-              if( '=' == *option_cp || !*option_cp ){
-                if( *name_cp ){
-                  if( next_option-> key ){
-                    fprintf( stderr, "%s: --%.*s: abbreviated option is ambiguous\n", argv[0], (int)( option_cp -( (*arg_p) + 2 )), (*arg_p) + 2 );
-                    free( opts );
-                    exit( EX_USAGE );
-                  }
-                  next_option-> key= opt_spec_p-> key;
-                }
-                else {
-                  next_option-> key= opt_spec_p-> key;
-                  goto found_long;
-                }
-              }
-              if( !*++longs ){
-                ++opt_spec_p;
-                if( opt_spec_p-> key )
-                  longs= opt_spec_p-> longs;
-              }
-            }
-            if( ! next_option-> key ){
-              fprintf( stderr, "%s: --%.*s: unknown option\n", argv[0], (int)strcspn( (*arg_p) + 2, "=" ), (*arg_p) + 2 );
-              free( opts );
-              exit( EX_USAGE );
-            }
-            for( opt_spec_p= opt_specs; opt_spec_p-> key != next_option-> key; ++opt_spec_p );
-            found_long:
-
-            if( !( opt_spec_p-> flags & GOPT_REPEAT )){
-              const opt_t *opt_p= opts;
-              for( ; opt_p != next_option; ++opt_p )
-                if( opt_p-> key == opt_spec_p-> key ){
-                  fprintf( stderr, "%s: --%.*s: option may not be repeated (in any long or short form)\n", argv[0], (int)strcspn( (*arg_p) + 2, "=" ), (*arg_p) + 2 );
-                  free( opts );
-                  exit( EX_USAGE );
-                }
-            }
-            if( opt_spec_p-> flags & GOPT_ARG ){
-              next_option-> arg= strchr( (*arg_p) + 2, '=' ) + 1;
-              if( (char*)0 + 1 == next_option-> arg ){
-                ++arg_p;
-                if( !*arg_p || ('-' == (*arg_p)[0] && (*arg_p)[1] )){
-                  fprintf( stderr, "%s: --%s: option requires an option argument\n", argv[0], (*(arg_p-1)) + 2 );
-                  free( opts );
-                  exit( EX_USAGE );
-                }
-                next_option-> arg= *arg_p;
-              }
-            }
-            else {
-              if( strchr( (*arg_p) + 2, '=' )){
-                fprintf( stderr, "%s: --%.*s: option may not take an option argument\n", argv[0], (int)strcspn( (*arg_p) + 2, "=" ), (*arg_p) + 2 );
-                free( opts );
-                exit( EX_USAGE );
-              }
-              next_option-> arg= NULL;
-            }
-            ++next_option;
-          }}}
-          else {
-            for( ++arg_p; *arg_p; ++arg_p )
-              *next_operand++= *arg_p;
-            break;
-          }
-        else
-        {{{
-          const char *short_opt= (*arg_p) + 1;
-          for( ;*short_opt; ++short_opt ){
-            const opt_spec_t *opt_spec_p= opt_specs;
-
-            for( ; opt_spec_p-> key; ++opt_spec_p )
-              if( strchr( opt_spec_p-> shorts, *short_opt )){
-                if( !( opt_spec_p-> flags & GOPT_REPEAT )){
-                  const opt_t *opt_p= opts;
-                  for( ; opt_p != next_option; ++opt_p )
-                    if( opt_p-> key == opt_spec_p-> key ){
-                      fprintf( stderr, "%s: -%c: option may not be repeated (in any long or short form)\n", argv[0], *short_opt );
-                      free( opts );
-                      exit( EX_USAGE );
-                    }
-                }
-                next_option-> key= opt_spec_p-> key;
-
-                if( opt_spec_p-> flags & GOPT_ARG ){
-                  if( short_opt[1] )
-                    next_option-> arg= short_opt + 1;
-
-                  else {
-                    ++arg_p;
-                    if( !*arg_p || ('-' == (*arg_p)[0] && (*arg_p)[1])){
-                      fprintf( stderr, "%s: -%c: option requires an option argument\n", argv[0], *short_opt );
-                      free( opts );
-                      exit( EX_USAGE );
-                    }
-                    next_option-> arg= *arg_p;
-                  }
-                  ++next_option;
-                  goto break_2;
-                }
-                next_option-> arg= NULL;
-                ++next_option;
-                goto continue_2;
-              }
-            fprintf( stderr, "%s: -%c: unknown option\n", argv[0], *short_opt );
-            free( opts );
-            exit( EX_USAGE );
-            continue_2: ;
-          }
-          break_2: ;
-        }}}
-      else
-        *next_operand++= *arg_p;
-
-    next_option-> key= 0;
-    *next_operand= NULL;
-    *argc= next_operand - argv;
-  }
-  return opts;
-}
-
-size_t gopt( const void *vptr_opts, int key ){
-  const opt_t *opts= vptr_opts;
-  size_t count= 0;
-  for( ; opts-> key; ++opts )
-    count+= opts-> key == key;
-
-  return count;
-}
-
-size_t gopt_arg( const void *vptr_opts, int key, const char **arg ){
-  const opt_t *opts= vptr_opts;
-  size_t count= 0;
-
-  for( ; opts-> key; ++opts )
-    if( opts-> key == key ){
-      if( ! count )
-        *arg= opts-> arg;
-      ++count;
-    }
-  return count;
-}
-
-const char *gopt_arg_i( const void *vptr_opts, int key, size_t i ){
-  const opt_t *opts= vptr_opts;
-
-  for( ; opts-> key; ++opts )
-    if( opts-> key == key ){
-      if( ! i )
-        return opts-> arg;
-      --i;
-    }
-  return NULL;
-}
-
-size_t gopt_args( const void *vptr_opts, int key, const char **args, size_t args_len ){
-  const char **args_stop= args + args_len;
-  const char **args_ptr= args;
-  const opt_t *opts= vptr_opts;
-
-  for( ; opts-> key; ++opts )
-    if( opts-> key == key ){
-      if( args_stop == args_ptr )
-        return args_len + gopt( opts, key );
-
-      *args_ptr++= opts-> arg;
-    }
-  if( args_stop != args_ptr )
-    *args_ptr= NULL;
-  return args_ptr - args;
-}
-
-void gopt_free( void *vptr_opts ){
-  free( vptr_opts );
-}
-
-void gopt_help(const void *opt_def){
-  const struct opt_spec_s *opt = opt_def;
-
-  /*
-   * layout:
-   * padding: 2 spaces ("  ")
-   * short option: 4 chars, padded with spaces ("    " or "-x  " or "-x, ")
-   * long option: 20 chars, padded with spaces ("--option                ")
-   * help: rest of line: 54
-   * help padding: 25 spaces
-   */
-
-  const int long_opt_width = 27; /* not counting leading "--" */
-  const size_t help_width = 54;
-  const char help_padding[] = "                                  ";
-
-    while (opt->key) {
-    const char *shorts = opt->shorts;
-    char has_shorts = 0;
-    printf("  ");
-    if (*shorts) {
-      has_shorts = 1;
-      printf("-%c", *shorts++);
-    } else
-      printf("  ");
-
-    const char *const *longs = opt->longs;
-    if (*longs) {
-      if (has_shorts)
-	printf(", ");
-      else
-	printf("  ");
-      if (opt->help_arg)
-	printf("--%s%-*s", *longs, long_opt_width - (int) strlen(*longs),
-               opt->help_arg);
-      else
-	printf("--%-*s", long_opt_width, *longs);
-    }
-    if (opt->help) {
-      const char *help = opt->help;
-      while (strlen(help) > help_width) {
-	const char *p = help + help_width;
-	while (p > help && *p != ' ')
-	  p--;
-	if (p == help)
-	  p = help + help_width;
-	printf("%.*s\n", (int) (p - help), help);
-	help = p;
-	if (strlen(help))
-	  printf("%s", help_padding);
-      }
-      puts(help);
-    } else
-      puts("");
-    opt++;
-  }
-}
diff --git a/third_party/gopt/gopt.h b/third_party/gopt/gopt.h
deleted file mode 100644
index 6c7e36af594011bfcf51fd1ee6c8e59a858f0c5e..0000000000000000000000000000000000000000
--- a/third_party/gopt/gopt.h
+++ /dev/null
@@ -1,79 +0,0 @@
-#ifndef GOPT_H_INCLUDED
-#define GOPT_H_INCLUDED
-/* gopt.h version 8.1: tom.viza@gmail.com PUBLIC DOMAIN 2003-8 */
-/*
-I, Tom Vajzovic, am the author of this software and its documentation and
-permanently abandon all copyright and other intellectual property rights in
-them, including the right to be identified as the author.
-
-I am fairly certain that this software does what the documentation says it
-does, but I cannot guarantee that it does, or that it does what you think it
-should, and I cannot guarantee that it will not have undesirable side effects.
-
-You are free to use, modify and distribute this software as you please, but
-you do so at your own risk.  If you remove or hide this warning then you are
-responsible for any problems encountered by people that you make the software
-available to.
-
-Before modifying or distributing this software I ask that you would please
-read http://www.purposeful.co.uk/tfl/
-*/
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define GOPT_ONCE   0
-#define GOPT_REPEAT 1
-#define GOPT_NOARG  0
-#define GOPT_ARG    2
-
-struct goptargs { int k; int f; const char *s; const char*const*l; const char *a; const char *h; };
-#define gopt_start(...)  (const void*)(const struct goptargs[]){ __VA_ARGS__, {0, 0, NULL, NULL, NULL, NULL}}
-
-#define gopt_option(k,f,s,l,a,h)    { k, f, s, l, a, h }
-#define gopt_shorts( ... )      (const char*)(const char[]){ __VA_ARGS__, 0 }
-#define gopt_longs( ... )       (const char**)(const char*[]){ __VA_ARGS__, NULL }
-
-
-/** Returns a pointer for use in the following calls. Prints to
- * stderr and call exit() on error.
- */
-void *gopt_sort( int *argc, const char **argv, const void *opt_specs );
-
-/** Returns the number of times the option was specified which
- * will be 0 or 1 unless GOPT_REPEAT was used.
- */
-size_t gopt( const void *opts, int key );
-
-/** Returns the number of times the option was specified writes
- * a pointer to the option argument from the first (or only)
- * occurrence to *arg.
- */
-size_t gopt_arg( const void *opts, int key, const char **arg );
-
-/** Returns a pointer to the ith (starting at zero) occurrence of
- * the option, or NULL if it was not specified that many times.
- */
-const char *gopt_arg_i( const void *opts, int key, size_t i );
-
-/** Returns the number of times the option was specified writes
- * pointers to the option arguments in the order of occurrence to
- * args[]. Writes at most args_len pointers if the return value is
- * less than args_len, also writes a null pointer.
- */
-size_t gopt_args( const void *opts, int key, const char **args, size_t args_len );
-
-/** Releases memory allocated in the corresponding call to
- * gopt_sort(); opts can no longer be used.
- */
-void gopt_free( void *opts );
-
-/** Prints descriptions for all options. */
-void gopt_help(const void *opt_def);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* GOPT_H_INCLUDED */
diff --git a/third_party/libev/Changes b/third_party/libev/Changes
index 4f23954ff58ca3b62f2287909a6caa8f7207e0e9..bb1e6d43dcc70707031f66b41fdb9df14448fc4a 100644
--- a/third_party/libev/Changes
+++ b/third_party/libev/Changes
@@ -1,18 +1,24 @@
 Revision history for libev, a high-performance and full-featured event loop.
 
-TODO: ev_loop_wakeup
-TODO: EV_STANDALONE == NO_HASSEL (do not use clock_gettime in ev_standalone)
-TODO: faq, process a thing in each iteration
-TODO: dbeugging tips, ev_verify, ev_init twice
-TODO: ev_break for immediate exit (EVBREAK_NOW?)
-TODO: ev_feed_child_event
-TODO: document the special problem of signals around fork.
-TODO: store pid for each signal
-TODO: document file descriptor usage per loop
-TODO: store loop pid_t and compare isndie signal handler,store 1 for same, 2 for differign pid, clean up in loop_fork
-TODO: embed watchers need updating when fd changes
-TODO: document portability requirements for atomic pointer access
-TODO: document requirements for function pointers and calling conventions.
+4.24 Wed Dec 28 05:19:55 CET 2016
+	- bump version to 4.24, as the release tarball inexplicably
+          didn't have the right version in ev.h, even though the cvs-tagged
+          version did have the right one (reported by Ales Teska).
+
+4.23 Wed Nov 16 18:23:41 CET 2016
+	- move some declarations at the beginning to help certain retarded
+          microsoft compilers, even though their documentation claims
+          otherwise (reported by Ruslan Osmanov).
+
+4.22 Sun Dec 20 22:11:50 CET 2015
+	- when epoll detects unremovable fds in the fd set, rebuild
+          only the epoll descriptor, not the signal pipe, to avoid
+          SIGPIPE in ev_async_send. This doesn't solve it on fork,
+          so document what needs to be done in ev_loop_fork
+          (analyzed by Benjamin Mahler).
+	- remove superfluous sys/timeb.h include on win32
+          (analyzed by Jason Madden).
+        - updated libecb.
 
 4.20 Sat Jun 20 13:01:43 CEST 2015
 	- prefer noexcept over throw () with C++ 11.
diff --git a/third_party/libev/configure.ac b/third_party/libev/configure.ac
index 7ff79bf0aebf800b4f65f3cf0655871ff2520e86..2590f8fd67ecc343065938db41e6ff0b0972e3f0 100644
--- a/third_party/libev/configure.ac
+++ b/third_party/libev/configure.ac
@@ -5,7 +5,7 @@ orig_CFLAGS="$CFLAGS"
 AC_CONFIG_SRCDIR([ev_epoll.c])
 
 dnl also update ev.h!
-AM_INIT_AUTOMAKE(libev,4.20)
+AM_INIT_AUTOMAKE(libev,4.24)
 AC_CONFIG_HEADERS([config.h])
 AM_MAINTAINER_MODE
 
diff --git a/third_party/libev/ev.3 b/third_party/libev/ev.3
index 003b7c1004b9a66feb7bbd994bf81cf530376a97..5b2599e9bdf0fa94cdaf7f1938ea9793ad49b8be 100644
--- a/third_party/libev/ev.3
+++ b/third_party/libev/ev.3
@@ -1,4 +1,4 @@
-.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.28)
+.\" Automatically generated by Pod::Man 2.28 (Pod::Simple 3.30)
 .\"
 .\" Standard preamble:
 .\" ========================================================================
@@ -133,7 +133,7 @@
 .\" ========================================================================
 .\"
 .IX Title "LIBEV 3"
-.TH LIBEV 3 "2015-05-01" "libev-4.19" "libev - high performance full featured event loop"
+.TH LIBEV 3 "2016-11-16" "libev-4.23" "libev - high performance full featured event loop"
 .\" For nroff, turn off justification.  Always turn off hyphenation; it makes
 .\" way too many mistakes in technical documents.
 .if n .ad l
@@ -543,8 +543,8 @@ without a system call and thus \fIvery\fR fast, but my GNU/Linux system also has
 \&\f(CW\*(C`pthread_atfork\*(C'\fR which is even faster).
 .Sp
 The big advantage of this flag is that you can forget about fork (and
-forget about forgetting to tell libev about forking) when you use this
-flag.
+forget about forgetting to tell libev about forking, although you still
+have to ignore \f(CW\*(C`SIGPIPE\*(C'\fR) when you use this flag.
 .Sp
 This flag setting cannot be overridden or specified in the \f(CW\*(C`LIBEV_FLAGS\*(C'\fR
 environment variable.
@@ -819,6 +819,9 @@ watchers (except inside an \f(CW\*(C`ev_prepare\*(C'\fR callback), but it makes
 sense after forking, in the child process. You \fImust\fR call it (or use
 \&\f(CW\*(C`EVFLAG_FORKCHECK\*(C'\fR) in the child before resuming or calling \f(CW\*(C`ev_run\*(C'\fR.
 .Sp
+In addition, if you want to reuse a loop (via this function or
+\&\f(CW\*(C`EVFLAG_FORKCHECK\*(C'\fR), you \fIalso\fR have to ignore \f(CW\*(C`SIGPIPE\*(C'\fR.
+.Sp
 Again, you \fIhave\fR to call it on \fIany\fR loop that you want to re-use after
 a fork, \fIeven if you do not plan to use the loop in the parent\fR. This is
 because some kernel interfaces *cough* \fIkqueue\fR *cough* do funny things
@@ -2345,7 +2348,7 @@ Periodic watchers are also timers of a kind, but they are very versatile
 .PP
 Unlike \f(CW\*(C`ev_timer\*(C'\fR, periodic watchers are not based on real time (or
 relative time, the physical time that passes) but on wall clock time
-(absolute time, the thing you can read on your calender or clock). The
+(absolute time, the thing you can read on your calendar or clock). The
 difference is that wall clock time can run faster or slower than real
 time, and time jumps are not uncommon (e.g. when you adjust your
 wrist-watch).
@@ -5411,6 +5414,10 @@ structure (guaranteed by \s-1POSIX\s0 but not by \s-1ISO C\s0 for example), but
 assumes that the same (machine) code can be used to call any watcher
 callback: The watcher callbacks have different type signatures, but libev
 calls them using an \f(CW\*(C`ev_watcher *\*(C'\fR internally.
+.IP "null pointers and integer zero are represented by 0 bytes" 4
+.IX Item "null pointers and integer zero are represented by 0 bytes"
+Libev uses \f(CW\*(C`memset\*(C'\fR to initialise structs and arrays to \f(CW0\fR bytes, and
+relies on this setting pointers and integers to null.
 .IP "pointer accesses must be thread-atomic" 4
 .IX Item "pointer accesses must be thread-atomic"
 Accessing a pointer value must be atomic, it must both be readable and
diff --git a/third_party/libev/ev.c b/third_party/libev/ev.c
index 409a854868b9b7dc63a33ca0ba83b1a75b0c7604..6a2648591961476e08b20c412c1fdef91feeb250 100644
--- a/third_party/libev/ev.c
+++ b/third_party/libev/ev.c
@@ -534,7 +534,7 @@ struct signalfd_siginfo
 #define ECB_H
 
 /* 16 bits major, 16 bits minor */
-#define ECB_VERSION 0x00010004
+#define ECB_VERSION 0x00010005
 
 #ifdef _WIN32
   typedef   signed char   int8_t;
@@ -561,7 +561,7 @@ struct signalfd_siginfo
   #endif
 #else
   #include <inttypes.h>
-  #if UINTMAX_MAX > 0xffffffffU
+  #if (defined INTPTR_MAX ? INTPTR_MAX : ULONG_MAX) > 0xffffffffU
     #define ECB_PTRSIZE 8
   #else
     #define ECB_PTRSIZE 4
@@ -649,6 +649,10 @@ struct signalfd_siginfo
   #include <builtins.h>
 #endif
 
+#if 1400 <= _MSC_VER
+  #include <intrin.h> /* fence functions _ReadBarrier, also bit search functions _BitScanReverse */
+#endif
+
 #ifndef ECB_MEMORY_FENCE
   #if ECB_GCC_VERSION(2,5) || defined __INTEL_COMPILER || (__llvm__ && __GNUC__) || __SUNPRO_C >= 0x5110 || __SUNPRO_CC >= 0x5110
     #if __i386 || __i386__
@@ -661,11 +665,19 @@ struct signalfd_siginfo
       #define ECB_MEMORY_FENCE_RELEASE __asm__ __volatile__ ("")
     #elif __powerpc__ || __ppc__ || __powerpc64__ || __ppc64__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("sync"     : : : "memory")
+    #elif defined __ARM_ARCH_2__ \
+      || defined __ARM_ARCH_3__  || defined __ARM_ARCH_3M__  \
+      || defined __ARM_ARCH_4__  || defined __ARM_ARCH_4T__  \
+      || defined __ARM_ARCH_5__  || defined __ARM_ARCH_5E__  \
+      || defined __ARM_ARCH_5T__ || defined __ARM_ARCH_5TE__ \
+      || defined __ARM_ARCH_5TEJ__
+      /* should not need any, unless running old code on newer cpu - arm doesn't support that */
     #elif defined __ARM_ARCH_6__  || defined __ARM_ARCH_6J__  \
-       || defined __ARM_ARCH_6K__ || defined __ARM_ARCH_6ZK__
+       || defined __ARM_ARCH_6K__ || defined __ARM_ARCH_6ZK__ \
+       || defined __ARM_ARCH_6T2__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("mcr p15,0,%0,c7,c10,5" : : "r" (0) : "memory")
     #elif defined __ARM_ARCH_7__  || defined __ARM_ARCH_7A__  \
-       || defined __ARM_ARCH_7M__ || defined __ARM_ARCH_7R__
+       || defined __ARM_ARCH_7R__ || defined __ARM_ARCH_7M__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("dmb"      : : : "memory")
     #elif __aarch64__
       #define ECB_MEMORY_FENCE         __asm__ __volatile__ ("dmb ish"  : : : "memory")
@@ -917,6 +929,11 @@ typedef int ecb_bool;
   ecb_function_ ecb_const int
   ecb_ctz32 (uint32_t x)
   {
+#if 1400 <= _MSC_VER && (_M_IX86 || _M_X64 || _M_IA64 || _M_ARM)
+    unsigned long r;
+    _BitScanForward (&r, x);
+    return (int)r;
+#else
     int r = 0;
 
     x &= ~x + 1; /* this isolates the lowest bit */
@@ -936,14 +953,21 @@ typedef int ecb_bool;
 #endif
 
     return r;
+#endif
   }
 
   ecb_function_ ecb_const int ecb_ctz64 (uint64_t x);
   ecb_function_ ecb_const int
   ecb_ctz64 (uint64_t x)
   {
-    int shift = x & 0xffffffffU ? 0 : 32;
+#if 1400 <= _MSC_VER && (_M_X64 || _M_IA64 || _M_ARM)
+    unsigned long r;
+    _BitScanForward64 (&r, x);
+    return (int)r;
+#else
+    int shift = x & 0xffffffff ? 0 : 32;
     return ecb_ctz32 (x >> shift) + shift;
+#endif
   }
 
   ecb_function_ ecb_const int ecb_popcount32 (uint32_t x);
@@ -961,6 +985,11 @@ typedef int ecb_bool;
   ecb_function_ ecb_const int ecb_ld32 (uint32_t x);
   ecb_function_ ecb_const int ecb_ld32 (uint32_t x)
   {
+#if 1400 <= _MSC_VER && (_M_IX86 || _M_X64 || _M_IA64 || _M_ARM)
+    unsigned long r;
+    _BitScanReverse (&r, x);
+    return (int)r;
+#else
     int r = 0;
 
     if (x >> 16) { x >>= 16; r += 16; }
@@ -970,16 +999,23 @@ typedef int ecb_bool;
     if (x >>  1) {           r +=  1; }
 
     return r;
+#endif
   }
 
   ecb_function_ ecb_const int ecb_ld64 (uint64_t x);
   ecb_function_ ecb_const int ecb_ld64 (uint64_t x)
   {
+#if 1400 <= _MSC_VER && (_M_X64 || _M_IA64 || _M_ARM)
+    unsigned long r;
+    _BitScanReverse64 (&r, x);
+    return (int)r;
+#else
     int r = 0;
 
     if (x >> 32) { x >>= 32; r += 32; }
 
     return r + ecb_ld32 (x);
+#endif
   }
 #endif
 
@@ -1092,8 +1128,8 @@ ecb_inline ecb_const uint64_t ecb_rotr64 (uint64_t x, unsigned int count) { retu
 /* try to tell the compiler that some condition is definitely true */
 #define ecb_assume(cond) if (!(cond)) ecb_unreachable (); else 0
 
-ecb_inline ecb_const unsigned char ecb_byteorder_helper (void);
-ecb_inline ecb_const unsigned char
+ecb_inline ecb_const uint32_t ecb_byteorder_helper (void);
+ecb_inline ecb_const uint32_t
 ecb_byteorder_helper (void)
 {
   /* the union code still generates code under pressure in gcc, */
@@ -1102,26 +1138,28 @@ ecb_byteorder_helper (void)
   /* the reason why we have this horrible preprocessor mess */
   /* is to avoid it in all cases, at least on common architectures */
   /* or when using a recent enough gcc version (>= 4.6) */
-#if ((__i386 || __i386__) && !__VOS__) || _M_IX86 || ECB_GCC_AMD64 || ECB_MSVC_AMD64
-  return 0x44;
-#elif __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-  return 0x44;
-#elif __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
-  return 0x11;
+#if (defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) \
+    || ((__i386 || __i386__ || _M_IX86 || ECB_GCC_AMD64 || ECB_MSVC_AMD64) && !__VOS__)
+  #define ECB_LITTLE_ENDIAN 1
+  return 0x44332211;
+#elif (defined __BYTE_ORDER__ && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__) \
+      || ((__AARCH64EB__ || __MIPSEB__ || __ARMEB__) && !__VOS__)
+  #define ECB_BIG_ENDIAN 1
+  return 0x11223344;
 #else
   union
   {
-    uint32_t i;
-    uint8_t c;
-  } u = { 0x11223344 };
-  return u.c;
+    uint8_t c[4];
+    uint32_t u;
+  } u = { 0x11, 0x22, 0x33, 0x44 };
+  return u.u;
 #endif
 }
 
 ecb_inline ecb_const ecb_bool ecb_big_endian    (void);
-ecb_inline ecb_const ecb_bool ecb_big_endian    (void) { return ecb_byteorder_helper () == 0x11; }
+ecb_inline ecb_const ecb_bool ecb_big_endian    (void) { return ecb_byteorder_helper () == 0x11223344; }
 ecb_inline ecb_const ecb_bool ecb_little_endian (void);
-ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44; }
+ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_helper () == 0x44332211; }
 
 #if ECB_GCC_VERSION(3,0) || ECB_C99
   #define ecb_mod(m,n) ((m) % (n) + ((m) % (n) < 0 ? (n) : 0))
@@ -1156,6 +1194,102 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
   #define ecb_array_length(name) (sizeof (name) / sizeof (name [0]))
 #endif
 
+ecb_function_ ecb_const uint32_t ecb_binary16_to_binary32 (uint32_t x);
+ecb_function_ ecb_const uint32_t
+ecb_binary16_to_binary32 (uint32_t x)
+{
+  unsigned int s = (x & 0x8000) << (31 - 15);
+  int          e = (x >> 10) & 0x001f;
+  unsigned int m =  x        & 0x03ff;
+
+  if (ecb_expect_false (e == 31))
+    /* infinity or NaN */
+    e = 255 - (127 - 15);
+  else if (ecb_expect_false (!e))
+    {
+      if (ecb_expect_true (!m))
+        /* zero, handled by code below by forcing e to 0 */
+        e = 0 - (127 - 15);
+      else
+        {
+          /* subnormal, renormalise */
+          unsigned int s = 10 - ecb_ld32 (m);
+
+          m = (m << s) & 0x3ff; /* mask implicit bit */
+          e -= s - 1;
+        }
+    }
+
+  /* e and m now are normalised, or zero, (or inf or nan) */
+  e += 127 - 15;
+
+  return s | (e << 23) | (m << (23 - 10));
+}
+
+ecb_function_ ecb_const uint16_t ecb_binary32_to_binary16 (uint32_t x);
+ecb_function_ ecb_const uint16_t
+ecb_binary32_to_binary16 (uint32_t x)
+{
+  unsigned int s =  (x >> 16) & 0x00008000; /* sign bit, the easy part */
+  unsigned int e = ((x >> 23) & 0x000000ff) - (127 - 15); /* the desired exponent */
+  unsigned int m =   x        & 0x007fffff;
+
+  x &= 0x7fffffff;
+
+  /* if it's within range of binary16 normals, use fast path */
+  if (ecb_expect_true (0x38800000 <= x && x <= 0x477fefff))
+    {
+      /* mantissa round-to-even */
+      m += 0x00000fff + ((m >> (23 - 10)) & 1);
+
+      /* handle overflow */
+      if (ecb_expect_false (m >= 0x00800000))
+        {
+          m >>= 1;
+          e +=  1;
+        }
+
+      return s | (e << 10) | (m >> (23 - 10));
+    }
+
+  /* handle large numbers and infinity */
+  if (ecb_expect_true (0x477fefff < x && x <= 0x7f800000))
+    return s | 0x7c00;
+
+  /* handle zero, subnormals and small numbers */
+  if (ecb_expect_true (x < 0x38800000))
+    {
+      /* zero */
+      if (ecb_expect_true (!x))
+        return s;
+
+      /* handle subnormals */
+
+      /* too small, will be zero */
+      if (e < (14 - 24)) /* might not be sharp, but is good enough */
+        return s;
+
+      m |= 0x00800000; /* make implicit bit explicit */
+
+      /* very tricky - we need to round to the nearest e (+10) bit value */
+      {
+        unsigned int bits = 14 - e;
+        unsigned int half = (1 << (bits - 1)) - 1;
+        unsigned int even = (m >> bits) & 1;
+
+        /* if this overflows, we will end up with a normalised number */
+        m = (m + half + even) >> bits;
+      }
+
+      return s | m;
+    }
+
+  /* handle NaNs, preserve leftmost nan bits, but make sure we don't turn them into infinities */
+  m >>= 13;
+
+  return s | 0x7c00 | m | !m;
+}
+
 /*******************************************************************************/
 /* floating point stuff, can be disabled by defining ECB_NO_LIBM */
 
@@ -1207,23 +1341,6 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
     #define ecb_frexpf(x,e) (float) frexp ((double) (x), (e))
   #endif
 
-  /* converts an ieee half/binary16 to a float */
-  ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x);
-  ecb_function_ ecb_const float
-  ecb_binary16_to_float (uint16_t x)
-  {
-    int e = (x >> 10) & 0x1f;
-    int m = x & 0x3ff;
-    float r;
-
-    if      (!e     ) r = ecb_ldexpf (m        ,    -24);
-    else if (e != 31) r = ecb_ldexpf (m + 0x400, e - 25);
-    else if (m      ) r = ECB_NAN;
-    else              r = ECB_INFINITY;
-
-    return x & 0x8000 ? -r : r;
-  }
-
   /* convert a float to ieee single/binary32 */
   ecb_function_ ecb_const uint32_t ecb_float_to_binary32 (float x);
   ecb_function_ ecb_const uint32_t
@@ -1364,6 +1481,22 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
     return r;
   }
 
+  /* convert a float to ieee half/binary16 */
+  ecb_function_ ecb_const uint16_t ecb_float_to_binary16 (float x);
+  ecb_function_ ecb_const uint16_t
+  ecb_float_to_binary16 (float x)
+  {
+    return ecb_binary32_to_binary16 (ecb_float_to_binary32 (x));
+  }
+
+  /* convert an ieee half/binary16 to float */
+  ecb_function_ ecb_const float ecb_binary16_to_float (uint16_t x);
+  ecb_function_ ecb_const float
+  ecb_binary16_to_float (uint16_t x)
+  {
+    return ecb_binary32_to_float (ecb_binary16_to_binary32 (x));
+  }
+
 #endif
 
 #endif
@@ -1396,7 +1529,7 @@ ecb_inline ecb_const ecb_bool ecb_little_endian (void) { return ecb_byteorder_he
 #if EV_FEATURE_CODE
 # define inline_speed      ecb_inline
 #else
-# define inline_speed      static noinline
+# define inline_speed      noinline static
 #endif
 
 #define NUMPRI (EV_MAXPRI - EV_MINPRI + 1)
@@ -1453,7 +1586,8 @@ static EV_ATOMIC_T have_monotonic; /* did clock_gettime (CLOCK_MONOTONIC) work?
 #include <float.h>
 
 /* a floor() replacement function, should be independent of ev_tstamp type */
-static ev_tstamp noinline
+noinline
+static ev_tstamp
 ev_floor (ev_tstamp v)
 {
   /* the choice of shift factor is not terribly important */
@@ -1495,7 +1629,8 @@ ev_floor (ev_tstamp v)
 # include <sys/utsname.h>
 #endif
 
-static unsigned int noinline ecb_cold
+noinline ecb_cold
+static unsigned int
 ev_linux_version (void)
 {
 #ifdef __linux
@@ -1534,7 +1669,8 @@ ev_linux_version (void)
 /*****************************************************************************/
 
 #if EV_AVOID_STDIO
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 ev_printerr (const char *msg)
 {
   write (STDERR_FILENO, msg, strlen (msg));
@@ -1543,13 +1679,15 @@ ev_printerr (const char *msg)
 
 static void (*syserr_cb)(const char *msg) EV_THROW;
 
-void ecb_cold
+ecb_cold
+void
 ev_set_syserr_cb (void (*cb)(const char *msg) EV_THROW) EV_THROW
 {
   syserr_cb = cb;
 }
 
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 ev_syserr (const char *msg)
 {
   if (!msg)
@@ -1590,7 +1728,8 @@ ev_realloc_emul (void *ptr, long size) EV_THROW
 
 static void *(*alloc)(void *ptr, long size) EV_THROW = ev_realloc_emul;
 
-void ecb_cold
+ecb_cold
+void
 ev_set_allocator (void *(*cb)(void *ptr, long size) EV_THROW) EV_THROW
 {
   alloc = cb;
@@ -1763,6 +1902,18 @@ ev_now (EV_P) EV_THROW
 }
 #endif
 
+ev_tstamp
+ev_monotonic_now (EV_P) EV_THROW
+{
+  return mn_now;
+}
+
+ev_tstamp
+ev_monotonic_time (void) EV_THROW
+{
+  return get_clock();
+}
+
 void
 ev_sleep (ev_tstamp delay) EV_THROW
 {
@@ -1814,7 +1965,8 @@ array_nextsize (int elem, int cur, int cnt)
   return ncur;
 }
 
-static void * noinline ecb_cold
+noinline ecb_cold
+static void *
 array_realloc (int elem, void *base, int *cur, int cnt)
 {
   *cur = array_nextsize (elem, *cur, cnt);
@@ -1827,7 +1979,7 @@ array_realloc (int elem, void *base, int *cur, int cnt)
 #define array_needsize(type,base,cur,cnt,init)			\
   if (expect_false ((cnt) > (cur)))				\
     {								\
-      int ecb_unused ocur_ = (cur);					\
+      ecb_unused int ocur_ = (cur);				\
       (base) = (type *)array_realloc				\
          (sizeof (type), (base), &(cur), (cnt));		\
       init ((base) + (ocur_), (cur) - ocur_);			\
@@ -1849,12 +2001,14 @@ array_realloc (int elem, void *base, int *cur, int cnt)
 /*****************************************************************************/
 
 /* dummy callback for pending events */
-static void noinline
+noinline
+static void
 pendingcb (EV_P_ ev_prepare *w, int revents)
 {
 }
 
-void noinline
+noinline
+void
 ev_feed_event (EV_P_ void *w, int revents) EV_THROW
 {
   W w_ = (W)w;
@@ -1994,7 +2148,8 @@ fd_reify (EV_P)
 }
 
 /* something about the given fd changed */
-inline_size void
+inline_size
+void
 fd_change (EV_P_ int fd, int flags)
 {
   unsigned char reify = anfds [fd].reify;
@@ -2009,7 +2164,7 @@ fd_change (EV_P_ int fd, int flags)
 }
 
 /* the given fd is invalid/unusable, so make sure it doesn't hurt us anymore */
-inline_speed void ecb_cold
+inline_speed ecb_cold void
 fd_kill (EV_P_ int fd)
 {
   ev_io *w;
@@ -2022,7 +2177,7 @@ fd_kill (EV_P_ int fd)
 }
 
 /* check whether the given fd is actually valid, for error recovery */
-inline_size int ecb_cold
+inline_size ecb_cold int
 fd_valid (int fd)
 {
 #ifdef _WIN32
@@ -2033,7 +2188,8 @@ fd_valid (int fd)
 }
 
 /* called on EBADF to verify fds */
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 fd_ebadf (EV_P)
 {
   int fd;
@@ -2045,7 +2201,8 @@ fd_ebadf (EV_P)
 }
 
 /* called on ENOMEM in select/poll to kill some fds and retry */
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 fd_enomem (EV_P)
 {
   int fd;
@@ -2059,7 +2216,8 @@ fd_enomem (EV_P)
 }
 
 /* usually called after fork if backend needs to re-arm all fds from scratch */
-static void noinline
+noinline
+static void
 fd_rearm_all (EV_P)
 {
   int fd;
@@ -2288,7 +2446,8 @@ static int evpipe_alloc(EV_P)
 }
 
 
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 evpipe_init (EV_P)
 {
   if (!ev_is_active (&pipe_w))
@@ -2445,7 +2604,8 @@ ev_sighandler (int signum)
   ev_feed_signal (signum);
 }
 
-void noinline
+noinline
+void
 ev_feed_signal_event (EV_P_ int signum) EV_THROW
 {
   WL w;
@@ -2572,20 +2732,20 @@ childcb (EV_P_ ev_signal *sw, int revents)
 # include "ev_select.c"
 #endif
 
-int ecb_cold
+ecb_cold int
 ev_version_major (void) EV_THROW
 {
   return EV_VERSION_MAJOR;
 }
 
-int ecb_cold
+ecb_cold int
 ev_version_minor (void) EV_THROW
 {
   return EV_VERSION_MINOR;
 }
 
 /* return true if we are running with elevated privileges and should ignore env variables */
-int inline_size ecb_cold
+inline_size ecb_cold int
 enable_secure (void)
 {
 #ifdef _WIN32
@@ -2596,7 +2756,8 @@ enable_secure (void)
 #endif
 }
 
-unsigned int ecb_cold
+ecb_cold
+unsigned int
 ev_supported_backends (void) EV_THROW
 {
   unsigned int flags = 0;
@@ -2610,7 +2771,8 @@ ev_supported_backends (void) EV_THROW
   return flags;
 }
 
-unsigned int ecb_cold
+ecb_cold
+unsigned int
 ev_recommended_backends (void) EV_THROW
 {
   unsigned int flags = ev_supported_backends ();
@@ -2632,7 +2794,8 @@ ev_recommended_backends (void) EV_THROW
   return flags;
 }
 
-unsigned int ecb_cold
+ecb_cold
+unsigned int
 ev_embeddable_backends (void) EV_THROW
 {
   int flags = EVBACKEND_EPOLL | EVBACKEND_KQUEUE | EVBACKEND_PORT;
@@ -2702,7 +2865,8 @@ ev_set_loop_release_cb (EV_P_ void (*release)(EV_P) EV_THROW, void (*acquire)(EV
 #endif
 
 /* initialise a loop structure, must be zero-initialised */
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 loop_init (EV_P_ unsigned int flags) EV_THROW
 {
   if (!backend)
@@ -2808,7 +2972,8 @@ loop_init (EV_P_ unsigned int flags) EV_THROW
 }
 
 /* free up a loop structure */
-void ecb_cold
+ecb_cold
+void
 ev_loop_destroy (EV_P)
 {
   int i;
@@ -2939,7 +3104,7 @@ loop_fork (EV_P)
 #endif
 
 #if EV_SIGNAL_ENABLE || EV_ASYNC_ENABLE
-  if (ev_is_active (&pipe_w))
+  if (ev_is_active (&pipe_w) && postfork != 2)
     {
       /* pipe_write_wanted must be false now, so modifying fd vars should be safe */
 
@@ -2960,7 +3125,8 @@ loop_fork (EV_P)
 
 #if EV_MULTIPLICITY
 
-struct ev_loop * ecb_cold
+ecb_cold
+struct ev_loop *
 ev_loop_new (unsigned int flags) EV_THROW
 {
   EV_P = (struct ev_loop *)ev_malloc (sizeof (struct ev_loop));
@@ -2978,7 +3144,8 @@ ev_loop_new (unsigned int flags) EV_THROW
 #endif /* multiplicity */
 
 #if EV_VERIFY
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 verify_watcher (EV_P_ W w)
 {
   assert (("libev: watcher has invalid priority", ABSPRI (w) >= 0 && ABSPRI (w) < NUMPRI));
@@ -2987,7 +3154,8 @@ verify_watcher (EV_P_ W w)
     assert (("libev: pending watcher not on pending queue", pendings [ABSPRI (w)][w->pending - 1].w == w));
 }
 
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 verify_heap (EV_P_ ANHE *heap, int N)
 {
   int i;
@@ -3002,7 +3170,8 @@ verify_heap (EV_P_ ANHE *heap, int N)
     }
 }
 
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 array_verify (EV_P_ W *ws, int cnt)
 {
   while (cnt--)
@@ -3101,7 +3270,8 @@ ev_verify (EV_P) EV_THROW
 #endif
 
 #if EV_MULTIPLICITY
-struct ev_loop * ecb_cold
+ecb_cold
+struct ev_loop *
 #else
 int
 #endif
@@ -3159,7 +3329,8 @@ ev_pending_count (EV_P) EV_THROW
   return count;
 }
 
-void noinline
+noinline
+void
 ev_invoke_pending (EV_P)
 {
   pendingpri = NUMPRI;
@@ -3244,7 +3415,8 @@ timers_reify (EV_P)
 
 #if EV_PERIODIC_ENABLE
 
-static void noinline
+noinline
+static void
 periodic_recalc (EV_P_ ev_periodic *w)
 {
   ev_tstamp interval = w->interval > MIN_INTERVAL ? w->interval : MIN_INTERVAL;
@@ -3312,7 +3484,8 @@ periodics_reify (EV_P)
 
 /* simply recalculate all periodics */
 /* TODO: maybe ensure that at least one event happens when jumping forward? */
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 periodics_reschedule (EV_P)
 {
   int i;
@@ -3335,7 +3508,8 @@ periodics_reschedule (EV_P)
 #endif
 
 /* adjust all timers by a given offset */
-static void noinline ecb_cold
+noinline ecb_cold
+static void
 timers_reschedule (EV_P_ ev_tstamp adjust)
 {
   int i;
@@ -3719,7 +3893,8 @@ ev_stop (EV_P_ W w)
 
 /*****************************************************************************/
 
-void noinline
+noinline
+void
 ev_io_start (EV_P_ ev_io *w) EV_THROW
 {
   int fd = w->fd;
@@ -3745,7 +3920,8 @@ ev_io_start (EV_P_ ev_io *w) EV_THROW
   EV_FREQUENT_CHECK;
 }
 
-void noinline
+noinline
+void
 ev_io_stop (EV_P_ ev_io *w) EV_THROW
 {
   clear_pending (EV_A_ (W)w);
@@ -3784,7 +3960,8 @@ ev_io_closing (EV_P_ int fd, int revents) EV_THROW
     }
 }
 
-void noinline
+noinline
+void
 ev_timer_start (EV_P_ ev_timer *w) EV_THROW
 {
   if (expect_false (ev_is_active (w)))
@@ -3808,7 +3985,8 @@ ev_timer_start (EV_P_ ev_timer *w) EV_THROW
   /*assert (("libev: internal timer heap corruption", timers [ev_active (w)] == (WT)w));*/
 }
 
-void noinline
+noinline
+void
 ev_timer_stop (EV_P_ ev_timer *w) EV_THROW
 {
   clear_pending (EV_A_ (W)w);
@@ -3838,7 +4016,8 @@ ev_timer_stop (EV_P_ ev_timer *w) EV_THROW
   EV_FREQUENT_CHECK;
 }
 
-void noinline
+noinline
+void
 ev_timer_again (EV_P_ ev_timer *w) EV_THROW
 {
   EV_FREQUENT_CHECK;
@@ -3872,7 +4051,8 @@ ev_timer_remaining (EV_P_ ev_timer *w) EV_THROW
 }
 
 #if EV_PERIODIC_ENABLE
-void noinline
+noinline
+void
 ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW
 {
   if (expect_false (ev_is_active (w)))
@@ -3902,7 +4082,8 @@ ev_periodic_start (EV_P_ ev_periodic *w) EV_THROW
   /*assert (("libev: internal periodic heap corruption", ANHE_w (periodics [ev_active (w)]) == (WT)w));*/
 }
 
-void noinline
+noinline
+void
 ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW
 {
   clear_pending (EV_A_ (W)w);
@@ -3930,7 +4111,8 @@ ev_periodic_stop (EV_P_ ev_periodic *w) EV_THROW
   EV_FREQUENT_CHECK;
 }
 
-void noinline
+noinline
+void
 ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW
 {
   /* TODO: use adjustheap and recalculation */
@@ -3945,7 +4127,8 @@ ev_periodic_again (EV_P_ ev_periodic *w) EV_THROW
 
 #if EV_SIGNAL_ENABLE
 
-void noinline
+noinline
+void
 ev_signal_start (EV_P_ ev_signal *w) EV_THROW
 {
   if (expect_false (ev_is_active (w)))
@@ -4027,7 +4210,8 @@ ev_signal_start (EV_P_ ev_signal *w) EV_THROW
   EV_FREQUENT_CHECK;
 }
 
-void noinline
+noinline
+void
 ev_signal_stop (EV_P_ ev_signal *w) EV_THROW
 {
   clear_pending (EV_A_ (W)w);
@@ -4113,14 +4297,15 @@ ev_child_stop (EV_P_ ev_child *w) EV_THROW
 #define NFS_STAT_INTERVAL 30.1074891 /* for filesystems potentially failing inotify */
 #define MIN_STAT_INTERVAL  0.1074891
 
-static void noinline stat_timer_cb (EV_P_ ev_timer *w_, int revents);
+noinline static void stat_timer_cb (EV_P_ ev_timer *w_, int revents);
 
 #if EV_USE_INOTIFY
 
 /* the * 2 is to allow for alignment padding, which for some reason is >> 8 */
 # define EV_INOTIFY_BUFSIZE (sizeof (struct inotify_event) * 2 + NAME_MAX)
 
-static void noinline
+noinline
+static void
 infy_add (EV_P_ ev_stat *w)
 {
   w->wd = inotify_add_watch (fs_fd, w->path,
@@ -4194,7 +4379,8 @@ infy_add (EV_P_ ev_stat *w)
   if (ev_is_active (&w->timer)) ev_unref (EV_A);
 }
 
-static void noinline
+noinline
+static void
 infy_del (EV_P_ ev_stat *w)
 {
   int slot;
@@ -4211,7 +4397,8 @@ infy_del (EV_P_ ev_stat *w)
   inotify_rm_watch (fs_fd, wd);
 }
 
-static void noinline
+noinline
+static void
 infy_wd (EV_P_ int slot, int wd, struct inotify_event *ev)
 {
   if (slot < 0)
@@ -4257,7 +4444,8 @@ infy_cb (EV_P_ ev_io *w, int revents)
     }
 }
 
-inline_size void ecb_cold
+inline_size ecb_cold
+void
 ev_check_2625 (EV_P)
 {
   /* kernels < 2.6.25 are borked
@@ -4365,7 +4553,8 @@ ev_stat_stat (EV_P_ ev_stat *w) EV_THROW
     w->attr.st_nlink = 1;
 }
 
-static void noinline
+noinline
+static void
 stat_timer_cb (EV_P_ ev_timer *w_, int revents)
 {
   ev_stat *w = (ev_stat *)(((char *)w_) - offsetof (ev_stat, timer));
@@ -4594,7 +4783,8 @@ ev_check_stop (EV_P_ ev_check *w) EV_THROW
 #endif
 
 #if EV_EMBED_ENABLE
-void noinline
+noinline
+void
 ev_embed_sweep (EV_P_ ev_embed *w) EV_THROW
 {
   ev_run (w->other, EVRUN_NOWAIT);
@@ -4901,7 +5091,8 @@ ev_once (EV_P_ int fd, int events, ev_tstamp timeout, void (*cb)(int revents, vo
 /*****************************************************************************/
 
 #if EV_WALK_ENABLE
-void ecb_cold
+ecb_cold
+void
 ev_walk (EV_P_ int types, void (*cb)(EV_P_ int type, void *w)) EV_THROW
 {
   int i, j;
diff --git a/third_party/libev/ev.h b/third_party/libev/ev.h
index 864e0f79cd0a851d7682e09e8c85a4a5fdc30edc..d42e2df4769a4d7279b7ab97c901774550b0ea8c 100644
--- a/third_party/libev/ev.h
+++ b/third_party/libev/ev.h
@@ -216,7 +216,7 @@ struct ev_loop;
 /*****************************************************************************/
 
 #define EV_VERSION_MAJOR 4
-#define EV_VERSION_MINOR 20
+#define EV_VERSION_MINOR 24
 
 /* eventmask, revents, events... */
 enum {
@@ -521,10 +521,10 @@ enum {
 
 /* method bits to be ored together */
 enum {
-  EVBACKEND_SELECT  = 0x00000001U, /* about anywhere */
-  EVBACKEND_POLL    = 0x00000002U, /* !win */
+  EVBACKEND_SELECT  = 0x00000001U, /* available just about anywhere */
+  EVBACKEND_POLL    = 0x00000002U, /* !win, !aix, broken on osx */
   EVBACKEND_EPOLL   = 0x00000004U, /* linux */
-  EVBACKEND_KQUEUE  = 0x00000008U, /* bsd */
+  EVBACKEND_KQUEUE  = 0x00000008U, /* bsd, broken on osx */
   EVBACKEND_DEVPOLL = 0x00000010U, /* solaris 8 */ /* NYI */
   EVBACKEND_PORT    = 0x00000020U, /* solaris 10 */
   EVBACKEND_ALL     = 0x0000003FU, /* all known backends */
@@ -606,6 +606,9 @@ ev_is_default_loop (void) EV_THROW
 
 #endif /* multiplicity */
 
+EV_API_DECL ev_tstamp ev_monotonic_time (void) EV_THROW;
+EV_API_DECL ev_tstamp ev_monotonic_now (EV_P) EV_THROW;
+
 /* destroy event loops, also works for the default loop */
 EV_API_DECL void ev_loop_destroy (EV_P);
 
diff --git a/third_party/libev/ev.pod b/third_party/libev/ev.pod
index b67bc859faa131686c13d8b54c3beaf0407e0e3b..633b87ea542a5ffab997ddfa71adc72c97c4f14e 100644
--- a/third_party/libev/ev.pod
+++ b/third_party/libev/ev.pod
@@ -418,8 +418,8 @@ without a system call and thus I<very> fast, but my GNU/Linux system also has
 C<pthread_atfork> which is even faster).
 
 The big advantage of this flag is that you can forget about fork (and
-forget about forgetting to tell libev about forking) when you use this
-flag.
+forget about forgetting to tell libev about forking, although you still
+have to ignore C<SIGPIPE>) when you use this flag.
 
 This flag setting cannot be overridden or specified in the C<LIBEV_FLAGS>
 environment variable.
@@ -691,6 +691,9 @@ watchers (except inside an C<ev_prepare> callback), but it makes most
 sense after forking, in the child process. You I<must> call it (or use
 C<EVFLAG_FORKCHECK>) in the child before resuming or calling C<ev_run>.
 
+In addition, if you want to reuse a loop (via this function or
+C<EVFLAG_FORKCHECK>), you I<also> have to ignore C<SIGPIPE>.
+
 Again, you I<have> to call it on I<any> loop that you want to re-use after
 a fork, I<even if you do not plan to use the loop in the parent>. This is
 because some kernel interfaces *cough* I<kqueue> *cough* do funny things
@@ -2208,7 +2211,7 @@ Periodic watchers are also timers of a kind, but they are very versatile
 
 Unlike C<ev_timer>, periodic watchers are not based on real time (or
 relative time, the physical time that passes) but on wall clock time
-(absolute time, the thing you can read on your calender or clock). The
+(absolute time, the thing you can read on your calendar or clock). The
 difference is that wall clock time can run faster or slower than real
 time, and time jumps are not uncommon (e.g. when you adjust your
 wrist-watch).
@@ -5299,6 +5302,11 @@ assumes that the same (machine) code can be used to call any watcher
 callback: The watcher callbacks have different type signatures, but libev
 calls them using an C<ev_watcher *> internally.
 
+=item null pointers and integer zero are represented by 0 bytes
+
+Libev uses C<memset> to initialise structs and arrays to C<0> bytes, and
+relies on this setting pointers and integers to null.
+
 =item pointer accesses must be thread-atomic
 
 Accessing a pointer value must be atomic, it must both be readable and
diff --git a/third_party/libev/ev_epoll.c b/third_party/libev/ev_epoll.c
index 7472b60e03050d418e36caf3b7849b7680c8d0c6..df118a6fee798621e89dac31178535e506718201 100644
--- a/third_party/libev/ev_epoll.c
+++ b/third_party/libev/ev_epoll.c
@@ -179,7 +179,7 @@ epoll_poll (EV_P_ ev_tstamp timeout)
       if (expect_false ((uint32_t)anfds [fd].egen != (uint32_t)(ev->data.u64 >> 32)))
         {
           /* recreate kernel state */
-          postfork = 1;
+          postfork |= 2;
           continue;
         }
 
@@ -203,7 +203,7 @@ epoll_poll (EV_P_ ev_tstamp timeout)
           /* which is fortunately easy to do for us. */
           if (epoll_ctl (backend_fd, want ? EPOLL_CTL_MOD : EPOLL_CTL_DEL, fd, ev))
             {
-              postfork = 1; /* an error occurred, recreate kernel state */
+              postfork |= 2; /* an error occurred, recreate kernel state */
               continue;
             }
         }
@@ -235,7 +235,8 @@ epoll_poll (EV_P_ ev_tstamp timeout)
     }
 }
 
-int inline_size
+inline_size
+int
 epoll_init (EV_P_ int flags)
 {
 #ifdef EPOLL_CLOEXEC
@@ -260,14 +261,16 @@ epoll_init (EV_P_ int flags)
   return EVBACKEND_EPOLL;
 }
 
-void inline_size
+inline_size
+void
 epoll_destroy (EV_P)
 {
   ev_free (epoll_events);
   array_free (epoll_eperm, EMPTY);
 }
 
-void inline_size
+inline_size
+void
 epoll_fork (EV_P)
 {
   close (backend_fd);
diff --git a/third_party/libev/ev_kqueue.c b/third_party/libev/ev_kqueue.c
index 27def23d43d29b2121e9ba594f503225a2809b09..0c05ab9e77c2f0f713d9e8c6090da21c6a427271 100644
--- a/third_party/libev/ev_kqueue.c
+++ b/third_party/libev/ev_kqueue.c
@@ -43,7 +43,8 @@
 #include <string.h>
 #include <errno.h>
 
-void inline_speed
+inline_speed
+void
 kqueue_change (EV_P_ int fd, int filter, int flags, int fflags)
 {
   ++kqueue_changecnt;
@@ -152,7 +153,8 @@ kqueue_poll (EV_P_ ev_tstamp timeout)
     }
 }
 
-int inline_size
+inline_size
+int
 kqueue_init (EV_P_ int flags)
 {
   /* initialize the kernel queue */
@@ -176,14 +178,16 @@ kqueue_init (EV_P_ int flags)
   return EVBACKEND_KQUEUE;
 }
 
-void inline_size
+inline_size
+void
 kqueue_destroy (EV_P)
 {
   ev_free (kqueue_events);
   ev_free (kqueue_changes);
 }
 
-void inline_size
+inline_size
+void
 kqueue_fork (EV_P)
 {
   /* some BSD kernels don't just destroy the kqueue itself,
diff --git a/third_party/libev/ev_poll.c b/third_party/libev/ev_poll.c
index 48323516dd0c5b7b5bae67c6b7279b611939aedf..bd742b07f317697042bbfd667f714c6e75b845e7 100644
--- a/third_party/libev/ev_poll.c
+++ b/third_party/libev/ev_poll.c
@@ -39,7 +39,8 @@
 
 #include <poll.h>
 
-void inline_size
+inline_size
+void
 pollidx_init (int *base, int count)
 {
   /* consider using memset (.., -1, ...), which is practically guaranteed
@@ -126,7 +127,8 @@ poll_poll (EV_P_ ev_tstamp timeout)
       }
 }
 
-int inline_size
+inline_size
+int
 poll_init (EV_P_ int flags)
 {
   backend_mintime = 1e-3;
@@ -139,7 +141,8 @@ poll_init (EV_P_ int flags)
   return EVBACKEND_POLL;
 }
 
-void inline_size
+inline_size
+void
 poll_destroy (EV_P)
 {
   ev_free (pollidxs);
diff --git a/third_party/libev/ev_port.c b/third_party/libev/ev_port.c
index 9368501cd5d9214e47abf1d2b5e8b89bc1414771..c7b0b70c1f9c9c460ba443f5d24208e779108bcc 100644
--- a/third_party/libev/ev_port.c
+++ b/third_party/libev/ev_port.c
@@ -55,7 +55,8 @@
 #include <string.h>
 #include <errno.h>
 
-void inline_speed
+inline_speed
+void
 port_associate_and_check (EV_P_ int fd, int ev)
 {
   if (0 >
@@ -136,7 +137,8 @@ port_poll (EV_P_ ev_tstamp timeout)
     }
 }
 
-int inline_size
+inline_size
+int
 port_init (EV_P_ int flags)
 {
   /* Initialize the kernel queue */
@@ -163,13 +165,15 @@ port_init (EV_P_ int flags)
   return EVBACKEND_PORT;
 }
 
-void inline_size
+inline_size
+void
 port_destroy (EV_P)
 {
   ev_free (port_events);
 }
 
-void inline_size
+inline_size
+void
 port_fork (EV_P)
 {
   close (backend_fd);
diff --git a/third_party/libev/ev_select.c b/third_party/libev/ev_select.c
index f38d6ca3e01a00d48dd0b858eca0531b981c7912..ed1fc7ad9298055eb5a981bec875923f22242cc3 100644
--- a/third_party/libev/ev_select.c
+++ b/third_party/libev/ev_select.c
@@ -271,7 +271,8 @@ select_poll (EV_P_ ev_tstamp timeout)
 #endif
 }
 
-int inline_size
+inline_size
+int
 select_init (EV_P_ int flags)
 {
   backend_mintime = 1e-6;
@@ -300,7 +301,8 @@ select_init (EV_P_ int flags)
   return EVBACKEND_SELECT;
 }
 
-void inline_size
+inline_size
+void
 select_destroy (EV_P)
 {
   ev_free (vec_ri);
diff --git a/third_party/libev/ev_win32.c b/third_party/libev/ev_win32.c
index d65634cce6bc498bb9ed8ab4e2adefa2f2be9a55..fd671356a4a12f083dd41c830a524f7b4e3b6c49 100644
--- a/third_party/libev/ev_win32.c
+++ b/third_party/libev/ev_win32.c
@@ -39,9 +39,6 @@
 
 #ifdef _WIN32
 
-/* timeb.h is actually xsi legacy functionality */
-#include <sys/timeb.h>
-
 /* note: the comment below could not be substantiated, but what would I care */
 /* MSDN says this is required to handle SIGFPE */
 /* my wild guess would be that using something floating-pointy is required */
@@ -91,6 +88,8 @@ ev_pipe (int filedes [2])
   if (connect (sock [0], (struct sockaddr *)&addr, addr_size))
     goto fail;
 
+  /* TODO: returns INVALID_SOCKET on winsock accept, not < 0. fix it */
+  /* when convenient, probably by just removing error checking altogether? */
   if ((sock [1] = accept (listener, 0, 0)) < 0)
     goto fail;