diff --git a/client/tarantool_checksum/tc_space.c b/client/tarantool_checksum/tc_space.c
index 4d76a771ffe4829ac1faa57a7e32912d76cf878f..37a4e8ceb94708e86dce8d29865b32023df74a15 100644
--- a/client/tarantool_checksum/tc_space.c
+++ b/client/tarantool_checksum/tc_space.c
@@ -51,8 +51,9 @@ int tc_space_init(struct tc_spaces *s) {
 
 void tc_space_free(struct tc_spaces *s)
 {
-	mh_int_t i;
-	mh_foreach(s->t, i) {
+	while (mh_size(s->t) > 0) {
+		mh_int_t i = mh_first(s->t);
+
 		struct tc_space *space = mh_u32ptr_node(s->t, i)->val;
 		mh_u32ptr_del(s->t, i, NULL);
 
diff --git a/include/box/box.h b/include/box/box.h
index 08cd9cb2c3e22065c032a5c47542fc134cf2b444..2070dd9bca43080584f26d524c31a38c00e94e35 100644
--- a/include/box/box.h
+++ b/include/box/box.h
@@ -49,7 +49,7 @@ struct tarantool_cfg;
 struct lua_State;
 
 /** To be called at program start. */
-void box_init(void);
+void box_init(bool init_storage);
 /** To be called at program end. */
 void box_free(void);
 
diff --git a/include/errcode.h b/include/errcode.h
index 5d9248d41b75d400218f8806872d97391130cfd9..3f834717c61c91bd4f1e36962a6c207cfbc8e993 100644
--- a/include/errcode.h
+++ b/include/errcode.h
@@ -55,8 +55,8 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/*  2 */_(ER_ILLEGAL_PARAMS,		2, "Illegal parameters, %s") \
 	/*  3 */_(ER_SECONDARY,			2, "Can't modify data upon a request on the secondary port.") \
 	/*  4 */_(ER_TUPLE_IS_RO,		1, "Tuple is marked as read-only") \
-	/*  5 */_(ER_UNUSED5,			2, "Unused5") \
-	/*  6 */_(ER_UNUSED6,			2, "Unused6") \
+	/*  5 */_(ER_INDEX_TYPE,		2, "Unsupported index type: %s") \
+	/*  6 */_(ER_SPACE_EXISTS,		2, "Space %u already exists") \
 	/*  7 */_(ER_MEMORY_ISSUE,		1, "Failed to allocate %u bytes in %s for %s") \
 	/*  8 */_(ER_UNUSED8,			2, "Unused8") \
 	/*  9 */_(ER_INJECTION,			2, "Error injection '%s'") \
diff --git a/include/lua/init.h b/include/lua/init.h
index 382516efc3490aca83809e71cba52705a6335b07..0088d33046c828cb7c29ce8a197dfada94116ed3 100644
--- a/include/lua/init.h
+++ b/include/lua/init.h
@@ -28,6 +28,7 @@
  * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  * SUCH DAMAGE.
  */
+#include <stddef.h>
 #include <inttypes.h>
 
 struct lua_State;
@@ -119,5 +120,13 @@ int luaL_pushnumber64(struct lua_State *L, uint64_t val);
 struct tbuf;
 void show_plugins_stat(struct tbuf *out);
 
+/**
+ * @brief A palloc-like wrapper to allocate memory using lua_newuserdata
+ * @param ctx lua_State
+ * @param size a number of bytes to allocate
+ * @return a pointer to the allocated memory
+ */
+void *
+lua_region_alloc(void *ctx, size_t size);
 
 #endif /* INCLUDES_TARANTOOL_LUA_H */
diff --git a/include/palloc.h b/include/palloc.h
index d1f89569e73a1091a77fa8bd8c3255318f516d1a..c108a42950dd7e8f2151c968ae1b13b5d5b99339 100644
--- a/include/palloc.h
+++ b/include/palloc.h
@@ -61,6 +61,12 @@ size_t palloc_allocated(struct palloc_pool *);
 
 void palloc_stat(struct tbuf *buf);
 
+static inline void *
+palloc_region_alloc(void *ctx, size_t size)
+{
+	return palloc((struct palloc_pool *) ctx, size);
+}
+
 #if defined(__cplusplus)
 } /* extern "C" */
 #endif /* defined(__cplusplus) */
diff --git a/include/tarantool.h b/include/tarantool.h
index 017ae428814d05b6e5f71b3a52ad5fcb092b0b7c..a32850acf2e7ec8c319df578f1fe377079e217f7 100644
--- a/include/tarantool.h
+++ b/include/tarantool.h
@@ -42,7 +42,7 @@ extern int snapshot_pid;
 extern struct tarantool_cfg cfg;
 extern const char *cfg_filename;
 extern char *cfg_filename_fullpath;
-extern bool init_storage, booting;
+extern bool booting;
 extern char *binary_filename;
 extern char *custom_proc_title;
 int reload_cfg(struct tbuf *out);
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 9929d5be5da4db8974cc4bbc9ab34fc7c6ea7641..3df5f3144828eb5c131872b6036b960112d847e3 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -19,6 +19,7 @@ tarantool_module("box"
     tuple.cc
     tuple_convert.cc
     tuple_update.cc
+    key_def.cc
     index.cc
     hash_index.cc
     tree_index.cc
diff --git a/src/box/box.cc b/src/box/box.cc
index 019ee1bc99ecd4e487dffe331a8f407ebd38907a..00bf4ed46d158cc59e1379a0abb9e8167569851f 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -119,21 +119,16 @@ process_ro(struct port *port, uint32_t op, const char *reqdata, uint32_t reqlen)
 static void
 recover_snap_row(const void *data)
 {
-	assert(primary_indexes_enabled == false);
-
 	const struct box_snap_row *row = (const struct box_snap_row *) data;
 
 	struct space *space = space_find(row->space);
 	Index *index = space_index(space, 0);
-	/* Check to see if the tuple has a sufficient number of fields. */
-	if (unlikely(row->tuple_size < space->max_fieldno)) {
-		tnt_raise(IllegalParams,
-			  "tuple must have all indexed fields");
-	}
+
 	struct tuple *tuple;
 	try {
 		const char *tuple_data = row->data;
-		tuple = tuple_new(row->tuple_size, &tuple_data,
+		tuple = tuple_new(space->format,
+				  row->tuple_size, &tuple_data,
 				  tuple_data + row->data_size);
 	} catch (const ClientError &e) {
 		say_error("\n"
@@ -323,7 +318,7 @@ box_free(void)
 }
 
 void
-box_init(void)
+box_init(bool init_storage)
 {
 	title("loading");
 	atexit(box_free);
@@ -400,10 +395,6 @@ snapshot_space(struct space *sp, void *udata)
 void
 box_snapshot(struct log_io *l, struct fio_batch *batch)
 {
-	/* --init-storage switch */
-	if (primary_indexes_enabled == false)
-		return;
-
 	struct snapshot_space_param ud = { l, batch };
 
 	space_foreach(snapshot_space, &ud);
diff --git a/src/box/box_lua.cc b/src/box/box_lua.cc
index 21503c9928c71d8d945b497609712265f5000660..1cc76df1ec279220fa87627ca2b26c0c6302c6d0 100644
--- a/src/box/box_lua.cc
+++ b/src/box/box_lua.cc
@@ -202,7 +202,7 @@ struct lua_field {
 		uint32_t u32;
 		uint64_t u64;
 	};
-	enum field_data_type type;
+	enum field_type type;
 };
 
 /**
@@ -353,7 +353,11 @@ lbox_tuple_transform(struct lua_State *L)
 	const char *expr = lua_tolstring(L, -1, &expr_len);
 
 	/* Execute tuple_update */
-	struct tuple *new_tuple = tuple_update(tuple, expr, expr + expr_len);
+	struct tuple *new_tuple = tuple_update(tuple_format_ber,
+					       lua_region_alloc, L,
+					       tuple, expr, expr + expr_len);
+	/* Cleanup memory allocated by lua_region_alloc */
+	lua_settop(L, 0);
 	lbox_pushtuple(L, new_tuple);
 	return 1;
 }
@@ -1031,7 +1035,7 @@ lua_table_to_tuple(struct lua_State *L, int index)
 		tuple_len += field.len + varint32_sizeof(field.len);
 		lua_pop(L, 1);
 	}
-	struct tuple *tuple = tuple_alloc(tuple_len);
+	struct tuple *tuple = tuple_alloc(tuple_format_ber, tuple_len);
 	/*
 	 * Important: from here and on if there is an exception,
 	 * the tuple is leaked.
@@ -1058,7 +1062,8 @@ lua_totuple(struct lua_State *L, int index)
 	struct lua_field field;
 	lua_tofield(L, index, &field);
 	if (field.type != UNKNOWN) {
-		tuple = tuple_alloc(field.len + varint32_sizeof(field.len));
+		tuple = tuple_alloc(tuple_format_ber,
+				    field.len + varint32_sizeof(field.len));
 		tuple->field_count = 1;
 		pack_lstr(tuple->data, field.data, field.len);
 		return tuple;
@@ -1587,7 +1592,8 @@ box_unpack_response(struct lua_State *L, const char *s, const char *end)
 		if (tend > end)
 			tnt_raise(IllegalParams, "incorrect packet length");
 
-		struct tuple *tuple = tuple_new(field_count, &s, tend);
+		struct tuple *tuple = tuple_new(tuple_format_ber,
+						field_count, &s, tend);
 		lbox_pushtuple(L, tuple);
 	}
 	return s;
diff --git a/src/box/index.cc b/src/box/index.cc
index 9a7c212c5c6a254df8237dc12ce37e803ce5f976..323e200cd9334cb9ad86f4be18a54abcde2f5153 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -35,8 +35,6 @@
 #include "exception.h"
 #include "space.h"
 
-const char *field_data_type_strs[] = {"UNKNOWN", "NUM", "NUM64", "STR", "\0"};
-STRS(index_type, INDEX_TYPE);
 STRS(iterator_type, ITERATOR_TYPE);
 
 /* {{{ Utilities. **********************************************/
@@ -48,7 +46,7 @@ key_validate_parts(struct key_def *key_def,
 	for (uint32_t part = 0; part < part_count; part++) {
 		uint32_t part_size = load_varint32(&key);
 
-		enum field_data_type part_type = key_def->parts[part].type;
+		enum field_type part_type = key_def->parts[part].type;
 
 		if (part_type == NUM && part_size != sizeof(uint32_t))
 			tnt_raise(ClientError, ER_KEY_FIELD_TYPE, "u32");
diff --git a/src/box/index.h b/src/box/index.h
index bc60a10399d5ff59f16b6aa29a7dd55b49539116..646562b84fec215097c3133d372369cf5fe2f964 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -32,26 +32,11 @@
 #include "tarantool/util.h"
 
 #include "object.h"
+#include "key_def.h"
 
 struct tuple;
 struct space;
 
-/*
- * 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_data_type { UNKNOWN = 0, NUM, NUM64, STRING, field_data_type_MAX };
-extern const char *field_data_type_strs[];
-
-#define INDEX_TYPE(_)                                               \
-	_(HASH,    0)       /* HASH Index  */                       \
-	_(TREE,    1)       /* TREE Index  */                       \
-	_(BITSET,  2)       /* BITSET Index  */                     \
-
-ENUM(index_type, INDEX_TYPE);
-extern const char *index_type_strs[];
-
 /**
  * @abstract Iterator type
  * Controls how to iterate over tuples in an index.
@@ -99,37 +84,6 @@ struct iterator {
 	void (*free)(struct iterator *);
 };
 
-/** Descriptor of a single part in a multipart key. */
-struct key_part {
-	uint32_t fieldno;
-	enum field_data_type type;
-};
-
-/* Descriptor of a multipart key. */
-struct key_def {
-	/* Description of parts of a multipart index. */
-	struct key_part *parts;
-	/*
-	 * An array holding field positions in 'parts' array.
-	 * Imagine there is index[1] = { key_field[0].fieldno=5,
-	 * key_field[1].fieldno=3 }.
-	 * 'parts' array for such index contains data from
-	 * key_field[0] and key_field[1] respectively.
-	 * max_fieldno is 5, and cmp_order array holds offsets of
-	 * field 3 and 5 in 'parts' array: -1, -1, 0, -1, 1.
-	 */
-	uint32_t *cmp_order;
-	/* The size of the 'parts' array. */
-	uint32_t part_count;
-	/*
-	 * The size of 'cmp_order' array (= max fieldno in 'parts'
-	 * array).
-	 */
-	uint32_t max_fieldno;
-	bool is_unique;
-	enum index_type type;
-};
-
 /**
  * Check that the key has correct part count and correct part size
  * for use in an index iterator.
@@ -148,7 +102,7 @@ key_validate(struct key_def *key_def, enum iterator_type type, const char *key,
  * index (i.e. the key must be fully specified).
  */
 void
-primary_key_validate(struct key_def *key_deff, const char *key,
+primary_key_validate(struct key_def *key_def, const char *key,
 		     uint32_t part_count);
 
 
@@ -175,7 +129,6 @@ enum dup_replace_mode {
 	DUP_REPLACE
 };
 
-
 class Index: public Object {
 public:
 
diff --git a/src/box/key_def.cc b/src/box/key_def.cc
new file mode 100644
index 0000000000000000000000000000000000000000..29c51c59443a58b79e15ec74e976fab385cff3e7
--- /dev/null
+++ b/src/box/key_def.cc
@@ -0,0 +1,99 @@
+/*
+ * 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"
+extern "C" {
+#include <cfg/tarantool_box_cfg.h>
+} /* extern "C" */
+#include "exception.h"
+#include <stddef.h>
+
+const char *field_type_strs[] = {"UNKNOWN", "NUM", "NUM64", "STR", "\0"};
+STRS(index_type, INDEX_TYPE);
+
+void
+key_def_create(struct key_def *def, struct tarantool_cfg_space_index *cfg_index)
+{
+	def->max_fieldno = 0;
+	def->part_count = 0;
+
+	def->type = STR2ENUM(index_type, cfg_index->type);
+	if (def->type == index_type_MAX)
+		tnt_raise(LoggedError, ER_INDEX_TYPE, cfg_index->type);
+
+	/* Calculate key part count and maximal field number. */
+	for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) {
+		auto cfg_key = cfg_index->key_field[k];
+
+		if (cfg_key->fieldno == -1) {
+			/* last filled key reached */
+			break;
+		}
+
+		def->max_fieldno = MAX(def->max_fieldno, cfg_key->fieldno);
+		def->part_count++;
+	}
+
+	/* init def array */
+	def->parts = (struct key_part *) malloc(sizeof(struct key_part) *
+						def->part_count);
+
+	uint32_t cmp_order_size = (def->max_fieldno + 1) * sizeof(uint32_t);
+	/* init compare order array */
+	def->cmp_order = (uint32_t *) malloc(cmp_order_size);
+
+	for (uint32_t fieldno = 0; fieldno <= def->max_fieldno; fieldno++)
+		def->cmp_order[fieldno] = UINT32_MAX;
+
+	/* fill fields and compare order */
+	for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) {
+		auto cfg_key = cfg_index->key_field[k];
+
+		if (cfg_key->fieldno == -1) {
+			/* last filled key reached */
+			break;
+		}
+
+		/* fill keys */
+		def->parts[k].fieldno = cfg_key->fieldno;
+		def->parts[k].type = STR2ENUM(field_type, cfg_key->type);
+		/* fill compare order */
+		if (def->cmp_order[cfg_key->fieldno] == UINT32_MAX)
+			def->cmp_order[cfg_key->fieldno] = k;
+	}
+	def->is_unique = cfg_index->unique;
+}
+
+/** Free a key definition. */
+void
+key_def_destroy(struct key_def *key_def)
+{
+	free(key_def->parts);
+	free(key_def->cmp_order);
+}
+
diff --git a/src/box/key_def.h b/src/box/key_def.h
new file mode 100644
index 0000000000000000000000000000000000000000..8cc234082736c727c7afdc99b55e3fb07703162d
--- /dev/null
+++ b/src/box/key_def.h
@@ -0,0 +1,97 @@
+#ifndef TARANTOOL_BOX_KEY_DEF_H_INCLUDED
+#define TARANTOOL_BOX_KEY_DEF_H_INCLUDED
+/*
+ * 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 "tarantool/util.h"
+/*
+ * 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 { UNKNOWN = 0, NUM, NUM64, STRING, field_type_MAX };
+extern const char *field_type_strs[];
+
+
+static inline uint32_t
+field_type_maxlen(enum field_type type)
+{
+	static const uint32_t maxlen[] =
+		{ UINT32_MAX, 4, 8, UINT32_MAX, UINT32_MAX };
+	return maxlen[type];
+}
+
+#define INDEX_TYPE(_)                                               \
+	_(HASH,    0)       /* HASH Index  */                       \
+	_(TREE,    1)       /* TREE Index  */                       \
+	_(BITSET,  2)       /* BITSET Index  */                     \
+
+ENUM(index_type, INDEX_TYPE);
+extern const char *index_type_strs[];
+
+/** Descriptor of a single part in a multipart key. */
+struct key_part {
+	uint32_t fieldno;
+	enum field_type type;
+};
+
+/* Descriptor of a multipart key. */
+struct key_def {
+	/* Description of parts of a multipart index. */
+	struct key_part *parts;
+	/*
+	 * An array holding field positions in 'parts' array.
+	 * Imagine there is index[1] = { key_field[0].fieldno=5,
+	 * key_field[1].fieldno=3 }.
+	 * 'parts' array for such index contains data from
+	 * key_field[0] and key_field[1] respectively.
+	 * max_fieldno is 5, and cmp_order array holds offsets of
+	 * field 3 and 5 in 'parts' array: -1, -1, -1, 0, -1, 1.
+	 */
+	uint32_t *cmp_order;
+	/* The size of the 'parts' array. */
+	uint32_t part_count;
+	/*
+	 * Max fieldno in 'parts' array. Defines the size of
+	 * cmp_order array (which is max_fieldno + 1).
+	 */
+	uint32_t max_fieldno;
+	bool is_unique;
+	enum index_type type;
+};
+
+struct tarantool_cfg_space_index;
+
+void
+key_def_create(struct key_def *def,
+	       struct tarantool_cfg_space_index *cfg_index);
+
+void
+key_def_destroy(struct key_def *def);
+
+#endif /* TARANTOOL_BOX_KEY_DEF_H_INCLUDED */
diff --git a/src/box/request.cc b/src/box/request.cc
index 4a476811f8e2bb1d3a821ab387ef6b3713cad969..cf71febf40db507cd032593fa8639ec2b942a6ff 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -72,16 +72,17 @@ execute_replace(struct request *request, struct txn *txn)
 	txn_add_redo(txn, request->type, request->data, request->len);
 	const char **reqpos = &request->data;
 	const char *reqend = request->data + request->len;
-	struct space *sp = read_space(reqpos, reqend);
+	struct space *space = read_space(reqpos, reqend);
 	request->flags |= (pick_u32(reqpos, reqend) &
 			   BOX_ALLOWED_REQUEST_FLAGS);
 	uint32_t field_count = pick_u32(reqpos, reqend);
 
-	struct tuple *new_tuple = tuple_new(field_count, reqpos, reqend);
+	struct tuple *new_tuple = tuple_new(space->format, field_count,
+					    reqpos, reqend);
 	try {
-		space_validate_tuple(sp, new_tuple);
+		space_validate_tuple(space, new_tuple);
 		enum dup_replace_mode mode = dup_replace_mode(request->flags);
-		txn_replace(txn, sp, NULL, new_tuple, mode);
+		txn_replace(txn, space, NULL, new_tuple, mode);
 
 	} catch (const Exception& e) {
 		tuple_free(new_tuple);
@@ -96,7 +97,7 @@ execute_update(struct request *request, struct txn *txn)
 	txn_add_redo(txn, request->type, request->data, request->len);
 	const char **reqpos = &request->data;
 	const char *reqend = request->data + request->len;
-	struct space *sp = read_space(reqpos, reqend);
+	struct space *space = read_space(reqpos, reqend);
 	request->flags |= (pick_u32(reqpos, reqend) &
 			   BOX_ALLOWED_REQUEST_FLAGS);
 	/* Parse UPDATE request. */
@@ -104,7 +105,7 @@ execute_update(struct request *request, struct txn *txn)
 	uint32_t key_part_count;
 	const char *key = read_key(reqpos, reqend, &key_part_count);
 
-	Index *pk = space_index(sp, 0);
+	Index *pk = space_index(space, 0);
 	/* Try to find the tuple by primary key. */
 	primary_key_validate(pk->key_def, key, key_part_count);
 	struct tuple *old_tuple = pk->findByKey(key, key_part_count);
@@ -113,10 +114,13 @@ execute_update(struct request *request, struct txn *txn)
 		return;
 
 	/* Update the tuple. */
-	struct tuple *new_tuple = tuple_update(old_tuple, *reqpos, reqend);
+	struct tuple *new_tuple = tuple_update(space->format,
+					       palloc_region_alloc,
+					       fiber->gc_pool,
+					       old_tuple, *reqpos, reqend);
 	try {
-		space_validate_tuple(sp, new_tuple);
-		txn_replace(txn, sp, old_tuple, new_tuple, DUP_INSERT);
+		space_validate_tuple(space, new_tuple);
+		txn_replace(txn, space, old_tuple, new_tuple, DUP_INSERT);
 	} catch (const Exception& e) {
 		tuple_free(new_tuple);
 		throw;
@@ -130,9 +134,9 @@ execute_select(struct request *request, struct port *port)
 {
 	const char **reqpos = &request->data;
 	const char *reqend = request->data + request->len;
-	struct space *sp = read_space(reqpos, reqend);
+	struct space *space = read_space(reqpos, reqend);
 	uint32_t index_no = pick_u32(reqpos, reqend);
-	Index *index = index_find(sp, index_no);
+	Index *index = index_find(space, index_no);
 	uint32_t offset = pick_u32(reqpos, reqend);
 	uint32_t limit = pick_u32(reqpos, reqend);
 	uint32_t count = pick_u32(reqpos, reqend);
@@ -181,7 +185,7 @@ execute_delete(struct request *request, struct txn *txn)
 	txn_add_redo(txn, type, request->data, request->len);
 	const char **reqpos = &request->data;
 	const char *reqend = request->data + request->len;
-	struct space *sp = read_space(reqpos, reqend);
+	struct space *space = read_space(reqpos, reqend);
 	if (type == DELETE) {
 		request->flags |= pick_u32(reqpos, reqend) &
 			BOX_ALLOWED_REQUEST_FLAGS;
@@ -190,14 +194,14 @@ execute_delete(struct request *request, struct txn *txn)
 	uint32_t key_part_count;
 	const char *key = read_key(reqpos, reqend, &key_part_count);
 	/* Try to find tuple by primary key */
-	Index *pk = space_index(sp, 0);
+	Index *pk = space_index(space, 0);
 	primary_key_validate(pk->key_def, key, key_part_count);
 	struct tuple *old_tuple = pk->findByKey(key, key_part_count);
 
 	if (old_tuple == NULL)
 		return;
 
-	txn_replace(txn, sp, old_tuple, NULL, DUP_REPLACE_OR_INSERT);
+	txn_replace(txn, space, old_tuple, NULL, DUP_REPLACE_OR_INSERT);
 }
 
 /** To collects stats, we need a valid request type.
diff --git a/src/box/space.cc b/src/box/space.cc
index 19b739d220fd4c7f3496aca6406ba7783b872cc5..d9c255010411abbeb9b31634804c152533b49948 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -44,29 +44,80 @@ extern "C" {
 
 static struct mh_i32ptr_t *spaces;
 
-bool secondary_indexes_enabled = false;
-bool primary_indexes_enabled = false;
+/**
+ * Secondary indexes are built in bulk after all data is
+ * recovered. This flag indicates that the indexes are
+ * already built and ready for use.
+ */
+static bool secondary_indexes_enabled = false;
+/**
+ * Primary indexes are enabled only after reading the snapshot.
+ */
+static bool primary_indexes_enabled = false;
 
-struct space *
-space_create(uint32_t space_no, struct key_def *key_defs, uint32_t key_count, uint32_t arity)
+
+static void
+space_create(struct space *space, uint32_t space_no,
+	     struct key_def *key_defs, uint32_t key_count,
+	     uint32_t arity)
+{
+	memset(space, 0, sizeof(struct space));
+	space->no = space_no;
+	space->arity = arity;
+	space->key_defs = key_defs;
+	space->key_count = key_count;
+	space->format = tuple_format_new(key_defs, key_count);
+	/* fill space indexes */
+	for (uint32_t j = 0; j < key_count; ++j) {
+		struct key_def *key_def = &space->key_defs[j];
+		Index *index = Index::factory(key_def->type, key_def, space);
+		if (index == NULL) {
+			tnt_raise(LoggedError, ER_MEMORY_ISSUE,
+				  "class Index", "malloc");
+		}
+		space->index[j] = index;
+	}
+}
+
+static void
+space_destroy(struct space *space)
 {
+	for (uint32_t j = 0 ; j < space->key_count; j++) {
+		Index *index = space->index[j];
+		delete index;
+		key_def_destroy(&space->key_defs[j]);
+	}
+	free(space->key_defs);
+}
 
+struct space *
+space_new(uint32_t space_no, struct key_def *key_defs,
+	  uint32_t key_count, uint32_t arity)
+{
 	struct space *space = space_by_n(space_no);
 	if (space)
-		panic("Space %d is already exists", space_no);
-	space = (struct space *) calloc(sizeof(struct space), 1);
-	space->no = space_no;
+		tnt_raise(LoggedError, ER_SPACE_EXISTS, space_no);
+
+	space = (struct space *) malloc(sizeof(struct space));
+
+	space_create(space, space_no, key_defs, key_count, arity);
 
 	const struct mh_i32ptr_node_t node = { space->no, space };
 	mh_i32ptr_put(spaces, &node, NULL, NULL);
 
-	space->arity = arity;
-	space->key_defs = key_defs;
-	space->key_count = key_count;
-
 	return space;
 }
 
+static void
+space_delete(struct space *space)
+{
+	const struct mh_i32ptr_node_t node = { space->no, NULL };
+	mh_int_t k = mh_i32ptr_get(spaces, &node, NULL);
+	assert(k != mh_end(spaces));
+	mh_i32ptr_del(spaces, k, NULL);
+	space_destroy(space);
+	free(space);
+}
 
 /* return space by its number */
 struct space *
@@ -109,22 +160,6 @@ space_foreach(void (*func)(struct space *sp, void *udata), void *udata) {
 	}
 }
 
-/** Set index by index no */
-void
-space_set_index(struct space *sp, uint32_t index_no, Index *idx)
-{
-	assert(index_no < BOX_INDEX_MAX);
-	sp->index[index_no] = idx;
-}
-
-/** Free a key definition. */
-static void
-key_free(struct key_def *key_def)
-{
-	free(key_def->parts);
-	free(key_def->cmp_order);
-}
-
 struct tuple *
 space_replace(struct space *sp, struct tuple *old_tuple,
 	      struct tuple *new_tuple, enum dup_replace_mode mode)
@@ -165,169 +200,25 @@ space_replace(struct space *sp, struct tuple *old_tuple,
 void
 space_validate_tuple(struct space *sp, struct tuple *new_tuple)
 {
-	/* Check to see if the tuple has a sufficient number of fields. */
-	if (new_tuple->field_count < sp->max_fieldno)
-		tnt_raise(IllegalParams,
-			  "tuple must have all indexed fields");
-
 	if (sp->arity > 0 && sp->arity != new_tuple->field_count)
 		tnt_raise(IllegalParams,
 			  "tuple field count must match space cardinality");
 
-	/* Sweep through the tuple and check the field sizes. */
-	struct tuple_iterator it;
-	tuple_rewind(&it, new_tuple);
-	const char *field;
-	uint32_t len;
-	uint32_t fieldno = 0;
-	while ((field = tuple_next(&it, &len))) {
-		if (fieldno == sp->max_fieldno)
-			break;
-		/*
-		 * Check fixed size fields (NUM and NUM64) and
-		 * skip undefined size fields (STRING and UNKNOWN).
-		 */
-		if (sp->field_types[fieldno] == NUM) {
-			if (len != sizeof(uint32_t))
-				tnt_raise(ClientError, ER_KEY_FIELD_TYPE,
-					  "NUM");
-		} else if (sp->field_types[fieldno] == NUM64) {
-			if (len != sizeof(uint64_t))
-				tnt_raise(ClientError, ER_KEY_FIELD_TYPE,
-					  "NUM64");
-		}
-		fieldno++;
-	}
 }
 
 void
 space_free(void)
 {
-	mh_int_t i;
+	while (mh_size(spaces) > 0) {
+		mh_int_t i = mh_first(spaces);
 
-	mh_foreach(spaces, i) {
 		struct space *space = (struct space *)
 				mh_i32ptr_node(spaces, i)->val;
-		mh_i32ptr_del(spaces, i, NULL);
-
-		for (uint32_t j = 0 ; j < space->key_count; j++) {
-			Index *index = space->index[j];
-			delete index;
-			key_free(&space->key_defs[j]);
-		}
-
-		free(space->key_defs);
-		free(space->field_types);
-		free(space);
+		space_delete(space);
 	}
-
+	tuple_free();
 }
 
-static void
-key_init(struct key_def *def, struct tarantool_cfg_space_index *cfg_index)
-{
-	def->max_fieldno = 0;
-	def->part_count = 0;
-
-	def->type = STR2ENUM(index_type, cfg_index->type);
-	if (def->type == index_type_MAX)
-		panic("Wrong index type: %s", cfg_index->type);
-
-	/* Calculate key part count and maximal field number. */
-	for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) {
-		auto cfg_key = cfg_index->key_field[k];
-
-		if (cfg_key->fieldno == -1) {
-			/* last filled key reached */
-			break;
-		}
-
-		def->max_fieldno = MAX(def->max_fieldno, cfg_key->fieldno);
-		def->part_count++;
-	}
-
-	/* init def array */
-	def->parts = (struct key_part *) malloc(sizeof(struct key_part) *
-						def->part_count);
-	if (def->parts == NULL) {
-		panic("can't allocate def parts array for index");
-	}
-
-	/* init compare order array */
-	def->max_fieldno++;
-	def->cmp_order = (uint32_t *) malloc(def->max_fieldno * sizeof(uint32_t));
-	if (def->cmp_order == NULL) {
-		panic("can't allocate def cmp_order array for index");
-	}
-	for (uint32_t fieldno = 0; fieldno < def->max_fieldno; fieldno++) {
-		def->cmp_order[fieldno] = BOX_FIELD_MAX;
-	}
-
-	/* fill fields and compare order */
-	for (uint32_t k = 0; cfg_index->key_field[k] != NULL; ++k) {
-		auto cfg_key = cfg_index->key_field[k];
-
-		if (cfg_key->fieldno == -1) {
-			/* last filled key reached */
-			break;
-		}
-
-		/* fill keys */
-		def->parts[k].fieldno = cfg_key->fieldno;
-		def->parts[k].type = STR2ENUM(field_data_type, cfg_key->type);
-		/* fill compare order */
-		if (def->cmp_order[cfg_key->fieldno] == BOX_FIELD_MAX)
-			def->cmp_order[cfg_key->fieldno] = k;
-	}
-	def->is_unique = cfg_index->unique;
-}
-
-/**
- * Extract all available field info from keys
- *
- * @param space		space to extract field info for
- * @param key_count	the number of keys
- * @param key_defs	key description array
- */
-static void
-space_init_field_types(struct space *space)
-{
-	uint32_t i, max_fieldno;
-	uint32_t key_count = space->key_count;
-	struct key_def *key_defs = space->key_defs;
-
-	/* find max max field no */
-	max_fieldno = 0;
-	for (i = 0; i < key_count; i++) {
-		max_fieldno= MAX(max_fieldno, key_defs[i].max_fieldno);
-	}
-
-	/* alloc & init field type info */
-	space->max_fieldno = max_fieldno;
-	space->field_types = (enum field_data_type *)
-			     calloc(max_fieldno, sizeof(enum field_data_type));
-
-	/* extract field type info */
-	for (i = 0; i < key_count; i++) {
-		struct key_def *def = &key_defs[i];
-		for (uint32_t pi = 0; pi < def->part_count; pi++) {
-			struct key_part *part = &def->parts[pi];
-			assert(part->fieldno < max_fieldno);
-			space->field_types[part->fieldno] = part->type;
-		}
-	}
-
-#ifndef NDEBUG
-	/* validate field type info */
-	for (i = 0; i < key_count; i++) {
-		struct key_def *def = &key_defs[i];
-		for (uint32_t pi = 0; pi < def->part_count; pi++) {
-			struct key_part *part = &def->parts[pi];
-			assert(space->field_types[part->fieldno] == part->type);
-		}
-	}
-#endif
-}
 
 static void
 space_config()
@@ -346,50 +237,26 @@ space_config()
 
 		assert(cfg.memcached_port == 0 || i != cfg.memcached_space);
 
-		struct space *space = space_by_n(i);
-		if (space)
-			panic("space %u is already exists", i);
-
-		space = (struct space *) calloc(sizeof(struct space), 1);
-		space->no = i;
-
-		space->arity = (cfg_space->cardinality != -1) ?
-					cfg_space->cardinality : 0;
+		uint32_t arity = (cfg_space->cardinality != -1 ?
+				  cfg_space->cardinality : 0);
 		/*
 		 * Collect key/field info. We need aggregate
 		 * information on all keys before we can create
 		 * indexes.
 		 */
-		space->key_count = 0;
-		for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) {
-			++space->key_count;
-		}
+		uint32_t key_count = 0;
+		while (cfg_space->index[key_count] != NULL)
+			key_count++;
 
+		struct key_def *key_defs = (struct key_def *)
+			malloc(key_count * sizeof(struct key_def));
 
-		space->key_defs = (struct key_def *) malloc(space->key_count *
-							    sizeof(struct key_def));
-		if (space->key_defs == NULL) {
-			panic("can't allocate key def array");
-		}
-		for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) {
-			auto cfg_index = cfg_space->index[j];
-			key_init(&space->key_defs[j], cfg_index);
-		}
-		space_init_field_types(space);
-
-		/* fill space indexes */
 		for (uint32_t j = 0; cfg_space->index[j] != NULL; ++j) {
 			auto cfg_index = cfg_space->index[j];
-			enum index_type type = STR2ENUM(index_type, cfg_index->type);
-			struct key_def *key_def = &space->key_defs[j];
-			Index *index = Index::factory(type, key_def, space);
-			assert(index != NULL);
-			space->index[j] = index;
+			key_def_create(&key_defs[j], cfg_index);
 		}
+		(void) space_new(i, key_defs, key_count, arity);
 
-		const struct mh_i32ptr_node_t node =
-			{ space->no, space };
-		mh_i32ptr_put(spaces, &node, NULL, NULL);
 		say_info("space %i successfully configured", i);
 	}
 }
@@ -398,6 +265,7 @@ void
 space_init(void)
 {
 	spaces = mh_i32ptr_new();
+	tuple_init();
 
 	/* configure regular spaces */
 	space_config();
@@ -548,7 +416,7 @@ check_spaces(struct tarantool_cfg *conf)
 				}
 
 				/* key must has valid type */
-				if (STR2ENUM(field_data_type, key->type) == field_data_type_MAX) {
+				if (STR2ENUM(field_type, key->type) == field_type_MAX) {
 					out_warning(CNF_OK, "(space = %zu index = %zu) "
 						    "unknown field data type: `%s'", i, j, key->type);
 					return -1;
@@ -628,8 +496,8 @@ check_spaces(struct tarantool_cfg *conf)
 						break;
 
 					uint32_t f = key->fieldno;
-					enum field_data_type t = STR2ENUM(field_data_type, key->type);
-					assert(t != field_data_type_MAX);
+					enum field_type t = STR2ENUM(field_type, key->type);
+					assert(t != field_type_MAX);
 					if (types[f] != t) {
 						if (types[f] == UNKNOWN) {
 							types[f] = t;
diff --git a/src/box/space.h b/src/box/space.h
index ec996baa95d21b8baa2a671e834b33d085d1a53e..8bd6aff5f5a649c09413275b133ce8c67f2b9639 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -29,6 +29,7 @@
  * SUCH DAMAGE.
  */
 #include "index.h"
+#include "key_def.h"
 #include <exception.h>
 
 #include <box/box.h>
@@ -38,8 +39,8 @@ struct tarantool_cfg;
 struct space {
 	Index *index[BOX_INDEX_MAX];
 	/** If not set (is 0), any tuple in the
-	 * space can have any number of fields (but
-	 * @sa max_fieldno). If set, Each tuple
+	 * space can have any number of fields.
+	 * If set, each tuple
 	 * must have exactly this many fields.
 	 */
 	uint32_t arity;
@@ -57,25 +58,11 @@ struct space {
 	 */
 	struct key_def *key_defs;
 
-	/**
-	 * Field types of indexed fields. This is an array of size
-	 * field_count. If there are gaps, i.e. fields that do not
-	 * participate in any index and thus we cannot infer their
-	 * type, then respective array members have value UNKNOWN.
-	 * XXX: right now UNKNOWN is also set for fields which types
-	 * in two indexes contradict each other.
-	 */
-	enum field_data_type *field_types;
-
-	/**
-	 * Max field no which participates in any of the space indexes.
-	 * Each tuple in this space must have, therefore, at least
-	 * field_count fields.
-	 */
-	uint32_t max_fieldno;
-
 	/** Space number. */
 	uint32_t no;
+
+	/** Default tuple format used by this space */
+	struct tuple_format *format;
 };
 
 
@@ -190,10 +177,6 @@ space_index(struct space *sp, uint32_t index_no)
 	return NULL;
 }
 
-/** Set index by index no. */
-void
-space_set_index(struct space *sp, uint32_t index_no, Index *idx);
-
 /**
  * Call a visitor function on every enabled space.
  */
@@ -217,7 +200,6 @@ space_find(uint32_t space_no)
 	tnt_raise(ClientError, ER_NO_SUCH_SPACE, space_no);
 }
 
-
 /** Get key_def ordinal number. */
 static inline uint32_t
 key_def_n(struct space *sp, struct key_def *kp)
@@ -226,21 +208,9 @@ key_def_n(struct space *sp, struct key_def *kp)
 	return kp - sp->key_defs;
 }
 
-static inline uint32_t
-space_max_fieldno(struct space *sp)
-{
-	return sp->max_fieldno;
-}
-
-static inline enum field_data_type
-space_field_type(struct space *sp, uint32_t no)
-{
-	return sp->field_types[no];
-}
-
-
 struct space *
-space_create(uint32_t space_no, struct key_def *key_defs, uint32_t key_count, uint32_t arity);
+space_new(uint32_t space_no, struct key_def *key_defs,
+	  uint32_t key_count, uint32_t arity);
 
 
 /** Get index ordinal number in space. */
@@ -257,17 +227,6 @@ index_is_primary(Index *index)
 	return index_n(index) == 0;
 }
 
-/**
- * Secondary indexes are built in bulk after all data is
- * recovered. This flag indicates that the indexes are
- * already built and ready for use.
- */
-extern bool secondary_indexes_enabled;
-/**
- * Primary indexes are enabled only after reading the snapshot.
- */
-extern bool primary_indexes_enabled;
-
 void space_init(void);
 void space_free(void);
 int
@@ -277,7 +236,6 @@ void begin_build_primary_indexes(void);
 void end_build_primary_indexes(void);
 void build_secondary_indexes(void);
 
-
 static inline Index *
 index_find(struct space *sp, uint32_t index_no)
 {
diff --git a/src/box/tuple.cc b/src/box/tuple.cc
index 76d1e5dc79bfb5bda2e80dc9d10e36e61285665e..14ba42296847a250ec7c293ba4def6a409a1dce5 100644
--- a/src/box/tuple.cc
+++ b/src/box/tuple.cc
@@ -31,22 +31,189 @@
 #include <salloc.h>
 #include "tbuf.h"
 
-#include "index.h"
+#include "key_def.h"
 #include "tuple_update.h"
 #include <exception.h>
 #include <palloc.h>
 #include <fiber.h>
 #include "scoped_guard.h"
+#include <stdio.h>
+
+/** Global table of tuple formats */
+struct tuple_format **tuple_formats;
+struct tuple_format *tuple_format_ber;
+
+static uint32_t formats_size, formats_capacity;
+
+/** Extract all available type info from keys. */
+void
+field_type_create(enum field_type *types, uint32_t field_count,
+		  struct key_def *key_def, uint32_t key_count)
+{
+	/* There may be fields between indexed fields (gaps). */
+	memset(types, 0, sizeof(*types) * field_count);
+
+	struct key_def *end = key_def + key_count;
+	/* extract field type info */
+	for (; key_def < end; key_def++) {
+		struct key_part *part = key_def->parts;
+		struct key_part *pend = part + key_def->part_count;
+		for (; part < pend; part++) {
+			assert(part->fieldno < field_count);
+			types[part->fieldno] = part->type;
+		}
+	}
+}
+
+static struct tuple_format *
+tuple_format_alloc_and_register(struct key_def *key_def,
+				uint32_t key_count)
+{
+	uint32_t total;
+	struct tuple_format *format;
+	struct key_def *end = key_def + key_count;
+	uint32_t max_fieldno = 0;
+	uint32_t field_count;
+
+	/* find max max field no */
+	for (; key_def < end; key_def++)
+		max_fieldno= MAX(max_fieldno, key_def->max_fieldno);
+
+	if (formats_size == formats_capacity) {
+		uint32_t new_capacity = formats_capacity ?
+			formats_capacity * 2 : 16;
+		struct tuple_format **formats;
+		if (new_capacity >= UINT16_MAX)
+			goto error;
+		 formats = (struct tuple_format **) realloc(tuple_formats,
+				new_capacity * sizeof(tuple_formats[0]));
+		if (formats == NULL)
+			goto error;
+
+		formats_capacity = new_capacity;
+		tuple_formats = formats;
+	}
+	field_count = key_count > 0 ? max_fieldno + 1 : 0;
+
+	total = sizeof(struct tuple_format) +
+		field_count * sizeof(int32_t) +
+		field_count * sizeof(enum field_type);
+
+	format = (struct tuple_format *) malloc(total);
+
+	if (format == NULL)
+		goto error;
+
+	format->id = formats_size++;
+	format->max_fieldno = max_fieldno;
+	format->field_count = field_count;
+	format->types = (enum field_type *)
+		((char *) format + sizeof(*format) +
+		field_count * sizeof(int32_t));
+	tuple_formats[format->id] = format;
+	return format;
+error:
+	tnt_raise(LoggedError, ER_MEMORY_ISSUE,
+		  sizeof(struct tuple_format), "tuple format", "malloc");
+	return NULL;
+}
+
+struct tuple_format *
+tuple_format_new(struct key_def *key_def, uint32_t key_count)
+{
+	struct tuple_format *format =
+		tuple_format_alloc_and_register(key_def, key_count);
+
+	field_type_create(format->types, format->field_count,
+			  key_def, key_count);
+
+	int32_t i = 0;
+	uint32_t prev_offset = 0;
+	/*
+	 * In the format, store all offsets available,
+	 * they may be useful.
+	 */
+	for (; i < format->max_fieldno; i++) {
+		uint32_t maxlen = field_type_maxlen(format->types[i]);
+		if (maxlen == UINT32_MAX)
+			break;
+		format->offset[i] = (varint32_sizeof(maxlen) + maxlen +
+				     prev_offset);
+		assert(format->offset[i] > 0);
+		prev_offset = format->offset[i];
+	}
+	int j = 0;
+	for (; i < format->max_fieldno; i++) {
+		/*
+		 * In the tuple, store only offsets necessary to
+		 * quickly access indexed fields. Start from
+		 * field 1, not field 0, field 0 offset is 0.
+		 */
+		if (format->types[i + 1] == UNKNOWN)
+			format->offset[i] = INT32_MIN;
+		else
+			format->offset[i] = --j;
+	}
+	if (format->field_count > 0) {
+		/*
+		 * The last offset is always there and is unused,
+		 * to simplify the loop in tuple_init_field_map()
+		 */
+		format->offset[format->field_count - 1] = INT32_MIN;
+	}
+	format->field_map_size = -j * sizeof(uint32_t);
+	return format;
+}
+
+/*
+ * Validate a new tuple format and initialize tuple-local
+ * format data.
+ */
+static inline void
+tuple_init_field_map(struct tuple *tuple, struct tuple_format *format)
+{
+	/* Check to see if the tuple has a sufficient number of fields. */
+	if (tuple->field_count < format->field_count)
+		tnt_raise(IllegalParams,
+			  "tuple must have all indexed fields");
+
+	int32_t *offset = format->offset;
+	enum field_type *type = format->types;
+	enum field_type *end = format->types + format->field_count;
+	const char *pos = tuple->data;
+	uint32_t *field_map = (uint32_t *) tuple;
+
+	for (; type < end; offset++, type++) {
+		if (pos >= tuple->data + tuple->bsize)
+			tnt_raise(IllegalParams,
+				  "incorrect tuple format");
+		uint32_t len = load_varint32(&pos);
+		uint32_t type_maxlen = field_type_maxlen(*type);
+		/*
+		 * For fixed offsets, validate fields have
+		 * correct lengths.
+		 */
+		if (type_maxlen != UINT32_MAX && len != type_maxlen) {
+			tnt_raise(ClientError, ER_KEY_FIELD_TYPE,
+				  field_type_strs[*type]);
+		}
+		pos += len;
+		if (*offset < 0 && *offset != INT32_MIN)
+			field_map[*offset] = pos - tuple->data;
+	}
+}
 
 /** Allocate a tuple */
 struct tuple *
-tuple_alloc(size_t size)
+tuple_alloc(struct tuple_format *format, size_t size)
 {
-	size_t total = sizeof(struct tuple) + size;
-	struct tuple *tuple = (struct tuple *) salloc(total, "tuple");
+	size_t total = sizeof(struct tuple) + size + format->field_map_size;
+	char *ptr = (char *) salloc(total, "tuple");
+	struct tuple *tuple = (struct tuple *)(ptr + format->field_map_size);
 
-	tuple->flags = tuple->refs = 0;
+	tuple->refs = 0;
 	tuple->bsize = size;
+	tuple->format_id = tuple_format_id(format);
 
 	say_debug("tuple_alloc(%zu) = %p", size, tuple);
 	return tuple;
@@ -61,7 +228,8 @@ tuple_free(struct tuple *tuple)
 {
 	say_debug("tuple_free(%p)", tuple);
 	assert(tuple->refs == 0);
-	sfree(tuple);
+	char *ptr = (char *) tuple - tuple_format(tuple)->field_map_size;
+	sfree(ptr);
 }
 
 /**
@@ -87,36 +255,39 @@ tuple_ref(struct tuple *tuple, int count)
  * @returns field data if field exists or NULL
  */
 const char *
-tuple_field_old(struct tuple *tuple, uint32_t i)
+tuple_field_old(const struct tuple_format *format,
+		const struct tuple *tuple, uint32_t i)
 {
 	const char *field = tuple->data;
-	const char *tuple_end = tuple->data + tuple->bsize;
+
+	if (i == 0)
+		return field;
+	i--;
+	if (i < format->max_fieldno) {
+		if (format->offset[i] > 0)
+			return field + format->offset[i];
+		if (format->offset[i] != INT32_MIN) {
+			uint32_t *field_map = (uint32_t *) tuple;
+			int32_t idx = format->offset[i];
+			return field + field_map[idx];
+		}
+	}
+	const char *tuple_end = field + tuple->bsize;
 
 	while (field < tuple_end) {
-		if (i == 0)
-			return field;
 		uint32_t len = load_varint32(&field);
 		field += len;
+		if (i == 0)
+			return field;
 		i--;
 	}
 	return tuple_end;
 }
 
 const char *
-tuple_field(const struct tuple *tuple, uint32_t field_no, uint32_t *len)
+tuple_seek(struct tuple_iterator *it, uint32_t i, uint32_t *len)
 {
-	const char *field = tuple_field_old((struct tuple *) tuple, field_no);
-	if (field < tuple->data + tuple->bsize) {
-		*len = load_varint32(&field);
-		return field;
-	}
-	return NULL;
-}
-
-const char *
-tuple_seek(struct tuple_iterator *it, uint32_t field_no, uint32_t *len)
-{
-	it->pos = tuple_field_old((struct tuple *) it->tuple, field_no);
+	it->pos = tuple_field_old(tuple_format(it->tuple), it->tuple, i);
 	return tuple_next(it, len);
 }
 
@@ -195,31 +366,28 @@ tuple_print(struct tbuf *buf, const struct tuple *tuple)
 	tbuf_printf(buf, "}");
 }
 
-static void *
-palloc_region_alloc(void *ctx, size_t size)
-{
-	return palloc((struct palloc_pool *) ctx, size);
-}
-
 struct tuple *
-tuple_update(const struct tuple *old_tuple, const char *expr,
+tuple_update(struct tuple_format *format,
+	     void *(*region_alloc)(void *, size_t), void *alloc_ctx,
+	     const struct tuple *old_tuple, const char *expr,
 	     const char *expr_end)
 {
 	uint32_t new_size = 0;
 	uint32_t new_field_count = 0;
 	struct tuple_update *update =
-		tuple_update_prepare(palloc_region_alloc, fiber->gc_pool,
+		tuple_update_prepare(region_alloc, alloc_ctx,
 				     expr, expr_end, old_tuple->data,
 				     old_tuple->data + old_tuple->bsize,
 				     old_tuple->field_count, &new_size,
 				     &new_field_count);
 
 	/* Allocate a new tuple. */
-	struct tuple *new_tuple = tuple_alloc(new_size);
+	struct tuple *new_tuple = tuple_alloc(format, new_size);
 	new_tuple->field_count = new_field_count;
 
 	try {
 		tuple_update_execute(update, new_tuple->data);
+		tuple_init_field_map(new_tuple, format);
 	} catch (const Exception&) {
 		tuple_free(new_tuple);
 		throw;
@@ -228,90 +396,76 @@ tuple_update(const struct tuple *old_tuple, const char *expr,
 }
 
 struct tuple *
-tuple_new(uint32_t field_count, const char **data, const char *end)
+tuple_new(struct tuple_format *format, uint32_t field_count,
+	  const char **data, const char *end)
 {
 	size_t tuple_len = end - *data;
 
 	if (tuple_len != tuple_range_size(data, end, field_count))
 		tnt_raise(IllegalParams, "tuple_new(): incorrect tuple format");
 
-	struct tuple *new_tuple = tuple_alloc(tuple_len);
+	struct tuple *new_tuple = tuple_alloc(format, tuple_len);
 	new_tuple->field_count = field_count;
 	memcpy(new_tuple->data, end - tuple_len, tuple_len);
+	try {
+		tuple_init_field_map(new_tuple, format);
+	} catch (...) {
+		tuple_free(new_tuple);
+		throw;
+	}
 	return new_tuple;
 }
 
+/*
+ * Compare two tuple fields.
+ * Separate version exists since compare is a very
+ * often used operation, so any performance speed up
+ * in it can have dramatic impact on the overall
+ * server performance.
+ */
 static inline int
-tuple_compare_field(const char *field_a, uint32_t size_a,
-		    const char *field_b, uint32_t size_b,
-		    enum field_data_type type)
+tuple_compare_field(const char *field_a, const char *field_b,
+		    enum field_type type)
 {
-	/*
-	 * field_a is always a tuple field.
-	 * field_b can be either a tuple field or a key part.
-	 * All tuple fields were validated before by space_validate_tuple().
-	 * All key parts were validated before by key_validate().
-	 */
-	switch (type) {
-	case NUM:
-	{
-		assert(size_a == sizeof(uint32_t));
-		assert(size_b == sizeof(uint32_t));
-		uint32_t a = *(uint32_t *) field_a;
-		uint32_t b = *(uint32_t *) field_b;
-		return a < b ? -1 : (a > b);
-	}
-	case NUM64:
-	{
-		assert(size_a == sizeof(uint64_t));
-		uint64_t a = *(uint64_t *) field_a;
-		uint64_t b;
-		/* Allow search in NUM64 indexes using NUM keys. */
-		if (size_b == sizeof(uint32_t)) {
-			b = *(uint32_t *) field_b;
-		} else {
-			assert(size_b == sizeof(uint64_t));
-			b = *(uint64_t *) field_b;
-		}
-		return a < b ? -1 : (a > b);
-	}
-	case STRING:
-	{
-		int cmp = memcmp(field_a, field_b, MIN(size_a, size_b));
-		if (cmp != 0)
-			return cmp;
-
-		if (size_a > size_b) {
-			return 1;
-		} else if (size_a < size_b){
-			return -1;
-		} else {
-			return 0;
-		}
-	}
-	default:
-		assert(false);
+	if (type != STRING) {
+		assert(field_a[0] == field_b[0]);
+		/*
+		 * Little-endian unsigned int is memcmp
+		 * compatible.
+		 */
+		return memcmp(field_a + 1, field_b + 1, field_a[0]);
 	}
+	uint32_t size_a = load_varint32(&field_a);
+	uint32_t size_b = load_varint32(&field_b);
+	int r = memcmp(field_a, field_b, MIN(size_a, size_b));
+	if (r == 0)
+		r = size_a < size_b ? -1 : size_a > size_b;
+	return r;
 }
 
 int
 tuple_compare(const struct tuple *tuple_a, const struct tuple *tuple_b,
 	      const struct key_def *key_def)
 {
-	for (uint32_t part = 0; part < key_def->part_count; part++) {
-		uint32_t field_no = key_def->parts[part].fieldno;
-		uint32_t size_a, size_b;
-		const char *field_a = tuple_field(tuple_a, field_no, &size_a);
-		const char *field_b = tuple_field(tuple_b, field_no, &size_b);
-
-		int r = tuple_compare_field(field_a, size_a, field_b, size_b,
-					    key_def->parts[part].type);
-		if (r != 0) {
-			return r;
-		}
+	if (key_def->part_count == 1 && key_def->parts[0].fieldno == 0)
+		return tuple_compare_field(tuple_a->data, tuple_b->data,
+					   key_def->parts[0].type);
+
+	struct key_part *part = key_def->parts;
+	struct key_part *end = part + key_def->part_count;
+	struct tuple_format *format_a = tuple_format(tuple_a);
+	struct tuple_format *format_b = tuple_format(tuple_b);
+	const char *field_a;
+	const char *field_b;
+	int r;
+
+	for (; part < end; part++) {
+		field_a = tuple_field_old(format_a, tuple_a, part->fieldno);
+		field_b = tuple_field_old(format_b, tuple_b, part->fieldno);
+		if ((r = tuple_compare_field(field_a, field_b, part->type)))
+			break;
 	}
-
-	return 0;
+	return r;
 }
 
 int
@@ -319,33 +473,67 @@ tuple_compare_dup(const struct tuple *tuple_a, const struct tuple *tuple_b,
 		  const struct key_def *key_def)
 {
 	int r = tuple_compare(tuple_a, tuple_b, key_def);
-	if (r != 0) {
-		return r;
-	}
+	if (r == 0)
+		r = tuple_a < tuple_b ? -1 : tuple_a > tuple_b;
 
-	return tuple_a < tuple_b ? -1 : (tuple_a > tuple_b);
+	return r;
 }
 
 int
-tuple_compare_with_key(const struct tuple *tuple_a, const char *key,
+tuple_compare_with_key(const struct tuple *tuple, const char *key,
 		       uint32_t part_count, const struct key_def *key_def)
 {
-	part_count = MIN(part_count, key_def->part_count);
-	for (uint32_t part = 0; part < part_count; part++) {
-		uint32_t field_no = key_def->parts[part].fieldno;
-
-		uint32_t size_a;
-		const char *field_a = tuple_field(tuple_a, field_no, &size_a);
-
-		uint32_t key_size = load_varint32(&key);
-		int r = tuple_compare_field(field_a, size_a, key, key_size,
-					    key_def->parts[part].type);
-		if (r != 0) {
-			return r;
+	struct key_part *part = key_def->parts;
+	struct key_part *end = part + MIN(part_count, key_def->part_count);
+	struct tuple_format *format = tuple_format(tuple);
+	const char *field;
+	uint32_t field_size;
+	uint32_t key_size;
+	int r = 0; /* Part count can be 0 in wildcard searches. */
+	for (; part < end; part++, key += key_size) {
+		field = tuple_field_old(format, tuple, part->fieldno);
+		field_size = load_varint32(&field);
+		key_size = load_varint32(&key);
+		switch (part->type) {
+		case NUM:
+			r = memcmp(field, key, sizeof(uint32_t));
+			break;
+		case NUM64:
+			if (key_size == sizeof(uint32_t)) {
+				/*
+				 * Allow search in NUM64 indexes
+				 * using NUM keys.
+				 */
+				uint64_t b = *(uint32_t *) key;
+				r = memcmp(field, &b, sizeof(uint64_t));
+			} else {
+				r = memcmp(field, key, sizeof(uint64_t));
+			}
+			break;
+		default:
+			r = memcmp(field, key, MIN(field_size, key_size));
+			if (r == 0)
+				r = field_size < key_size ? -1 : field_size > key_size;
+			break;
 		}
-
-		key += key_size;
+		if (r != 0)
+			break;
 	}
+	return r;
+}
 
-	return 0;
+void
+tuple_init()
+{
+	tuple_format_ber = tuple_format_new(NULL, 0);
+}
+
+void
+tuple_free()
+{
+	for (struct tuple_format **format = tuple_formats;
+	     format < tuple_formats + formats_size;
+	     format++)
+		free(*format);
+	free(tuple_formats);
 }
diff --git a/src/box/tuple.h b/src/box/tuple.h
index 401b21a26f67ca318e194091b456de5d474e2445..8ba49b1946c8ac3317b3c7cac1dc0eb343e4cc13 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -29,10 +29,85 @@
  * SUCH DAMAGE.
  */
 #include "tarantool/util.h"
+#include "key_def.h"
 #include <pickle.h>
 
 struct tbuf;
-struct key_def;
+
+/**
+ * @brief In-memory tuple format
+ */
+struct tuple_format {
+	uint16_t id;
+	/**
+	 * Max field no which participates in any of the space
+	 * indexes. Each tuple of this format must have,
+	 * therefore, at least max_fieldno fields.
+	 *
+	 */
+	uint32_t max_fieldno;
+	/* Length of 'types' and 'offset' arrays. */
+	uint32_t field_count;
+	/**
+	 * Field types of indexed fields. This is an array of size
+	 * field_count. If there are gaps, i.e. fields that do not
+	 * participate in any index and thus we cannot infer their
+	 * type, then respective array members have value UNKNOWN.
+	 */
+	enum field_type *types;
+	/**
+	 * Each tuple has an area with field offsets. This area
+	 * is located in front of the tuple. It is used to quickly
+	 * find field start inside tuple data. This area only
+	 * stores offsets of fields preceded with fields of
+	 * dynamic length. If preceding fields have a fixed
+	 * length, field offset can be calculated once for all
+	 * tuples and thus is stored directly in the format object.
+	 * The variable below stores the size of field map in the
+	 * tuple, *in bytes*.
+	 */
+	uint32_t field_map_size;
+	/**
+	 * For each field participating in an index, the format
+	 * may either store the fixed offset of the field
+	 * (identical in all tuples with this format), or an
+	 * offset in the dynamic offset map (field_map), which,
+	 * in turn, stores the offset of the field (such offset is
+	 * varying between different tuples of the same format).
+	 * If an offset is fixed, it's positive, so that
+	 * tuple->data[format->offset[fieldno] gives the
+	 * start of the field.
+	 * If it is varying, it's negative, so that
+	 * tuple->data[((uint32_t *) * tuple)[format->offset[fieldno]]]
+	 * gives the start of the field.
+	 */
+	int32_t offset[0];
+};
+
+extern struct tuple_format **tuple_formats;
+/**
+ * Default format for a tuple which does not belong
+ * to any space and is stored in memory.
+ */
+extern struct tuple_format *tuple_format_ber;
+
+
+static inline uint32_t
+tuple_format_id(struct tuple_format *format)
+{
+	assert(tuple_formats[format->id] == format);
+	return format->id;
+}
+
+/**
+ * @brief Allocate, construct and register a new in-memory tuple
+ *	 format.
+ * @param space description
+ *
+ * @return tuple format
+ */
+struct tuple_format *
+tuple_format_new(struct key_def *key_def, uint32_t key_count);
 
 /**
  * An atom of Tarantool/Box storage. Consists of a list of fields.
@@ -42,8 +117,8 @@ struct tuple
 {
 	/** reference counter */
 	uint16_t refs;
-	/* see enum tuple_flags */
-	uint16_t flags;
+	/** format identifier */
+	uint16_t format_id;
 	/** length of the variable part of the tuple */
 	uint32_t bsize;
 	/** number of fields in the variable part. */
@@ -62,7 +137,7 @@ struct tuple
  * @post tuple->refs = 1
  */
 struct tuple *
-tuple_alloc(size_t size);
+tuple_alloc(struct tuple_format *format, size_t size);
 
 /**
  * Create a new tuple from a sequence of BER-len encoded fields.
@@ -73,7 +148,8 @@ tuple_alloc(size_t size);
  * Throws an exception if tuple format is incorrect.
  */
 struct tuple *
-tuple_new(uint32_t field_count, const char **data, const char *end);
+tuple_new(struct tuple_format *format, uint32_t field_count,
+	  const char **data, const char *end);
 
 /**
  * Change tuple reference counter. If it has reached zero, free the tuple.
@@ -83,24 +159,46 @@ tuple_new(uint32_t field_count, const char **data, const char *end);
 void
 tuple_ref(struct tuple *tuple, int count);
 
+/**
+* @brief Return a tuple format instance
+* @param tuple tuple
+* @return tuple format instance
+*/
+static inline struct tuple_format *
+tuple_format(const struct tuple *tuple)
+{
+	struct tuple_format *format = tuple_formats[tuple->format_id];
+	assert(tuple_format_id(format) == tuple->format_id);
+	return format;
+}
+
 /**
  * Get a field from tuple by index.
+ * Returns a pointer to BER-length prefixed field.
  *
- * @returns field data if the field exists, or NULL
  */
 const char *
-tuple_field_old(struct tuple *tuple, uint32_t i);
+tuple_field_old(const struct tuple_format *format,
+		const struct tuple *tuple, uint32_t i);
 
 /**
  * @brief Return field data of the field
  * @param tuple tuple
  * @param field_no field number
- * @param field pointer where the start of field data will be stored
+ * @param field pointer where the start of field data will be stored,
+ *        or NULL if field is out of range
  * @param len pointer where the len of the field will be stored
- * @throws IllegalParams if \a field_no is out of range
  */
-const char *
-tuple_field(const struct tuple *tuple, uint32_t field_no, uint32_t *len);
+static inline const char *
+tuple_field(const struct tuple *tuple, uint32_t i, uint32_t *len)
+{
+	const char *field = tuple_field_old(tuple_format(tuple), tuple, i);
+	if (field < tuple->data + tuple->bsize) {
+		*len = load_varint32(&field);
+		return field;
+	}
+	return NULL;
+}
 
 /**
  * @brief Tuple Interator
@@ -166,8 +264,10 @@ void
 tuple_print(struct tbuf *buf, const struct tuple *tuple);
 
 struct tuple *
-tuple_update(const struct tuple *old_tuple, const char *expr,
-             const char *expr_end);
+tuple_update(struct tuple_format *new_format,
+	     void *(*region_alloc)(void *, size_t), void *alloc_ctx,
+	     const struct tuple *old_tuple,
+	     const char *expr, const char *expr_end);
 
 /** Tuple length when adding to iov. */
 static inline size_t tuple_len(struct tuple *tuple)
@@ -240,5 +340,12 @@ tuple_to_obuf(struct tuple *tuple, struct obuf *buf);
 void
 tuple_to_luabuf(struct tuple *tuple, struct luaL_Buffer *b);
 
+/** Initialize tuple library */
+void
+tuple_init();
+
+/** Cleanup tuple library */
+void
+tuple_free();
 #endif /* TARANTOOL_BOX_TUPLE_H_INCLUDED */
 
diff --git a/src/lua/init.cc b/src/lua/init.cc
index 6892af4b491e76bb23c406f38d5b899c100f08b8..78e82997f961b20d5f59eb77d560f9326381acea 100644
--- a/src/lua/init.cc
+++ b/src/lua/init.cc
@@ -1381,7 +1381,9 @@ tarantool_lua_dofile(struct lua_State *L, const char *filename)
 {
 	lua_getglobal(L, "dofile");
 	lua_pushstring(L, filename);
-	return lua_pcall(L, 1, 1, 0);
+	lbox_pcall(L);
+	bool result = lua_toboolean(L, 1);
+	return result ? 0 : 1;
 }
 
 void
@@ -1503,6 +1505,9 @@ load_init_script(va_list ap)
 		/* Execute the init file. */
 		if (tarantool_lua_dofile(L, path))
 			panic("%s", lua_tostring(L, -1));
+
+		/* clear the stack from return values. */
+		lua_settop(L, 0);
 	}
 	/*
 	 * The file doesn't exist. It's OK, tarantool may
@@ -1555,3 +1560,10 @@ tarantool_lua_load_init_script(struct lua_State *L)
 	*/
 	tarantool_lua_sandbox(tarantool_L);
 }
+
+void *
+lua_region_alloc(void *ctx, size_t size)
+{
+	struct lua_State *L = (struct lua_State *) ctx;
+	return lua_newuserdata(L, size);
+}
diff --git a/src/memcached.cc b/src/memcached.cc
index 5494b5862b87f3790199249b82fada07641c0c6e..7e99cf54a610db095008801dab29bae87d7b5a1d 100644
--- a/src/memcached.cc
+++ b/src/memcached.cc
@@ -501,7 +501,6 @@ memcached_space_init()
         if (cfg.memcached_port == 0)
                 return;
 
-
 	/* Configure memcached index key. */
 	struct key_def *key_def = (struct key_def *) malloc(sizeof(struct key_def));
 	key_def->part_count = 1;
@@ -511,21 +510,13 @@ memcached_space_init()
 	key_def->parts = (struct key_part *) malloc(sizeof(struct key_part));
 	key_def->cmp_order = (uint32_t *) malloc(sizeof(uint32_t));
 
-	if (key_def->parts == NULL || key_def->cmp_order == NULL)
-		panic("out of memory when configuring memcached_space");
-
 	key_def->parts[0].fieldno = 0;
 	key_def->parts[0].type = STRING;
 
 	key_def->max_fieldno = 1;
 	key_def->cmp_order[0] = 0;
 
-
-	struct space *memc_s =
-		space_create(cfg.memcached_space, key_def, 1, 4);
-
-	Index *memc_index = Index::factory(HASH, key_def, memc_s);
-	space_set_index(memc_s, 0, memc_index);
+	(void) space_new(cfg.memcached_space, key_def, 1, 4);
 }
 
 /** Delete a bunch of expired keys. */
diff --git a/src/tarantool.cc b/src/tarantool.cc
index 22c8766928cd01244c674fa691ae51b7ded2d1d8..f467159a42a997c69d27282cb045a2948def4908 100644
--- a/src/tarantool.cc
+++ b/src/tarantool.cc
@@ -84,7 +84,7 @@ struct tarantool_cfg cfg;
 static ev_signal *sigs = NULL;
 
 int snapshot_pid = 0; /* snapshot processes pid */
-bool init_storage, booting = true;
+bool booting = true;
 extern const void *opt_def;
 
 static int
@@ -578,7 +578,7 @@ background()
 }
 
 void
-tarantool_free(void)
+tarantool_lua_free()
 {
 	/*
 	 * Got to be done prior to anything else, since GC
@@ -586,7 +586,13 @@ tarantool_free(void)
 	 */
 	if (tarantool_L)
 		tarantool_lua_close(tarantool_L);
+	tarantool_L = NULL;
+}
+
 
+void
+tarantool_free(void)
+{
 	recovery_free();
 	stat_free();
 
@@ -622,6 +628,13 @@ initialize_minimal()
 	coeio_init();
 }
 
+/** Callback of snapshot_save() when doing --init-storage */
+void
+init_storage(struct log_io * /* l */, struct fio_batch * /* batch */)
+{
+	/* Nothing. */
+}
+
 int
 main(int argc, char **argv)
 {
@@ -790,11 +803,10 @@ main(int argc, char **argv)
 	}
 
 	if (gopt(opt, 'I')) {
-		init_storage = true;
 		initialize_minimal();
-		box_init();
+		box_init(true);
 		set_lsn(recovery_state, 1);
-		snapshot_save(recovery_state, box_snapshot);
+		snapshot_save(recovery_state, init_storage);
 		exit(EXIT_SUCCESS);
 	}
 
@@ -838,7 +850,8 @@ main(int argc, char **argv)
 
 	try {
 		tarantool_L = tarantool_lua_init();
-		box_init();
+		box_init(false);
+		atexit(tarantool_lua_free);
 		memcached_init(cfg.bind_ipaddr, cfg.memcached_port);
 		tarantool_lua_load_cfg(tarantool_L, &cfg);
 		/*
diff --git a/test/box/lua_misc.result b/test/box/lua_misc.result
index d931d1f6be428a68e8d13331a967c9874d199d82..e4a93593306388d349eb4c4dd1fdc46491e72695 100644
--- a/test/box/lua_misc.result
+++ b/test/box/lua_misc.result
@@ -105,25 +105,27 @@ box.error.ER_NONMASTER: 258
 box.error.ER_PROC_RET: 12290
 box.error.ER_TUPLE_IS_TOO_LONG: 11010
 box.error.ER_EXACT_MATCH: 11522
-box.error.ER_SECONDARY: 770
+box.error.ER_FIELD_TYPE: 10242
+box.error.ER_PROC_LUA: 13058
+box.error.ER_TUPLE_FOUND: 14082
 box.error.ER_OK: 0
-box.error.ER_NO_SUCH_INDEX: 13570
+box.error.ER_NO_SUCH_FIELD: 13826
 box.error.ER_TUPLE_NOT_FOUND: 12546
 box.error.ER_FIBER_STACK: 6658
-box.error.ER_UNKNOWN_UPDATE_OP: 11266
-box.error.ER_TUPLE_FOUND: 14082
+box.error.ER_SPLICE: 10754
+box.error.ER_NO_SUCH_INDEX: 13570
 box.error.ER_UNSUPPORTED: 2562
 box.error.ER_INJECTION: 2306
-box.error.ER_NO_SUCH_FIELD: 13826
-box.error.ER_TUPLE_IS_RO: 1025
+box.error.ER_SPACE_DISABLED: 13314
+box.error.ER_INDEX_TYPE: 1282
 box.error.ER_ARG_TYPE: 10498
 box.error.ER_NO_SUCH_SPACE: 14594
-box.error.ER_SPACE_DISABLED: 13314
-box.error.ER_PROC_LUA: 13058
+box.error.ER_UNKNOWN_UPDATE_OP: 11266
+box.error.ER_SPACE_EXISTS: 1538
 box.error.ER_NO_SUCH_PROC: 12802
-box.error.ER_SPLICE: 10754
-box.error.ER_KEY_PART_COUNT: 12034
 box.error.ER_WAL_IO: 9986
-box.error.ER_FIELD_TYPE: 10242
+box.error.ER_KEY_PART_COUNT: 12034
+box.error.ER_TUPLE_IS_RO: 1025
+box.error.ER_SECONDARY: 770
 box.error.ER_MEMORY_ISSUE: 1793
 ...
diff --git a/test/lib/server.py b/test/lib/server.py
index 8f292b14cb33b00a38aadc3ae36d96b467f1d8ca..20be099eb85ac228423d67a608e09cb501057f00 100644
--- a/test/lib/server.py
+++ b/test/lib/server.py
@@ -226,7 +226,9 @@ class Server(object):
             return
 
         # kill process
-        os.kill(self.read_pidfile(), signal.SIGTERM)
+        pid = self.read_pidfile();
+        if pid != -1:
+            os.kill(pid, signal.SIGTERM)
         #self.process.kill(signal.SIGTERM)
         if self.gdb or self.valgrind:
             self.process.expect(pexpect.EOF, timeout = 1 << 30)
diff --git a/test/lib/sql_ast.py b/test/lib/sql_ast.py
index 6bf6e9f59938b4976efa0c2bca1ac35df8afc305..26b6d07e435dc3bd1bb1be0e508603902c5b3755 100644
--- a/test/lib/sql_ast.py
+++ b/test/lib/sql_ast.py
@@ -30,8 +30,8 @@ ER = {
     2: "ER_ILLEGAL_PARAMS"      ,
     3: "ER_SECONDARY"           ,
     4: "ER_TUPLE_IS_RO"         ,
-    5: "ER_UNUSED5"             ,
-    6: "ER_UNUSED6"             ,
+    5: "ER_INDEX_TYPE"          ,
+    6: "ER_SPACE_EXISTS"        ,
     7: "ER_MEMORY_ISSUE"        ,
     8: "ER_UNUSED8"             ,
     9: "ER_INJECTION"           ,
diff --git a/test/replication/memcached.test b/test/replication/memcached.test
index 0152df2e3cd20200b7f89813956dfe3cd8c5e15f..f7c3d45fbffbbb231d6fd78430a0143348434096 100644
--- a/test/replication/memcached.test
+++ b/test/replication/memcached.test
@@ -22,39 +22,21 @@ replica.deploy("replication/cfg/replica.cfg",
 replica_memcached = replica.memcached
 
 ###################################
-def wait_for_lsn(lsn, serv):
-    serv_admin = serv.admin
-    while True:
-        if get_lsn(serv) == lsn:
-            return lsn
-        time.sleep(0.01)
-
-def wait_for_next_lsn(lsn, serv):
-    serv_admin = serv.admin
-    while True:
-        if get_lsn(serv) != lsn:
-            return lsn
-        time.sleep(0.01)
-
 def get_lsn(serv):
     serv_admin = serv.admin
     resp = exec serv_admin silent "lua box.info.lsn"
     return yaml.load(resp)[0]
 
-def wait(next = False, serv_master = master, serv_replica = replica):
-    if next:
-        lsn = get_lsn(serv_replica)
-        return wait_for_next_lsn(lsn, serv_replica)
-    else:
-        lsn = get_lsn(serv_master)
-        return wait_for_lsn(lsn, serv_replica)
+def wait(serv_master = master, serv_replica = replica):
+    lsn = get_lsn(serv_master)
+    serv_replica.wait_lsn(lsn)
+    return lsn
 
 def get_memcached_len(serv):
     serv_admin = serv.admin
     resp = exec serv_admin silent "lua box.space[box.cfg.memcached_space]:len()"
     return yaml.load(resp)[0]
 
-
 def wait_for_empty_space(serv):
     serv_admin = serv.admin
     while True:
@@ -102,20 +84,21 @@ exec replica_memcached "get 10\r\n"
 
 print """# check that expiration is working properly on replica"""
 exec master_memcached silent "set 1 0 1 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0])
-wait()
+lsn = wait()
 exec replica_memcached "get 1\r\n"
-wait(True)
+replica.wait_lsn(lsn + 1)
 exec replica_memcached "get 1\r\n"
 
 print """# check that expiration is working properly, when replica becomes master"""
 exec master_memcached silent "set 1 0 1 %d\r\n%s\r\n" % (len(sonet[0]), sonet[0])
+lsn = wait()
 replica.reconfigure("replication/cfg/replica_to_master.cfg")
 exec replica_memcached "get 1\r\n"
-wait(True)
+replica.wait_lsn(lsn + 1)
 exec replica_memcached "get 1\r\n"
 
 
-# resore default suite config
+# restore default suite config
 replica.stop()
 replica.cleanup(True)
 master.stop()
diff --git a/test/unit/base64.c b/test/unit/base64.c
index dca11da881db7b082e8e25aa444514984cf65bdc..89077f69283252e159273eaa4cb8ff9ac63e1b99 100644
--- a/test/unit/base64.c
+++ b/test/unit/base64.c
@@ -9,7 +9,7 @@ base64_test(const char *str)
 	header();
 
 	int len = strlen(str);
-	int base64_buflen = len * 4/3 + 4;
+	int base64_buflen = base64_bufsize(len);
 	char *base64_buf = malloc(base64_buflen);
 	char *strbuf = malloc(len + 1);