diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95b116a224216533503f5652b12a6453c9f5f59b..9829bbdf0393fd398ef1584d8a8c7194624c2946 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -300,6 +300,7 @@ add_dependencies(build_bundled_libs misc)
 
 include(BuildSophia)
 sophia_build()
+set (sophia_lib "${PROJECT_BINARY_DIR}/third_party/sophia/db/libsophia.a")
 
 option(ENABLE_RPM "Enable install of a RPM specific files" OFF)
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index be7eb5ba1dcd1ededc9a9fc97614d68a407876d2..83c0aef69da56967490b50c19a29e58d00382f3e 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -152,7 +152,11 @@ set(TARANTOOL_CXX_FLAGS ${CMAKE_CXX_FLAGS} PARENT_SCOPE)
 
 add_executable(tarantool tarantool.cc)
 add_dependencies(tarantool build_bundled_libs)
-target_link_libraries(tarantool box ${common_libraries} -rdynamic)
+target_link_libraries(tarantool
+    box
+    ${sophia_lib}
+    ${common_libraries}
+    -rdynamic)
 
 if (module_link_flags)
     set_target_properties(tarantool PROPERTIES
diff --git a/src/box/CMakeLists.txt b/src/box/CMakeLists.txt
index beea2c89dd5bd085e846421e1e26ccb9ef4f4fe1..600f848a2bc544640eee935a9a8ea2325f653719 100644
--- a/src/box/CMakeLists.txt
+++ b/src/box/CMakeLists.txt
@@ -4,6 +4,8 @@ endif()
 
 file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/src/box/lua)
 
+include_directories(${SOPHIA_INCLUDE_DIR})
+
 set(lua_sources)
 lua_source(lua_sources lua/schema.lua)
 lua_source(lua_sources lua/box.lua)
@@ -27,6 +29,8 @@ add_library(box
     bitset_index.cc
     engine.cc
     engine_memtx.cc
+    engine_sophia.cc
+    sophia_index.cc
     space.cc
     alter.cc
     schema.cc
@@ -42,4 +46,3 @@ add_library(box
     lua/slab.cc
     lua/index.cc
     lua/space.cc)
-
diff --git a/src/box/alter.cc b/src/box/alter.cc
index f19e311d4c450c31e43845953da6d069074758b3..aec77035a1e5d7d81ecda2b3d535f7646ddc24fb 100644
--- a/src/box/alter.cc
+++ b/src/box/alter.cc
@@ -596,11 +596,7 @@ DropIndex::commit(struct alter_space *alter)
 	Index *pk = index_find(alter->old_space, 0);
 	if (pk == NULL)
 		return;
-	struct iterator *it = pk->position();
-	pk->initIterator(it, ITER_ALL, NULL, 0);
-	struct tuple *tuple;
-	while ((tuple = it->next(it)))
-		tuple_ref(tuple, -1);
+	alter->old_space->engine->factory->dropIndex(pk);
 }
 
 /** Change non-essential (no data change) properties of an index. */
diff --git a/src/box/box.cc b/src/box/box.cc
index 4952ee201d197be84b27c543544d76352f8741a4..ae93421c29fe0eb0e76484e2101c0c6904fea6e8 100644
--- a/src/box/box.cc
+++ b/src/box/box.cc
@@ -46,6 +46,7 @@
 #include "schema.h"
 #include "engine.h"
 #include "engine_memtx.h"
+#include "engine_sophia.h"
 #include "space.h"
 #include "port.h"
 #include "request.h"
@@ -315,6 +316,10 @@ engine_init()
 {
 	MemtxFactory *memtx = new MemtxFactory();
 	engine_register(memtx);
+
+	SophiaFactory *sophia = new SophiaFactory();
+	sophia->init();
+	engine_register(sophia);
 }
 
 void
diff --git a/src/box/engine.cc b/src/box/engine.cc
index 578e5ca00164c559171399dba09d1d136537629d..8efcc17516af982092311fe2c3456bc6b2b20d90 100644
--- a/src/box/engine.cc
+++ b/src/box/engine.cc
@@ -46,6 +46,12 @@ void EngineFactory::init()
 void EngineFactory::shutdown()
 {}
 
+void EngineFactory::txnFinish(struct txn*)
+{}
+
+void EngineFactory::recoveryEvent(enum engine_recovery_event)
+{}
+
 Engine::Engine(EngineFactory *f)
 	:factory(f)
 {
diff --git a/src/box/engine.h b/src/box/engine.h
index fcb2462fd79e1181f1f9d06b60a5277e892fdb1e..21c586c41994540fc13e3123b4a1f28d9280b368 100644
--- a/src/box/engine.h
+++ b/src/box/engine.h
@@ -58,6 +58,15 @@ enum engine_recovery_state {
 	READY_ALL_KEYS
 };
 
+/**
+ * Engine specific recovery events that represents
+ * global recovery stage change.
+ */
+enum engine_recovery_event {
+	END_RECOVERY_SNAPSHOT,
+	END_RECOVERY
+};
+
 typedef void (*engine_recover_f)(struct space*);
 
 typedef struct tuple *
@@ -83,7 +92,7 @@ class EngineFactory: public Object {
 	virtual ~EngineFactory() {}
 	/** Called once at startup. */
 	virtual void init();
-	/** Called at server shutdown */
+	/** Called at server shutdown. */
 	virtual void shutdown();
 	/** Create a new engine instance for a space. */
 	virtual Engine *open() = 0;
@@ -92,6 +101,19 @@ class EngineFactory: public Object {
 	 * space.
 	 */
 	virtual Index *createIndex(struct key_def*) = 0;
+	/*
+	 * Delete all tuples in the index on drop.
+	 */
+	virtual void dropIndex(Index*) = 0;
+	/**
+	 * Check a key definition for violation of
+	 * various limits.
+	 */
+	virtual void keydefCheck(struct key_def *key_def) = 0;
+	/* Clean transaction engine resources.  */
+	virtual void txnFinish(struct txn *txn);
+	/* Inform engine about a recovery stage change. */
+	virtual void recoveryEvent(enum engine_recovery_event);
 public:
 	/** Name of the engine. */
 	const char *name;
@@ -100,7 +122,7 @@ class EngineFactory: public Object {
 	struct rlist link;
 };
 
-/** Engine handle  - an operator of a space */
+/** Engine handle - an operator of a space */
 
 struct Engine: public Object {
 public:
diff --git a/src/box/engine_memtx.cc b/src/box/engine_memtx.cc
index f99d06b0a20ef0c86d202e1fa3af53e3cbee2a10..cb8fab1c7d87b54c46117546c8029133787ae275 100644
--- a/src/box/engine_memtx.cc
+++ b/src/box/engine_memtx.cc
@@ -46,7 +46,7 @@ struct Memtx: public Engine {
 	virtual ~Memtx()
 	{
 		/* do nothing */
-		/* engine->close(this); */
+		/* factory->close(this); */
 	}
 };
 
@@ -62,6 +62,7 @@ struct Memtx: public Engine {
  * 2) when all XLOGs are loaded:
  *    recover = space_build_all_keys
 */
+
 static inline void
 memtx_recovery_prepare(struct engine_recovery *r)
 {
@@ -76,6 +77,19 @@ MemtxFactory::MemtxFactory()
 	memtx_recovery_prepare(&recovery);
 }
 
+void
+MemtxFactory::recoveryEvent(enum engine_recovery_event event)
+{
+	switch (event) {
+	case END_RECOVERY_SNAPSHOT:
+		recovery.recover = space_build_primary_key;
+		break;
+	case END_RECOVERY:
+		recovery.recover = space_build_all_keys;
+		break;
+	}
+}
+
 Engine *MemtxFactory::open()
 {
 	return new Memtx(this);
@@ -96,3 +110,50 @@ MemtxFactory::createIndex(struct key_def *key_def)
 		return NULL;
 	}
 }
+
+void
+MemtxFactory::dropIndex(Index *index)
+{
+	struct iterator *it = index->position();
+	index->initIterator(it, ITER_ALL, NULL, 0);
+	struct tuple *tuple;
+	while ((tuple = it->next(it)))
+		tuple_ref(tuple, -1);
+}
+
+void
+MemtxFactory::keydefCheck(struct key_def *key_def)
+{
+	switch (key_def->type) {
+	case HASH:
+		if (! key_def->is_unique) {
+			tnt_raise(ClientError, ER_MODIFY_INDEX,
+				  (unsigned) key_def->iid,
+				  (unsigned) key_def->space_id,
+				  "HASH index must be unique");
+		}
+		break;
+	case TREE:
+		/* TREE index has no limitations. */
+		break;
+	case BITSET:
+		if (key_def->part_count != 1) {
+			tnt_raise(ClientError, ER_MODIFY_INDEX,
+				  (unsigned) key_def->iid,
+				  (unsigned) key_def->space_id,
+				  "BITSET index key can not be multipart");
+		}
+		if (key_def->is_unique) {
+			tnt_raise(ClientError, ER_MODIFY_INDEX,
+				  (unsigned) key_def->iid,
+				  (unsigned) key_def->space_id,
+				  "BITSET can not be unique");
+		}
+		break;
+	default:
+		tnt_raise(ClientError, ER_INDEX_TYPE,
+			  (unsigned) key_def->iid,
+			  (unsigned) key_def->space_id);
+		break;
+	}
+}
diff --git a/src/box/engine_memtx.h b/src/box/engine_memtx.h
index 14b8871fc2e361ce34882119af795e9cac80daad..e3ea0c0fa41094536414a4f445c453eeac4e2dfe 100644
--- a/src/box/engine_memtx.h
+++ b/src/box/engine_memtx.h
@@ -33,6 +33,9 @@ struct MemtxFactory: public EngineFactory {
 	MemtxFactory();
 	virtual Engine *open();
 	virtual Index *createIndex(struct key_def *key_def);
+	virtual void dropIndex(Index *index);
+	virtual void keydefCheck(struct key_def *key_def);
+	virtual void recoveryEvent(enum engine_recovery_event event);
 };
 
 #endif /* TARANTOOL_BOX_ENGINE_MEMTX_H_INCLUDED */
diff --git a/src/box/engine_sophia.cc b/src/box/engine_sophia.cc
new file mode 100644
index 0000000000000000000000000000000000000000..3a06c76c0582600a10716d9c40cd508cd6cfa791
--- /dev/null
+++ b/src/box/engine_sophia.cc
@@ -0,0 +1,180 @@
+/*
+ * 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 "txn.h"
+#include "tuple.h"
+#include "engine.h"
+#include "engine_sophia.h"
+#include "index.h"
+#include "sophia_index.h"
+#include "space.h"
+#include "exception.h"
+#include "salad/rlist.h"
+#include <sophia.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <errno.h>
+
+struct Sophia: public Engine {
+	Sophia(EngineFactory*);
+};
+
+Sophia::Sophia(EngineFactory *e)
+	:Engine(e)
+{ }
+
+static struct tuple *
+sophia_replace_noop(struct space*,
+                    struct tuple*, struct tuple*,
+                    enum dup_replace_mode)
+{
+	return NULL;
+}
+
+static void
+sophia_end_build_primary_key(struct space *space)
+{
+	engine_recovery *r = &space->engine->recovery;
+	/* enable replace */
+	r->state = READY_ALL_KEYS;
+	r->replace = space_replace_primary_key;
+	r->recover = space_noop;
+}
+
+static void
+sophia_begin_build_primary_key(struct space *space)
+{
+	engine_recovery *r = &space->engine->recovery;
+	r->replace = sophia_replace_noop;
+	r->recover = sophia_end_build_primary_key;
+}
+
+static inline void
+sophia_recovery_prepare(struct engine_recovery *r)
+{
+	r->state   = READY_NO_KEYS;
+	r->recover = sophia_begin_build_primary_key;
+	r->replace = space_replace_no_keys;
+}
+
+SophiaFactory::SophiaFactory()
+	:EngineFactory("sophia")
+{
+	sophia_recovery_prepare(&recovery);
+}
+
+void
+SophiaFactory::init()
+{
+	int rc = mkdir("sophia", 0755);
+
+	if (rc == -1 && errno != EEXIST) {
+		say_error("failed to create directory: 'sophia', %d, %s",
+		          errno, strerror(errno));
+	}
+}
+
+Engine*
+SophiaFactory::open()
+{
+	return new Sophia(this);
+}
+
+void
+SophiaFactory::recoveryEvent(enum engine_recovery_event event)
+{
+	switch (event) {
+	case END_RECOVERY_SNAPSHOT:
+		recovery.replace = sophia_replace_noop;
+		break;
+	case END_RECOVERY:
+		recovery.state   = READY_NO_KEYS;
+		recovery.replace = space_replace_primary_key;
+		recovery.recover = space_noop;
+		break;
+	}
+}
+
+Index*
+SophiaFactory::createIndex(struct key_def *key_def)
+{
+	switch (key_def->type) {
+	case TREE: return new SophiaIndex(key_def);
+	default:
+		assert(false);
+		return NULL;
+	}
+}
+
+void
+SophiaFactory::dropIndex(Index *index)
+{
+	(void)index;
+}
+
+void
+SophiaFactory::keydefCheck(struct key_def *key_def)
+{
+	switch (key_def->type) {
+	case TREE:
+		if (! key_def->is_unique) {
+			tnt_raise(ClientError, ER_MODIFY_INDEX,
+				  (unsigned) key_def->iid,
+				  (unsigned) key_def->space_id,
+				  "Sophia TREE index must be unique");
+		}
+		if (key_def->iid != 0) {
+			tnt_raise(ClientError, ER_MODIFY_INDEX,
+				  (unsigned) key_def->iid,
+				  (unsigned) key_def->space_id,
+				  "Sophia TREE secondary indexes are not supported");
+		}
+		if (key_def->part_count != 1) {
+			tnt_raise(ClientError, ER_MODIFY_INDEX,
+				  (unsigned) key_def->iid,
+				  (unsigned) key_def->space_id,
+				  "Sophia TREE index key can not be multipart");
+		}
+		break;
+	default:
+		tnt_raise(ClientError, ER_INDEX_TYPE,
+			  (unsigned) key_def->iid,
+			  (unsigned) key_def->space_id);
+		break;
+	}
+}
+
+void
+SophiaFactory::txnFinish(struct txn *txn)
+{
+	if (txn->new_tuple)
+		tuple_ref(txn->new_tuple, -1);
+}
diff --git a/src/box/engine_sophia.h b/src/box/engine_sophia.h
new file mode 100644
index 0000000000000000000000000000000000000000..c4320ada4517af084c519315d18786347262d9e0
--- /dev/null
+++ b/src/box/engine_sophia.h
@@ -0,0 +1,43 @@
+#ifndef TARANTOOL_BOX_ENGINE_SOPHIA_H_INCLUDED
+#define TARANTOOL_BOX_ENGINE_SOPHIA_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 SophiaFactory: public EngineFactory {
+	SophiaFactory();
+	virtual void init();
+	virtual Engine *open();
+	virtual Index *createIndex(struct key_def *key_def);
+	virtual void dropIndex(Index *index);
+	virtual void keydefCheck(struct key_def *key_def);
+	virtual void txnFinish(struct txn *txn);
+	virtual void recoveryEvent(enum engine_recovery_event event);
+};
+
+#endif /* TARANTOOL_BOX_ENGINE_SOPHIA_H_INCLUDED */
diff --git a/src/box/index.cc b/src/box/index.cc
index 4860452c3ea492598dd5419c8e77ba9a3d040cfa..13f33bc962b97ffa001b0de7ce8b2f0bf648a039 100644
--- a/src/box/index.cc
+++ b/src/box/index.cc
@@ -27,6 +27,11 @@
  * SUCH DAMAGE.
  */
 #include "index.h"
+#include "hash_index.h"
+#include "tree_index.h"
+#include "bitset_index.h"
+#include "sophia_index.h"
+#include "tuple.h"
 #include "say.h"
 #include "exception.h"
 
diff --git a/src/box/index.h b/src/box/index.h
index 862caa578178aa0248626f1ca0c3c3fef520d8c2..6adb06236138fd1ba60e85e684329b81bfb26f32 100644
--- a/src/box/index.h
+++ b/src/box/index.h
@@ -83,8 +83,15 @@ iterator_type_is_reverse(enum iterator_type type)
 struct iterator {
 	struct tuple *(*next)(struct iterator *);
 	void (*free)(struct iterator *);
+	void (*close)(struct iterator *);
 };
 
+static inline void
+iterator_close(struct iterator *it) {
+	if (it->close)
+		it->close(it);
+}
+
 /**
  * Check that the key has correct part count and correct part size
  * for use in an index iterator.
@@ -142,6 +149,11 @@ class Index: public Object {
 	 */
 	Index(struct key_def *key_def);
 
+	/*
+	 * Pre-allocated iterator to speed up the main case of
+	 * box_process(). Should not be used elsewhere.
+	 */
+	struct iterator *m_position;
 public:
 	virtual ~Index();
 
@@ -180,12 +192,6 @@ class Index: public Object {
 			m_position = allocIterator();
 		return m_position;
 	}
-private:
-	/*
-	 * Pre-allocated iterator to speed up the main case of
-	 * box_process(). Should not be used elsewhere.
-	 */
-	struct iterator *m_position;
 };
 
 /**
diff --git a/src/box/key_def.cc b/src/box/key_def.cc
index 57a396eb3f3b9548b3877ca09cb24097dd96cda0..78cc0c0eed2bd8eacfc807403c3e6cc35d6cb7f3 100644
--- a/src/box/key_def.cc
+++ b/src/box/key_def.cc
@@ -27,6 +27,8 @@
  * SUCH DAMAGE.
  */
 #include "key_def.h"
+#include "space.h"
+#include "schema.h"
 #include <stdlib.h>
 #include <stdio.h>
 #include "exception.h"
@@ -184,38 +186,11 @@ key_def_check(struct key_def *key_def)
 			}
 		}
 	}
-	switch (key_def->type) {
-	case HASH:
-		if (! key_def->is_unique) {
-			tnt_raise(ClientError, ER_MODIFY_INDEX,
-				  (unsigned) key_def->iid,
-				  (unsigned) key_def->space_id,
-				  "HASH index must be unique");
-		}
-		break;
-	case TREE:
-		/* TREE index has no limitations. */
-		break;
-	case BITSET:
-		if (key_def->part_count != 1) {
-			tnt_raise(ClientError, ER_MODIFY_INDEX,
-				  (unsigned) key_def->iid,
-				  (unsigned) key_def->space_id,
-				  "BITSET index key can not be multipart");
-		}
-		if (key_def->is_unique) {
-			tnt_raise(ClientError, ER_MODIFY_INDEX,
-				  (unsigned) key_def->iid,
-				  (unsigned) key_def->space_id,
-				  "BITSET can not be unique");
-		}
-		break;
-	default:
-		tnt_raise(ClientError, ER_INDEX_TYPE,
-			  (unsigned) key_def->iid,
-			  (unsigned) key_def->space_id);
-		break;
-	}
+	struct space *space =
+		space_cache_find(key_def->space_id);
+
+	/* validate key_def->type */
+	space->engine->factory->keydefCheck(key_def);
 }
 
 void
diff --git a/src/box/key_def.h b/src/box/key_def.h
index 1b45972719b9d6ebb170a428394885aab81ce482..de837c8d66e69373cdf5ffe63a21fbd3a81e0997 100644
--- a/src/box/key_def.h
+++ b/src/box/key_def.h
@@ -194,9 +194,7 @@ key_list_del_key(struct rlist *key_list, uint32_t id);
 /**
  * Check a key definition for violation of various limits.
  *
- * @param id        space id
  * @param key_def   key_def
- * @param type_str  type name (to produce a nice error)
  */
 void
 key_def_check(struct key_def *key_def);
diff --git a/src/box/lua/schema.lua b/src/box/lua/schema.lua
index a2e50ed5ee838f860cd4d81bc1297e924a659b75..00346e1cfbd606121a979316549ac17ca50bac26 100644
--- a/src/box/lua/schema.lua
+++ b/src/box/lua/schema.lua
@@ -8,6 +8,7 @@ ffi.cdef[[
     struct iterator {
         struct tuple *(*next)(struct iterator *);
         void (*free)(struct iterator *);
+        void (*close)(struct iterator *);
     };
     struct iterator *
     boxffi_index_iterator(uint32_t space_id, uint32_t index_id, int type,
diff --git a/src/box/request.cc b/src/box/request.cc
index 21bee5b6e782059e11aad5b2b97e2b6fd539afc1..b17eb91e68faa344693a3e4002832a8ac8646482 100644
--- a/src/box/request.cc
+++ b/src/box/request.cc
@@ -199,6 +199,8 @@ execute_select(struct request *request, struct txn *txn, struct port *port)
 	struct iterator *it = index->position();
 	key_validate(index->key_def, type, key, part_count);
 	index->initIterator(it, type, key, part_count);
+	auto iterator_guard =
+		make_scoped_guard([=] { iterator_close(it); });
 
 	struct tuple *tuple;
 	while ((tuple = it->next(it)) != NULL) {
diff --git a/src/box/schema.cc b/src/box/schema.cc
index fb38919620f258ee44e4126726f4760bc8adb879..e93d744b4a28d764fbbe9916eccc54db378b7a63 100644
--- a/src/box/schema.cc
+++ b/src/box/schema.cc
@@ -303,7 +303,7 @@ static inline void
 space_end_recover_snapshot_cb(EngineFactory *f, void *udate)
 {
 	(void)udate;
-	f->recovery.recover = space_build_primary_key;
+	f->recoveryEvent(END_RECOVERY_SNAPSHOT);
 }
 
 void
@@ -328,7 +328,7 @@ static inline void
 space_end_recover_cb(EngineFactory *f, void *udate)
 {
 	(void)udate;
-	f->recovery.recover = space_build_all_keys;
+	f->recoveryEvent(END_RECOVERY);
 }
 
 void
diff --git a/src/box/sophia_index.cc b/src/box/sophia_index.cc
new file mode 100644
index 0000000000000000000000000000000000000000..642e9439d105dcffb4702ed20155598e13f2cbab
--- /dev/null
+++ b/src/box/sophia_index.cc
@@ -0,0 +1,355 @@
+/*
+ * 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 "sophia_index.h"
+#include "say.h"
+#include "tuple.h"
+#include "pickle.h"
+#include "scoped_guard.h"
+#include "exception.h"
+#include "errinj.h"
+
+#include "schema.h"
+#include "space.h"
+
+#include <sophia.h>
+#include <stdio.h>
+
+static inline int
+sophia_index_compare(char *a, size_t asz __attribute__((unused)),
+                     char *b, size_t bsz __attribute__((unused)),
+                     void *arg)
+{
+	struct key_def *key_def = (struct key_def*)arg;
+
+	int rc = tuple_compare_field(a, b, key_def->parts[0].type);
+	return (rc == 0) ? 0 :
+	       ((rc > 0) ? 1 : -1);
+}
+
+static struct tuple *
+sophia_gettuple(void *db, const char *key, size_t keysize)
+{
+	size_t valuesize = 0;
+	char *value = NULL;
+	int rc = sp_get(db, key, keysize, (void**)&value, &valuesize);
+	if (rc == -1)
+		tnt_raise(ClientError, ER_SOPHIA, sp_error(db));
+	if (rc == 0)
+		return NULL;
+	auto scoped_guard = make_scoped_guard([=] { free(value); });
+	struct tuple *ret =
+		tuple_new(tuple_format_ber, value, value + valuesize);
+	tuple_ref(ret, 1);
+	return ret;
+}
+
+/* {{{ SophiaIndex */
+
+SophiaIndex::SophiaIndex(struct key_def *key_def_arg __attribute__((unused)))
+	: Index(key_def_arg)
+{
+	env = sp_env();
+	if (env == NULL)
+		tnt_raise(ClientError, ER_MEMORY_ISSUE, sizeof(void*),
+			  "SophiaIndex", "env");
+
+	auto env_freer =
+		make_scoped_guard([=] { sp_destroy(env); });
+
+	int rc = sp_ctl(env, SPCMP, sophia_index_compare, key_def);
+	if (rc == -1)
+		tnt_raise(ClientError, ER_SOPHIA, sp_error(env));
+
+	char path[PATH_MAX];
+	snprintf(path, sizeof(path), "sophia/%04d", key_def->space_id);
+	rc = sp_ctl(env, SPDIR, SPO_RDWR|SPO_CREAT, path);
+	if (rc == -1)
+		tnt_raise(ClientError, ER_SOPHIA, sp_error(env));
+
+	say_info("start sophia space '%s' recover", path);
+
+	db = sp_open(env);
+	if (db == NULL)
+		tnt_raise(ClientError, ER_SOPHIA, sp_error(env));
+
+	say_info("recover complete");
+
+	env_freer.is_active = false;
+}
+
+SophiaIndex::~SophiaIndex()
+{
+	if (m_position != NULL) {
+		m_position->free(m_position);
+		m_position = NULL;
+	}
+
+	if (db) {
+		int rc = sp_destroy(db);
+		if (rc == -1)
+			say_info("sophia space %d close error: %s", key_def->space_id,
+			         sp_error(env));
+	}
+	if (env) {
+		sp_destroy(env);
+	}
+}
+
+void
+SophiaIndex::endBuild()
+{
+}
+
+size_t
+SophiaIndex::size() const
+{
+	return 0;
+}
+
+size_t
+SophiaIndex::memsize() const
+{
+	return 0;
+}
+
+struct tuple *
+SophiaIndex::findByKey(const char *key, uint32_t part_count) const
+{
+	assert(part_count == 1);
+	assert(key_def->is_unique && part_count == key_def->part_count);
+	const char *keyptr = key;
+	mp_next(&keyptr);
+	size_t keysize = keyptr - key;
+	struct tuple *ret = sophia_gettuple(db, key, keysize);
+	return ret;
+}
+
+static inline uint32_t
+sophia_check_dup(struct key_def *key_def,
+                 struct tuple *old_tuple,
+                 struct tuple *dup_tuple, enum dup_replace_mode mode)
+{
+	if (dup_tuple == NULL) {
+		if (mode == DUP_REPLACE) {
+			/*
+			 * dup_replace_mode is DUP_REPLACE, and
+			 * a tuple with the same key is not found.
+			 */
+			return ER_TUPLE_NOT_FOUND;
+		}
+	} else { /* dup_tuple != NULL */
+
+		int equal = old_tuple != NULL &&
+			tuple_compare(dup_tuple, old_tuple, key_def) == 0;
+
+		if (!equal && (old_tuple != NULL || mode == DUP_INSERT)) {
+			/*
+			 * There is a duplicate of new_tuple,
+			 * and it's not old_tuple: we can't
+			 * possibly delete more than one tuple
+			 * at once.
+			 */
+			return ER_TUPLE_FOUND;
+		}
+	}
+	return 0;
+}
+
+struct tuple *
+SophiaIndex::replace(struct tuple *old_tuple, struct tuple *new_tuple,
+		   enum dup_replace_mode mode)
+{
+	if (new_tuple) {
+		assert(new_tuple->refs == 0);
+
+		const char *key = tuple_field(new_tuple, key_def->parts[0].fieldno);
+		const char *keyptr = key;
+		mp_next(&keyptr);
+		size_t keysize = keyptr - key;
+
+		struct tuple *dup_tuple = sophia_gettuple(db, key, keysize);
+
+		uint32_t errcode =
+			sophia_check_dup(key_def, old_tuple, dup_tuple, mode);
+		if (errcode) {
+			if (dup_tuple)
+				tuple_ref(dup_tuple, -1);
+			tnt_raise(ClientError, errcode, index_id(this));
+		}
+
+		int rc = sp_set(db, key, keysize, new_tuple->data, new_tuple->bsize);
+		if (rc == -1) {
+			if (dup_tuple)
+				tuple_ref(dup_tuple, -1);
+			tnt_raise(ClientError, ER_SOPHIA, sp_error(db));
+		}
+
+		if (dup_tuple)
+			return dup_tuple;
+	}
+
+	if (old_tuple) {
+		/* delete */
+		const char *key = tuple_field(old_tuple, key_def->parts[0].fieldno);
+		const char *keyptr = key;
+		mp_next(&keyptr);
+		size_t keysize = keyptr - key;
+		int rc = sp_delete(db, key, keysize);
+		if (rc == -1)
+			tnt_raise(ClientError, ER_SOPHIA, sp_error(db));
+	}
+
+	return old_tuple;
+}
+
+struct sophia_iterator {
+	struct iterator base;
+	const char *key;
+	int keysize;
+	uint32_t part_count;
+	void *db;
+	void *cursor;
+};
+
+void
+sophia_iterator_free(struct iterator *ptr)
+{
+	assert(ptr->free == sophia_iterator_free);
+	struct sophia_iterator *it = (struct sophia_iterator *) ptr;
+	if (it->cursor)
+		sp_destroy(it->cursor);
+	free(ptr);
+}
+
+void
+sophia_iterator_close(struct iterator *ptr)
+{
+	assert(ptr->free == sophia_iterator_free);
+	struct sophia_iterator *it = (struct sophia_iterator *) ptr;
+	if (it->cursor) {
+		sp_destroy(it->cursor);
+		it->cursor = NULL;
+	}
+}
+
+struct tuple *
+sophia_iterator_next(struct iterator *ptr)
+{
+	assert(ptr->next == sophia_iterator_next);
+	struct sophia_iterator *it = (struct sophia_iterator *) ptr;
+	assert(it->cursor != NULL);
+	int rc = sp_fetch(it->cursor);
+	if (rc == 0)
+		return NULL;
+	size_t valuesize = sp_valuesize(it->cursor);
+	const char *value = sp_value(it->cursor);
+	struct tuple *ret =
+		tuple_new(tuple_format_ber, value, value + valuesize);
+	tuple_ref(ret, 1);
+	return ret;
+}
+
+struct tuple *
+sophia_iterator_last(struct iterator *ptr __attribute__((unused)))
+{
+	return NULL;
+}
+
+struct tuple *
+sophia_iterator_eq(struct iterator *ptr)
+{
+	ptr->next = sophia_iterator_last;
+	struct sophia_iterator *it = (struct sophia_iterator *) ptr;
+	assert(it->cursor == NULL);
+	return sophia_gettuple(it->db, it->key, it->keysize);
+}
+
+struct iterator *
+SophiaIndex::allocIterator() const
+{
+	struct sophia_iterator *it =
+		(struct sophia_iterator *) calloc(1, sizeof(*it));
+	if (it == NULL) {
+		tnt_raise(ClientError, ER_MEMORY_ISSUE,
+		          sizeof(struct sophia_iterator), "SophiaIndex",
+		          "iterator");
+	}
+	it->base.next  = sophia_iterator_next;
+	it->base.close = sophia_iterator_close;
+	it->base.free  = sophia_iterator_free;
+	it->cursor = NULL;
+	return (struct iterator *) it;
+}
+
+void
+SophiaIndex::initIterator(struct iterator *ptr, enum iterator_type type,
+		const char *key, uint32_t part_count) const
+{
+	assert(part_count <= 1);
+
+	struct sophia_iterator *it = (struct sophia_iterator *) ptr;
+	assert(it->cursor == NULL);
+
+	size_t keysize;
+	if (part_count > 0) {
+		const char *keyptr = key;
+		mp_next(&keyptr);
+		keysize = keyptr - key;
+	} else {
+		keysize = 0;
+		key = NULL;
+	}
+	it->key = key;
+	it->keysize = keysize;
+	it->part_count = part_count;
+	it->db = db;
+
+	sporder compare;
+	switch (type) {
+	case ITER_EQ: it->base.next = sophia_iterator_eq;
+		return;
+	case ITER_ALL:
+	case ITER_GE: compare = SPGTE;
+		break;
+	case ITER_GT: compare = SPGT;
+		break;
+	case ITER_LE: compare = SPLTE;
+		break;
+	case ITER_LT: compare = SPLT;
+		break;
+	default:
+		tnt_raise(ClientError, ER_UNSUPPORTED,
+		          "SophiaIndex", "requested iterator type");
+	}
+	it->cursor = sp_cursor(db, compare, key, keysize);
+	if (it->cursor == NULL)
+		tnt_raise(ClientError, ER_SOPHIA, sp_error(db));
+}
+
+/* }}} */
diff --git a/src/box/sophia_index.h b/src/box/sophia_index.h
new file mode 100644
index 0000000000000000000000000000000000000000..547f5ef455ebe548fa0a0f2c3e8343ebdca321cb
--- /dev/null
+++ b/src/box/sophia_index.h
@@ -0,0 +1,58 @@
+#ifndef TARANTOOL_BOX_SOPHIA_INDEX_H_INCLUDED
+#define TARANTOOL_BOX_SOPHIA_INDEX_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 "index.h"
+
+class SophiaIndex: public Index {
+public:
+	SophiaIndex(struct key_def *key_def);
+	~SophiaIndex();
+
+	virtual size_t size() const;
+	virtual void endBuild();
+
+	virtual struct tuple *findByKey(const char *key, uint32_t part_count) const;
+	virtual struct tuple *replace(struct tuple *old_tuple,
+				      struct tuple *new_tuple,
+				      enum dup_replace_mode mode);
+
+	virtual struct iterator *allocIterator() const;
+	virtual void initIterator(struct iterator *iterator,
+				  enum iterator_type type,
+				  const char *key, uint32_t part_count) const;
+	virtual size_t memsize() const;
+
+protected:
+	void *env;
+	void *db;
+};
+
+#endif /* TARANTOOL_BOX_SOPHIA_INDEX_H_INCLUDED */
diff --git a/src/box/space.h b/src/box/space.h
index 2ea00cfa4c5bfa5ca2b09f4b199abfd8951eeff5..7999a4b06205fec7fd00651e7d4aec1e35e93984 100644
--- a/src/box/space.h
+++ b/src/box/space.h
@@ -189,10 +189,14 @@ space_replace(struct space *space, struct tuple *old_tuple,
 struct tuple *
 space_replace_no_keys(struct space*, struct tuple*, struct tuple*,
                       enum dup_replace_mode);
+struct tuple *
+space_replace_primary_key(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);
+void space_noop(struct space *space);
 
 uint32_t
 space_size(struct space *space);
diff --git a/src/box/tuple.cc b/src/box/tuple.cc
index c509a545a0ec1aee392128070af68699d2fb8ff4..bcb1eb0ff3171c80514a5924e222da861b1e592a 100644
--- a/src/box/tuple.cc
+++ b/src/box/tuple.cc
@@ -428,7 +428,7 @@ tuple_new(struct tuple_format *format, const char *data, const char *end)
 inline __attribute__((always_inline)) int
 mp_compare_uint(const char **data_a, const char **data_b);
 
-static inline int
+int
 tuple_compare_field(const char *field_a, const char *field_b,
 		    enum field_type type)
 {
diff --git a/src/box/tuple.h b/src/box/tuple.h
index 0f94a4c0a1053cc956cf2e9e66b887dbe23a8bab..e766bd668f82ef52283e9f997e16efd0499c28f0 100644
--- a/src/box/tuple.h
+++ b/src/box/tuple.h
@@ -391,6 +391,19 @@ tuple_update(struct tuple_format *new_format,
 	     const struct tuple *old_tuple,
 	     const char *expr, const char *expr_end);
 
+/**
+ * @brief Compare two tuple fields using using field type definition
+ * @param field_a field
+ * @param field_b field
+ * @param field_type field type definition
+ * @retval 0  if field_a == field_b
+ * @retval <0 if field_a < field_b
+ * @retval >0 if field_a > field_b
+ */
+int
+tuple_compare_field(const char *field_a, const char *field_b,
+		    enum field_type type);
+
 /**
  * @brief Compare two tuples using field by field using key definition
  * @param tuple_a tuple
diff --git a/src/box/txn.cc b/src/box/txn.cc
index 622a6550606f618524c35ea1e7d951f44e6b52c7..62831885d7e9593511f99738afe0375ff8da6887 100644
--- a/src/box/txn.cc
+++ b/src/box/txn.cc
@@ -130,6 +130,8 @@ txn_finish(struct txn *txn)
 {
 	if (txn->old_tuple)
 		tuple_ref(txn->old_tuple, -1);
+	if (txn->space)
+		txn->space->engine->factory->txnFinish(txn);
 	TRASH(txn);
 }
 
diff --git a/src/box/txn.h b/src/box/txn.h
index e2e22bbb4d29eaafaadeb53133883029db723c05..c67d6ad687dde949a47532613aa9b8df02567fa4 100644
--- a/src/box/txn.h
+++ b/src/box/txn.h
@@ -56,4 +56,5 @@ void txn_replace(struct txn *txn, struct space *space,
 		 struct tuple *old_tuple, struct tuple *new_tuple,
 		 enum dup_replace_mode mode);
 void txn_add_redo(struct txn *txn, struct request *request);
+
 #endif /* TARANTOOL_BOX_TXN_H_INCLUDED */
diff --git a/src/errcode.h b/src/errcode.h
index 41476d99f71be24b1cf0fadc37ed805c2d3d19be..586bb944a02de0872bd07424a31ab22e37fd0809 100644
--- a/src/errcode.h
+++ b/src/errcode.h
@@ -107,8 +107,9 @@ enum { TNT_ERRMSG_MAX = 512 };
 	/* 55 */_(ER_SPACE_ACCESS_DENIED,	2, "%s access denied for user '%s' to space '%s'") \
 	/* 56 */_(ER_USER_MAX,			2, "A limit on the total number of users has been reached: %u") \
 	/* 57 */_(ER_NO_SUCH_ENGINE,		2, "Space engine '%s' does not exist") \
-	/* 57 */_(ER_RELOAD_CFG,		2, "Can't set option '%s' dynamically") \
-	/* 57 */_(ER_CFG,			2, "Incorrect option value: %s") \
+	/* 58 */_(ER_RELOAD_CFG,		2, "Can't set option '%s' dynamically") \
+	/* 59 */_(ER_CFG,			2, "Incorrect option value: %s") \
+	/* 60 */_(ER_SOPHIA,			2, "%s") \
 
 
 /*
diff --git a/test/big/lua.result b/test/big/lua.result
index c84bab07982137e1eeb78874d1692c702301ebaf..3b334151f611623824dbc6301d077b6ce7bcfe5e 100644
--- a/test/big/lua.result
+++ b/test/big/lua.result
@@ -472,7 +472,7 @@ t = {}
 ...
 index:pairs('sid_t', { iterator = 'wrong_iterator_type' })
 ---
-- error: '[string "-- schema.lua (internal file)..."]:324: Wrong iterator type: wrong_iterator_type'
+- error: '[string "-- schema.lua (internal file)..."]:325: Wrong iterator type: wrong_iterator_type'
 ...
 index = nil
 ---
diff --git a/test/box/misc.result b/test/box/misc.result
index c67e1939055ab2f237077e577bb2672908c11636..a6c0f62361a4bd710eb261e607e3f9d3ee525da8 100644
--- a/test/box/misc.result
+++ b/test/box/misc.result
@@ -196,6 +196,7 @@ t;
   - 'box.error.ER_NO_SUCH_INDEX : 35'
   - 'box.error.ER_TUPLE_FOUND : 3'
   - 'box.error.ER_CREATE_SPACE : 9'
+  - 'box.error.ER_PROC_RET : 21'
   - 'box.error.ER_TUPLE_FORMAT_LIMIT : 16'
   - 'box.error.ER_FIELD_TYPE : 23'
   - 'box.error.ER_CFG : 59'
@@ -247,7 +248,7 @@ t;
   - 'box.error.ER_NO_SUCH_PROC : 33'
   - 'box.error.ER_SPACE_EXISTS : 10'
   - 'box.error.ER_PROC_LUA : 32'
-  - 'box.error.ER_PROC_RET : 21'
+  - 'box.error.ER_SOPHIA : 60'
   - 'box.error.ER_NO_SUCH_TRIGGER : 34'
   - 'box.error.ER_TUPLE_IS_TOO_LONG : 27'
   - 'box.error.ER_SPLICE : 25'
diff --git a/test/box/sophia.result b/test/box/sophia.result
new file mode 100644
index 0000000000000000000000000000000000000000..7cae8cef6e6fc90c5528fb2ef51db96264e78d82
--- /dev/null
+++ b/test/box/sophia.result
@@ -0,0 +1,109 @@
+space = box.schema.create_space('tweedledum', { id = 123, engine = 'sophia' })
+---
+...
+space:create_index('primary', { type = 'tree', parts = {0, 'num'} })
+---
+...
+for v=1, 10 do space:insert({v}) end
+---
+...
+t = space.index[0]:select({}, {iterator = box.index.ALL})
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+t = space.index[0]:select({}, {iterator = box.index.GE})
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+t = space.index[0]:select(4, {iterator = box.index.GE})
+---
+...
+t
+---
+- - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+t = space.index[0]:select({}, {iterator = box.index.LE})
+---
+...
+t
+---
+- - [10]
+  - [9]
+  - [8]
+  - [7]
+  - [6]
+  - [5]
+  - [4]
+  - [3]
+  - [2]
+  - [1]
+...
+t = space.index[0]:select(7, {iterator = box.index.LE})
+---
+...
+t
+---
+- - [7]
+  - [6]
+  - [5]
+  - [4]
+  - [3]
+  - [2]
+  - [1]
+...
+t = {}
+---
+...
+for v=1, 10 do table.insert(t, space:get({v})) end
+---
+...
+t
+---
+- - [1]
+  - [2]
+  - [3]
+  - [4]
+  - [5]
+  - [6]
+  - [7]
+  - [8]
+  - [9]
+  - [10]
+...
+space:drop()
+---
+...
+os.execute("rm -rf sophia")
+---
+- 0
+...
diff --git a/test/box/sophia.test.lua b/test/box/sophia.test.lua
new file mode 100644
index 0000000000000000000000000000000000000000..759fe750abd7e9d79163701fadeb2c02606a263f
--- /dev/null
+++ b/test/box/sophia.test.lua
@@ -0,0 +1,27 @@
+
+space = box.schema.create_space('tweedledum', { id = 123, engine = 'sophia' })
+space:create_index('primary', { type = 'tree', parts = {0, 'num'} })
+
+for v=1, 10 do space:insert({v}) end
+
+t = space.index[0]:select({}, {iterator = box.index.ALL})
+t
+
+t = space.index[0]:select({}, {iterator = box.index.GE})
+t
+
+t = space.index[0]:select(4, {iterator = box.index.GE})
+t
+
+t = space.index[0]:select({}, {iterator = box.index.LE})
+t
+
+t = space.index[0]:select(7, {iterator = box.index.LE})
+t
+
+t = {}
+for v=1, 10 do table.insert(t, space:get({v})) end
+t
+
+space:drop()
+os.execute("rm -rf sophia")
diff --git a/test/module/suite.ini b/test/module/suite.ini
index 0dede8fba9a77d53803c4ed063efcad5a29fc4d9..fba1bc22dc6b612d5b9dc675c36c3639258c8edb 100644
--- a/test/module/suite.ini
+++ b/test/module/suite.ini
@@ -1,3 +1,4 @@
 [default]
 script = box.lua
+disabled = sophia.test.lua
 description = tarantool/box, optional lua modules