diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index 980678e2f7cb40ed45900db3095211b43a5551f1..a37523dceaf7190a287ccd3bc967eaf7f281c6b3 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -25,6 +25,8 @@ tarantool_module("box"
     hash_index.cc
     tree_index.cc
     bitset_index.cc
+    engine.cc
+    engine_memtx.cc
     space.cc
     alter.cc
     schema.cc
diff --git a/src/box/alter.cc b/src/box/alter.cc
index 74bb31ff5dc9d398156c3ef9240d21cb67cab31b..f19e311d4c450c31e43845953da6d069074758b3 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -442,7 +442,9 @@ alter_space_do(struct txn *txn, struct alter_space *alter,
 	 * since engine.recover does different things depending on
 	 * the recovery phase.
 	 */
-	alter->new_space->engine = alter->old_space->engine;
+	alter->new_space->engine->recovery =
+		alter->old_space->engine->recovery;
+
 	memcpy(alter->new_space->access, alter->old_space->access,
 	       sizeof(alter->old_space->access));
 	/*
@@ -492,9 +494,12 @@ ModifySpace::prepare(struct alter_space *alter)
 			  (unsigned) space_id(alter->old_space),
 			  "can not change space engine");
 
+	engine_recovery *recovery =
+		&alter->old_space->engine->recovery;
+
 	if (def.arity != 0 &&
 	    def.arity != alter->old_space->def.arity &&
-	    alter->old_space->engine.state != READY_NO_KEYS &&
+	    recovery->state != READY_NO_KEYS &&
 	    space_size(alter->old_space) > 0) {
 
 		tnt_raise(ClientError, ER_ALTER_SPACE,
@@ -502,7 +507,7 @@ ModifySpace::prepare(struct alter_space *alter)
 			  "can not change arity on a non-empty space");
 	}
 	if (def.temporary != alter->old_space->def.temporary &&
-	    alter->old_space->engine.state != READY_NO_KEYS &&
+	    recovery->state != READY_NO_KEYS &&
 	    space_size(alter->old_space) > 0) {
 		tnt_raise(ClientError, ER_ALTER_SPACE,
 			  (unsigned) space_id(alter->old_space),
@@ -576,7 +581,7 @@ DropIndex::alter(struct alter_space *alter)
 	 *   can be put back online properly with
 	 *   engine_no_keys.recover.
 	 */
-	alter->new_space->engine = engine_no_keys;
+	alter->new_space->engine->initRecovery();
 }
 
 void
@@ -765,7 +770,10 @@ AddIndex::alter(struct alter_space *alter)
 	 * READY_NO_KEYS is when a space has no functional keys.
 	 * Possible both during and after recovery.
 	 */
-	if (alter->new_space->engine.state == READY_NO_KEYS) {
+	engine_recovery *recovery =
+		&alter->new_space->engine->recovery;
+
+	if (recovery->state == READY_NO_KEYS) {
 		if (new_key_def->iid == 0) {
 			/*
 			 * Adding a primary key: bring the space
@@ -777,7 +785,7 @@ AddIndex::alter(struct alter_space *alter)
 			 * key. After recovery, it means building
 			 * all keys.
 			 */
-			alter->new_space->engine.recover(alter->new_space);
+			recovery->recover(alter->new_space);
 		} else {
 			/*
 			 * Adding a secondary key: nothing to do.
@@ -796,8 +804,9 @@ AddIndex::alter(struct alter_space *alter)
 	}
 	Index *pk = index_find(alter->old_space, 0);
 	Index *new_index = index_find(alter->new_space, new_key_def->iid);
+
 	/* READY_PRIMARY_KEY is a state that only occurs during WAL recovery. */
-	if (alter->new_space->engine.state == READY_PRIMARY_KEY) {
+	if (recovery->state == READY_PRIMARY_KEY) {
 		if (new_key_def->iid == 0) {
 			/*
 			 * Bulk rebuild of the new primary key
diff --git a/src/box/box.cc b/src/box/box.cc
index 9bf639e61363396a7c486a79e759d95f418501f8..fc376105d77e30c278f5ae23eeb4ec1673150e11 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -46,6 +46,8 @@ extern "C" {
 #include "tuple.h"
 #include "lua/call.h"
 #include "schema.h"
+#include "engine.h"
+#include "engine_memtx.h"
 #include "space.h"
 #include "port.h"
 #include "request.h"
@@ -273,9 +275,17 @@ box_free(void)
 	schema_free();
 	tuple_free();
 	recovery_free();
+	engine_shutdown();
 	stat_free();
 }
 
+static void
+box_engine_init()
+{
+	MemtxFactory *memtx = new MemtxFactory();
+	engine_register(memtx);
+}
+
 void
 box_init()
 {
@@ -284,6 +294,9 @@ box_init()
 
 	tuple_init(cfg.slab_alloc_arena, cfg.slab_alloc_minimal,
 		   cfg.slab_alloc_factor);
+
+	box_engine_init();
+
 	schema_init();
 	user_cache_init();
 
diff --git a/src/box/engine.cc b/src/box/engine.cc
new file mode 100644
index 0000000000000000000000000000000000000000..578e5ca00164c559171399dba09d1d136537629d
--- /dev/null
+++ b/src/box/engine.cc
@@ -0,0 +1,91 @@
+/*
+ * 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 "engine.h"
+#include "space.h"
+#include "exception.h"
+#include "salad/rlist.h"
+#include <stdlib.h>
+#include <string.h>
+
+static RLIST_HEAD(engines);
+
+EngineFactory::EngineFactory(const char *engine_name)
+	:name(engine_name),
+	 link(RLIST_INITIALIZER(link))
+{}
+
+void EngineFactory::init()
+{}
+
+void EngineFactory::shutdown()
+{}
+
+Engine::Engine(EngineFactory *f)
+	:factory(f)
+{
+	/* derive recovery state from engine factory */
+	initRecovery();
+}
+
+/** Register engine factory instance. */
+void engine_register(EngineFactory *engine)
+{
+	rlist_add_entry(&engines, engine, link);
+}
+
+/** Find factory engine by name. */
+EngineFactory *
+engine_find(const char *name)
+{
+	EngineFactory *e;
+	rlist_foreach_entry(e, &engines, link) {
+		if (strcmp(e->name, name) == 0)
+			return e;
+	}
+	tnt_raise(LoggedError, ER_NO_SUCH_ENGINE, name);
+}
+
+/** Call a visitor function on every registered engine. */
+void engine_foreach(void (*func)(EngineFactory *engine, void *udata),
+                    void *udata)
+{
+	EngineFactory *e;
+	rlist_foreach_entry(e, &engines, link)
+		func(e, udata);
+}
+
+/** Shutdown all engine factories. */
+void engine_shutdown()
+{
+	EngineFactory *e, *tmp;
+	rlist_foreach_entry_safe(e, &engines, link, tmp) {
+		e->shutdown();
+		delete e;
+	}
+}
diff --git a/src/box/engine.h b/src/box/engine.h
new file mode 100644
index 0000000000000000000000000000000000000000..fcb2462fd79e1181f1f9d06b60a5277e892fdb1e
--- /dev/null
+++ b/src/box/engine.h
@@ -0,0 +1,143 @@
+#ifndef TARANTOOL_BOX_ENGINE_H_INCLUDED
+#define TARANTOOL_BOX_ENGINE_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 <exception.h>
+#include "index.h"
+
+struct space;
+struct tuple;
+
+/** Reflects what space_replace() is supposed to do. */
+enum engine_recovery_state {
+	/**
+	 * The space is created, but has no data
+	 * and no primary key, or, if there is a primary
+	 * key, it's not ready for use (being built with
+	 * buildNext()).
+	 * Replace is always an error, since there are no
+	 * indexes to add data to.
+	 */
+	READY_NO_KEYS,
+	/**
+	 * The space has a functional primary key.
+	 * Replace adds the tuple to this key.
+	 */
+	READY_PRIMARY_KEY,
+	/**
+	 * The space is fully functional, all keys
+	 * are fully built, replace adds its tuple
+	 * to all keys.
+	 */
+	READY_ALL_KEYS
+};
+
+typedef void (*engine_recover_f)(struct space*);
+
+typedef struct tuple *
+(*engine_replace_f)(struct space *, struct tuple *, struct tuple *,
+                    enum dup_replace_mode);
+
+struct engine_recovery {
+	enum engine_recovery_state state;
+	/* Recover is called after each recover step to enable
+	 * keys. When recovery is complete, it enables all keys
+	 * at once and resets itself to a no-op.
+	 */
+	engine_recover_f recover;
+	engine_replace_f replace;
+};
+
+class Engine;
+
+/** Engine instance */
+class EngineFactory: public Object {
+public:
+	EngineFactory(const char *engine_name);
+	virtual ~EngineFactory() {}
+	/** Called once at startup. */
+	virtual void init();
+	/** Called at server shutdown */
+	virtual void shutdown();
+	/** Create a new engine instance for a space. */
+	virtual Engine *open() = 0;
+	/**
+	 * Create an instance of space index. Used in alter
+	 * space.
+	 */
+	virtual Index *createIndex(struct key_def*) = 0;
+public:
+	/** Name of the engine. */
+	const char *name;
+	struct engine_recovery recovery;
+	/** Used for search for engine by name. */
+	struct rlist link;
+};
+
+/** Engine handle  - an operator of a space */
+
+struct Engine: public Object {
+public:
+	Engine(EngineFactory *f);
+	virtual ~Engine() {}
+
+	inline struct tuple*
+	replace(struct space *space,
+	        struct tuple *old_tuple,
+	        struct tuple *new_tuple, enum dup_replace_mode mode)
+	{
+		return recovery.replace(space, old_tuple, new_tuple, mode);
+	}
+
+	inline void recover(struct space *space) {
+		recovery.recover(space);
+	}
+
+	inline void initRecovery() {
+		recovery = factory->recovery;
+	}
+
+	engine_recovery recovery;
+	EngineFactory *factory;
+};
+
+/** Register engine factory instance. */
+void engine_register(EngineFactory *engine);
+
+/** Call a visitor function on every registered engine. */
+void engine_foreach(void (*func)(EngineFactory *engine, void *udata),
+                    void *udata);
+
+/** Find engine factory by name. */
+EngineFactory *engine_find(const char *name);
+
+/** Shutdown all engine factories. */
+void engine_shutdown();
+
+#endif /* TARANTOOL_BOX_ENGINE_H_INCLUDED */
diff --git a/src/box/engine_memtx.cc b/src/box/engine_memtx.cc
new file mode 100644
index 0000000000000000000000000000000000000000..f99d06b0a20ef0c86d202e1fa3af53e3cbee2a10
--- /dev/null
+++ b/src/box/engine_memtx.cc
@@ -0,0 +1,98 @@
+/*
+ * 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 "tuple.h"
+#include "engine.h"
+#include "engine_memtx.h"
+#include "index.h"
+#include "hash_index.h"
+#include "tree_index.h"
+#include "bitset_index.h"
+#include "space.h"
+#include "exception.h"
+#include "salad/rlist.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct Memtx: public Engine {
+	Memtx(EngineFactory *e)
+		: Engine(e)
+	{ }
+	virtual ~Memtx()
+	{
+		/* do nothing */
+		/* engine->close(this); */
+	}
+};
+
+/**
+ * This is a vtab with which a newly created space which has no
+ * keys is primed.
+ * At first it is set to correctly work for spaces created during
+ * recovery from snapshot. In process of recovery it is updated as
+ * below:
+ *
+ * 1) after SNAP is loaded:
+ *    recover = space_build_primary_key
+ * 2) when all XLOGs are loaded:
+ *    recover = space_build_all_keys
+*/
+static inline void
+memtx_recovery_prepare(struct engine_recovery *r)
+{
+	r->state   = READY_NO_KEYS;
+	r->recover = space_begin_build_primary_key;
+	r->replace = space_replace_no_keys;
+}
+
+MemtxFactory::MemtxFactory()
+	:EngineFactory("memtx")
+{
+	memtx_recovery_prepare(&recovery);
+}
+
+Engine *MemtxFactory::open()
+{
+	return new Memtx(this);
+}
+
+Index *
+MemtxFactory::createIndex(struct key_def *key_def)
+{
+	switch (key_def->type) {
+	case HASH:
+		return new HashIndex(key_def);
+	case TREE:
+		return new TreeIndex(key_def);
+	case BITSET:
+		return new BitsetIndex(key_def);
+	default:
+		assert(false);
+		return NULL;
+	}
+}
diff --git a/src/box/engine_memtx.h b/src/box/engine_memtx.h
new file mode 100644
index 0000000000000000000000000000000000000000..14b8871fc2e361ce34882119af795e9cac80daad
--- /dev/null
+++ b/src/box/engine_memtx.h
@@ -0,0 +1,38 @@
+#ifndef TARANTOOL_BOX_ENGINE_MEMTX_H_INCLUDED
+#define TARANTOOL_BOX_ENGINE_MEMTX_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.
+ */
+
+struct MemtxFactory: public EngineFactory {
+	MemtxFactory();
+	virtual Engine *open();
+	virtual Index *createIndex(struct key_def *key_def);
+};
+
+#endif /* TARANTOOL_BOX_ENGINE_MEMTX_H_INCLUDED */
diff --git a/src/box/index.cc b/src/box/index.cc
index ecd52fde3c66e376d0f1c407141eb501dd4fc1b0..4860452c3ea492598dd5419c8e77ba9a3d040cfa 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -27,13 +27,8 @@
  * SUCH DAMAGE.
  */
 #include "index.h"
-#include "hash_index.h"
-#include "tree_index.h"
-#include "bitset_index.h"
-#include "tuple.h"
 #include "say.h"
 #include "exception.h"
-#include <new>
 
 STRS(iterator_type, ITERATOR_TYPE);
 
@@ -100,22 +95,6 @@ primary_key_validate(struct key_def *key_def, const char *key,
 
 /* {{{ Index -- base class for all indexes. ********************/
 
-Index *
-Index::factory(struct key_def *key_def)
-{
-	switch (key_def->type) {
-	case HASH:
-		return new HashIndex(key_def);
-	case TREE:
-		return new TreeIndex(key_def);
-	case BITSET:
-		return new BitsetIndex(key_def);
-	default:
-		assert(false);
-		return NULL; /* silent compiler warning. */
-	}
-}
-
 Index::Index(struct key_def *key_def_arg)
 	:key_def(key_def_dup(key_def_arg)),
 	m_position(NULL)
diff --git a/src/box/index.h b/src/box/index.h
index 4262b2dfbed4bc12f1b5e6b481f363bf903f6db1..862caa578178aa0248626f1ca0c3c3fef520d8c2 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -134,13 +134,6 @@ class Index: public Object {
 	/* Description of a possibly multipart key. */
 	struct key_def *key_def;
 
-	/**
-	 * Allocate index instance.
-	 *
-	 * @param key_def  key part description
-	 */
-	static Index *factory(struct key_def *key_def);
-
 protected:
 	/**
 	 * Initialize index instance.
diff --git a/src/box/key_def.h b/src/box/key_def.h
index e9efbe3cc79f0edffc84b88e88821cf41cf2dd69..1b45972719b9d6ebb170a428394885aab81ce482 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -82,10 +82,10 @@ field_type_maxlen(enum field_type type)
 	return maxlen[type];
 }
 
-#define ENUM_INDEX_TYPE(_)                                          \
-	_(HASH,    0)       /* HASH Index  */                       \
-	_(TREE,    1)       /* TREE Index  */                       \
-	_(BITSET,  2)       /* BITSET Index  */                     \
+#define ENUM_INDEX_TYPE(_) \
+	_(HASH,    0) /* HASH Index */   \
+	_(TREE,    1) /* TREE Index */   \
+	_(BITSET,  2) /* BITSET Index */
 
 ENUM(index_type, ENUM_INDEX_TYPE);
 extern const char *index_type_strs[];
diff --git a/src/box/schema.cc b/src/box/schema.cc
index 83399b6d1b915477e1ee610766973a7d9c37c339..fb38919620f258ee44e4126726f4760bc8adb879 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -28,6 +28,7 @@
  */
 #include "schema.h"
 #include "access.h"
+#include "engine.h"
 #include "space.h"
 #include "tuple.h"
 #include "assoc.h"
@@ -164,10 +165,14 @@ space_cache_replace(struct space *space)
 static void
 do_one_recover_step(struct space *space, void * /* param */)
 {
-	if (space_index(space, 0))
-		space->engine.recover(space);
-	else
-		space->engine = engine_no_keys;
+	if (space_index(space, 0)) {
+		space->engine->recover(space);
+	} else {
+		/* in case of space has no primary index,
+		 * derive it's engine handler recovery state from
+		 * the global one. */
+		space->engine->initRecovery();
+	}
 }
 
 /** A wrapper around space_new() for data dictionary spaces. */
@@ -193,9 +198,9 @@ sc_space_new(struct space_def *space_def,
 	 *   ensures validation of tuples when starting from
 	 *   a snapshot of older version.
 	 */
-	space->engine.recover(space); /* load snapshot - begin */
-	space->engine.recover(space); /* load snapshot - end */
-	space->engine.recover(space); /* build secondary keys */
+	space->engine->recover(space); /* load snapshot - begin */
+	space->engine->recover(space); /* load snapshot - end */
+	space->engine->recover(space); /* build secondary keys */
 	return space;
 }
 
@@ -241,7 +246,7 @@ schema_init()
 	 */
 	/* _schema - key/value space with schema description */
 	struct space_def def = {
-		SC_SCHEMA_ID, ADMIN, 0, "_schema", MEMTX, false
+		SC_SCHEMA_ID, ADMIN, 0, "_schema", "memtx", false
 	};
 	struct key_def *key_def = key_def_new(def.id,
 					      0 /* index id */,
@@ -294,6 +299,13 @@ schema_init()
 	key_def_delete(key_def);
 }
 
+static inline void
+space_end_recover_snapshot_cb(EngineFactory *f, void *udate)
+{
+	(void)udate;
+	f->recovery.recover = space_build_primary_key;
+}
+
 void
 space_end_recover_snapshot()
 {
@@ -301,7 +313,8 @@ space_end_recover_snapshot()
 	 * For all new spaces created from now on, when the
 	 * PRIMARY key is added, enable it right away.
 	 */
-	engine_no_keys.recover = space_build_primary_key;
+	engine_foreach(space_end_recover_snapshot_cb, NULL);
+
 	space_foreach(do_one_recover_step, NULL);
 }
 
@@ -311,6 +324,13 @@ fix_lua(struct space *space, void * /* param */)
 	box_lua_space_new(tarantool_L, space);
 }
 
+static inline void
+space_end_recover_cb(EngineFactory *f, void *udate)
+{
+	(void)udate;
+	f->recovery.recover = space_build_all_keys;
+}
+
 void
 space_end_recover()
 {
@@ -318,8 +338,10 @@ space_end_recover()
 	 * For all new spaces created after recovery is complete,
 	 * when the primary key is added, enable all keys.
 	 */
-	engine_no_keys.recover = space_build_all_keys;
+	engine_foreach(space_end_recover_cb, NULL);
+
 	space_foreach(do_one_recover_step, NULL);
+
 	/* TODO: temporary solution for Bug#1229709 */
 	space_foreach(fix_lua, NULL);
 }
diff --git a/src/box/space.cc b/src/box/space.cc
index d438fac6e305b79bed4f17e67210eea0b00dc6c0..cef92e2ce70b9fecc665259814b3bda64958d4af 100644
--- a/src/box/space.cc
+++ b/src/box/space.cc
@@ -34,15 +34,6 @@
 #include "scoped_guard.h"
 #include "trigger.h"
 
-static struct engine*
-space_engine_find(const char *name)
-{
-	if (strcmp(name, MEMTX) == 0)
-		return &engine_no_keys;
-
-	tnt_raise(LoggedError, ER_NO_SUCH_ENGINE, name);
-}
-
 void
 space_fill_index_map(struct space *space)
 {
@@ -85,12 +76,15 @@ space_new(struct space_def *def, struct rlist *key_list)
 	space->format = tuple_format_new(key_list);
 	tuple_format_ref(space->format, 1);
 	space->index_id_max = index_id_max;
+	/* init space engine instance */
+	EngineFactory *engine = engine_find(def->engine_name);
+	space->engine = engine->open();
 	/* fill space indexes */
 	rlist_foreach_entry(key_def, key_list, link) {
-		space->index_map[key_def->iid] = Index::factory(key_def);
+		space->index_map[key_def->iid] =
+			space->engine->factory->createIndex(key_def);
 	}
 	space_fill_index_map(space);
-	space->engine = *space_engine_find(def->engine_name);
 	space->run_triggers = true;
 	scoped_guard.is_active = false;
 	return space;
@@ -103,6 +97,8 @@ space_delete(struct space *space)
 		delete space->index[j];
 	if (space->format)
 		tuple_format_ref(space->format, -1);
+	if (space->engine)
+		delete space->engine;
 
 	struct trigger *trigger, *tmp;
 	rlist_foreach_entry_safe(trigger, &space->on_replace, link, tmp) {
@@ -236,9 +232,10 @@ space_build_secondary_keys(struct space *space)
 			say_info("Space %d: done", space_id(space));
 		}
 	}
-	space->engine.state = READY_ALL_KEYS;
-	space->engine.recover = space_noop; /* mark the end of recover */
-	space->engine.replace = space_replace_all_keys;
+	engine_recovery *r = &space->engine->recovery;
+	r->state   = READY_ALL_KEYS;
+	r->recover = space_noop; /* mark the end of recover */
+	r->replace = space_replace_all_keys;
 }
 
 /** Build the primary key after loading data from a snapshot. */
@@ -246,9 +243,10 @@ void
 space_end_build_primary_key(struct space *space)
 {
 	space->index[0]->endBuild();
-	space->engine.state = READY_PRIMARY_KEY;
-	space->engine.replace = space_replace_primary_key;
-	space->engine.recover = space_build_secondary_keys;
+	engine_recovery *r = &space->engine->recovery;
+	r->state   = READY_PRIMARY_KEY;
+	r->replace = space_replace_primary_key;
+	r->recover = space_build_secondary_keys;
 }
 
 /** Prepare the primary key for bulk load (loading from
@@ -258,8 +256,9 @@ void
 space_begin_build_primary_key(struct space *space)
 {
 	space->index[0]->beginBuild();
-	space->engine.replace = space_replace_build_next;
-	space->engine.recover = space_end_build_primary_key;
+	engine_recovery *r = &space->engine->recovery;
+	r->replace = space_replace_build_next;
+	r->recover = space_end_build_primary_key;
 }
 
 /**
@@ -287,24 +286,6 @@ space_build_all_keys(struct space *space)
 	space_build_secondary_keys(space);
 }
 
-/**
- * This is a vtab with which a newly created space which has no
- * keys is primed.
- * At first it is set to correctly work for spaces created during
- * recovery from snapshot. In process of recovery it is updated as
- * below:
- *
- * 1) after SNAP is loaded:
- *    recover = space_build_primary_key
- * 2) when all XLOGs are loaded:
- *    recover = space_build_all_keys
- */
-struct engine engine_no_keys = {
-	/* .state = */   READY_NO_KEYS,
-	/* .recover = */ space_begin_build_primary_key,
-	/* .replace = */ space_replace_no_keys
-};
-
 void
 space_validate_tuple(struct space *sp, struct tuple *new_tuple)
 {
diff --git a/src/box/space.h b/src/box/space.h
index 146700e27a1cecff26524d8d53a77a3147b13a64..2ea00cfa4c5bfa5ca2b09f4b199abfd8951eeff5 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -30,55 +30,10 @@
  */
 #include "index.h"
 #include "key_def.h"
+#include "engine.h"
 #include "salad/rlist.h"
 #include <exception.h>
 
-typedef void (*space_f)(struct space *space);
-typedef struct tuple *(*space_replace_f)
-	(struct space *space, struct tuple *old_tuple,
-	 struct tuple *new_tuple, enum dup_replace_mode mode);
-
-/** Reflects what space_replace() is supposed to do. */
-enum space_state {
-	/**
-	 * The space is created, but has no data
-	 * and no primary key, or, if there is a primary
-	 * key, it's not ready for use (being built with
-	 * buildNext()).
-	 * Replace is always an error, since there are no
-	 * indexes to add data to.
-	 */
-	READY_NO_KEYS,
-	/**
-	 * The space has a functional primary key.
-	 * Replace adds the tuple to this key.
-	 */
-	READY_PRIMARY_KEY,
-	/**
-	 * The space is fully functional, all keys
-	 * are fully built, replace adds its tuple
-	 * to all keys.
-	 */
-	READY_ALL_KEYS
-};
-
-struct engine {
-	enum space_state state;
-	/* Recover is called after each recover step to enable
-	 * keys. When recovery is complete, it enables all keys
-	 * at once and resets itself to a no-op.
-	 */
-	space_f recover;
-	space_replace_f replace;
-};
-
-#define MEMTX "memtx"
-
-extern struct engine engine_no_keys;
-
-void space_build_primary_key(struct space *space);
-void space_build_all_keys(struct space *space);
-
 struct space {
 	uint8_t access[BOX_USER_MAX];
 	/**
@@ -87,7 +42,7 @@ struct space {
 	 * life cycle, throughout phases of recovery or with
 	 * deletion and addition of indexes.
 	 */
-	struct engine engine;
+	Engine *engine;
 
 	/** Triggers fired after space_replace() -- see txn_replace(). */
 	struct rlist on_replace;
@@ -228,10 +183,17 @@ static inline struct tuple *
 space_replace(struct space *space, struct tuple *old_tuple,
 	      struct tuple *new_tuple, enum dup_replace_mode mode)
 {
-	return space->engine.replace(space, old_tuple, new_tuple,
-				     mode);
+	return space->engine->replace(space, old_tuple, new_tuple, mode);
 }
 
+struct tuple *
+space_replace_no_keys(struct space*, struct tuple*, struct tuple*,
+                      enum dup_replace_mode);
+
+void space_begin_build_primary_key(struct space *space);
+void space_build_primary_key(struct space *space);
+void space_build_all_keys(struct space *space);
+
 uint32_t
 space_size(struct space *space);
 
diff --git a/src/lib/salad/rlist.h b/src/lib/salad/rlist.h
index d5226be162461cd224576134dc0ef64dde11bcc7..7952d07cf5157732f5a6dea3e26b5415e4087947 100644
--- a/src/lib/salad/rlist.h
+++ b/src/lib/salad/rlist.h
@@ -191,11 +191,16 @@ rlist_swap(struct rlist *rhs, struct rlist *lhs)
 	rhs->prev->next = rhs;
 }
 
+/**
+ * list initializer
+ */
+#define RLIST_INITIALIZER(name) { &(name), &(name) }
+
 /**
  * allocate and init head of list
  */
 #define RLIST_HEAD(name)	\
-	struct rlist name = { &(name), &(name) }
+	struct rlist name = RLIST_INITIALIZER(name)
 
 /**
  * return entry by list item