diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95b116a224216533503f5652b12a6453c9f5f59b..e72e8748e07822e3721becee39041186fc7b834f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -97,7 +97,7 @@ add_custom_target(tags COMMAND ctags -R -f tags
 #
 set (CPACK_PACKAGE_VERSION_MAJOR "1")
 set (CPACK_PACKAGE_VERSION_MINOR "6")
-set (CPACK_PACKAGE_VERSION_PATCH "1")
+set (CPACK_PACKAGE_VERSION_PATCH "2")
 
 set (PACKAGE_VERSION "")
 
@@ -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/README.md b/README.md
index 777b60f3e749c6cab2ef3b312ad82c7edf8674b8..b2bb486e05ff159f3ba6f3999c9202692e3e2a39 100644
--- a/README.md
+++ b/README.md
@@ -53,8 +53,7 @@ The build depends on the following external libraries:
 Please follow these steps to compile Tarantool:
 
     # If compiling from git
-    tarantool $ git submodule init
-    tarantool $ git submodule update
+    tarantool $ git submodule update --init
 
     tarantool $ cmake .
     tarantool $ make
diff --git a/doc/box-protocol.txt b/doc/box-protocol.txt
index 1ecc0be4a61ba34407d090b5e18e3065a0a19ce5..6ded42d535ef3b8f67851a53321627c3ebb3a0e9 100644
--- a/doc/box-protocol.txt
+++ b/doc/box-protocol.txt
@@ -4,8 +4,15 @@
 ; The latest version of this document can be found in
 ; Tarantool source tree, doc/box-protocol.txt
 ;
-; IPROTO is a binary request/response protocol that features a
-; complete access to Tarantool functionality, including:
+; IPROTO is a binary request/response protocol.
+; The server begins the dialogue by sending a fixed-size (128 bytes)
+; text greeting to the client. The first 64 bytes of the greeting
+; contain server version. The second 64 bytes contain a base64-
+; encoded random string, to use in authentification packet.
+
+; Once a greeting is read, the protocol becomes pure
+; request/response and features a complete access to Tarantool
+; functionality, including:
 ; - request multiplexing, e.g. ability to asynchronously issue
 ;   multiple requests via the same connection
 ; - response format that supports zero-copy writes
@@ -51,7 +58,7 @@
 
 ;
 ; The value of the constant defines the type of value of the map.
-; For example, for <error> key (0x31), the expected value is a 
+; For example, for <error> key (0x31), the expected value is a
 ; msgpack string with error message.
 ; All requests and responses utilize the same basic structure
 
@@ -89,13 +96,15 @@
 ; Request packet structure
 ; -----------------------------------
 ; Value for <code> key in request can be:
-; 0  -- <ping>
 ; 1  -- <select>
 ; 2  -- <insert>
 ; 3  -- <replace>
 ; 4  -- <update>
 ; 5  -- <delete>
 ; 6  -- <call>
+; 7  -- <auth>
+; 64  -- <ping>
+; 66 -- <subscribe>
 
 ; <sync> is a unique request identifier, preserved in the response,
 ; The identifier is necessary to allow request multiplexing --
@@ -130,9 +139,24 @@
 ; Call a stored function
 <call> ::= <function_name> | <tuple>
 
+; Authenticate a session
+; <key> holds the user name. <tuple> must be an array of 2 fields:
+; authentication mechanism ("chap-sha1" is the only supported
+; mechanism right now) and password, encrypted according to the
+; specified mechanism
+; https://github.com/tarantool/tarantool/blob/master/src/scramble.h
+; for instructions how to prepare a hashed password for "chap-sha1"
+; authentication mechanism.
+; Authentication in Tarantool is optional, if no
+; authentication is performed, session user is 'guest'.
+; The server responds to authentication packet with a standard
+; response with 0 tuples.
+
+<auth> ::= <key> | <tuple>
+
 ; As can be seen from the grammar some requests have common keys,
 ; whereas other keys can be present only in a body of a single
-; request type. 
+; request type.
 
 ; <space_id> space to use in the request
 ; To find the numeric space id by space name, one
@@ -201,7 +225,7 @@
 ; more tuples, otherwise it carries an error message that corresponds
 ; to the return code.
 
-; On success, the server always returns a tuple or tuples, 
+; On success, the server always returns a tuple or tuples,
 ; when found.
 ; I.e. on success, response <body> contains <set> key.
 ; For select/update/delete, it's the tuple that matched
@@ -250,5 +274,9 @@
 ; Convenience macros which define hexadecimal constants for
 ; <int32> return codes (completion status + code) can be found here:
 ; https://github.com/tarantool/tarantool/blob/master/src/errcode.h
+;
+;
+; Additional packets
+; ------------------
 
 ; vim: syntax=bnf
diff --git a/doc/user/connectors.xml b/doc/user/connectors.xml
index 8e7b9ce36ea239fe68ddaf50e082f99117dafc91..0b7069dfc917f41dd515953cc7c5673eca69103d 100644
--- a/doc/user/connectors.xml
+++ b/doc/user/connectors.xml
@@ -180,7 +180,7 @@ int main()
     <para>
        The example program only shows one command and does not show all that's necessary for
        good practice. For that, please see <link
-       xlink:href="https://github.com/tarantool/tarantool/blob/master/connector/c/include/tp.h"><filename>connector/c</filename></link> in the source tree.
+       xlink:href="https://github.com/tarantool/tarantool-c"><filename>connector/c</filename></link> source tree.
     </para>
   </section>
 
diff --git a/doc/user/plugins.xml b/doc/user/plugins.xml
index dee8c756402214d5bc275ccaeeacb0af6a571f5c..8c27b79452d77b31a4047be26ae22275bba4a305 100644
--- a/doc/user/plugins.xml
+++ b/doc/user/plugins.xml
@@ -57,7 +57,7 @@ This example assumes that MySQL 5.5 or MySQL 5.6 has been installed
 
 <para>
 The example was run on a Linux machine where the base directory
-had a copy of the Tarantool source on ~/tarantool-stable, and
+had a copy of the Tarantool source on ~/tarantool, and
 a copy of MySQL on ~/mysql-5.5. The mysqld server is already running
 on the local host 127.0.0.1.
 </para>
@@ -95,7 +95,7 @@ Bye
 # Build the Tarantool server. Make certain that "cmake" gets the right
 # paths for the MySQL include directory and the MySQL libmysqlclient
 # library which were checked earlier.
-<prompt>$ </prompt><userinput>cd ~/tarantool-stable</userinput>
+<prompt>$ </prompt><userinput>cd ~/tarantool</userinput>
 <prompt>$ </prompt><userinput>make clean</userinput>
 <prompt>$ </prompt><userinput>rm CMakeCache.txt</userinput>
 <prompt>$ </prompt><userinput>cmake . -DWITH_MYSQL=on -DMYSQL_INCLUDE_DIR=~/mysql-5.5/include\</userinput>
@@ -108,7 +108,7 @@ Bye
 ...
 -- Configuring done
 -- Generating done
--- Build files have been written to: ~/tarantool-stable
+-- Build files have been written to: ~/tarantool
 <prompt>$ </prompt><userinput>make</userinput>
 ...
 [ 49%] Built target core
@@ -137,8 +137,8 @@ Linking CXX shared library libmysql.so
 # The script_dir is the directory where the init.lua file must be.
 # Now create a file named /home/pgulutzan/tarantool_test/init.lua
 # and put the following lines in it (again, change "/home/pgulutzan"
-# to whatever the real directory is that contains tarantool-stable):
-package.path = "/home/pgulutzan/tarantool-stable/src/module/sql/?.lua;"..package.path
+# to whatever the real directory is that contains tarantool):
+package.path = "/home/pgulutzan/tarantool/src/module/sql/?.lua;"..package.path
 require("sql")
 if type(box.net.sql) ~= "table" then
 error("net.sql load failed")
@@ -149,19 +149,19 @@ require("box.net.mysql")
 <userinput>cd /home/pgulutzan/tarantool_test</userinput>
 <userinput>mkdir box</userinput>
 <userinput>mkdir box/net</userinput>
-<userinput>cp ~/tarantool-stable/src/module/mysql/mysql.so ./box/net/mysql.so</userinput>
+<userinput>cp ~/tarantool/src/module/mysql/mysql.so ./box/net/mysql.so</userinput>
 
 # Start the Tarantool server.
 # Run it in the background but let the initial display be in the foreground.
 # So it's possible to see the message that the plugin was loaded.
-<prompt>$ </prompt><userinput>~/tarantool-stable/src/box/tarantool&amp;</userinput>
+<prompt>$ </prompt><userinput>~/tarantool/src/box/tarantool&amp;</userinput>
 2013-12-03 17:46:16.239 [12957] 1/sched C&gt; version 1.5.1-271-g610930e
 ... loading /home/pgulutzan/tarantool_test/init.lua ...
 ...
 2013-12-03 17:46:16.244 [12957] 1/sched C&gt; entering event loop
 
 # Type 'Enter' and then start the Tarantool client.
-<prompt>$ </prompt><userinput>~/tarantool-stable/client/tarantool/tarantool</userinput>
+<prompt>$ </prompt><userinput>~/tarantool/client/tarantool/tarantool</userinput>
 <prompt>localhost&gt; </prompt>
 
 # Create a Lua function that will connect to the MySQL server,
@@ -207,7 +207,7 @@ must explicitly state where they are when building Tarantool from source.
 
 <para>
 The example was run on a Linux machine where the base directory
-had a copy of the Tarantool source on ~/tarantool-stable, and
+had a copy of the Tarantool source on ~/tarantool, and
 a copy of PostgreSQL on /usr. The postgres server is already running
 on the local host 127.0.0.1.
 </para>
@@ -242,7 +242,7 @@ INSERT 0 1
 # Build the Tarantool server. Make certain that "cmake" gets the right
 # paths for the PostgreSQL include directory and the PostgreSQL libpq
 # library which were checked earlier.
-<prompt>$ </prompt><userinput>cd ~/tarantool-stable</userinput>
+<prompt>$ </prompt><userinput>cd ~/tarantool</userinput>
 <prompt>$ </prompt><userinput>make clean</userinput>
 <prompt>$ </prompt><userinput>rm CMakeCache.txt</userinput>
 <prompt>$ </prompt><userinput>cmake . -DWITH_POSTGRESQL=on -DPostgreSQL_LIBRARY=/usr/lib/libpq.so\</userinput>
@@ -254,7 +254,7 @@ INSERT 0 1
 ...
 -- Configuring done
 -- Generating done
--- Build files have been written to: ~/tarantool-stable
+-- Build files have been written to: ~/tarantool
 <prompt>$ </prompt><userinput>make</userinput>
 ...
 [ 49%] Built target core
@@ -267,21 +267,21 @@ Linking CXX shared library libpg.so
 <prompt>$ </prompt>
 
 # Before starting Tarantool server, tell it where the PostgreSQL plugin is.
-<prompt>$ </prompt><userinput>export TARANTOOL_PLUGIN_DIR=~/tarantool-stable/src/plugin/pg</userinput>
+<prompt>$ </prompt><userinput>export TARANTOOL_PLUGIN_DIR=~/tarantool/src/plugin/pg</userinput>
 <prompt>$ </prompt>
 
 # Start the Tarantool server.
 # Run it in the background but let the initial display be in the foreground.
 # So it's possible to see the message that the plugin was loaded.
-<prompt>$ </prompt><userinput>~/tarantool-stable/src/box/tarantool&amp;</userinput>
+<prompt>$ </prompt><userinput>~/tarantool/src/box/tarantool&amp;</userinput>
 2013-12-06 13:01:51.518 [23978] 1/sched C> version 1.5.1-290-g45b93e7
-... Loading plugin: ~/tarantool-stable/src/plugin/pg/libpg.so ...
+... Loading plugin: ~/tarantool/src/plugin/pg/libpg.so ...
 ... Plugin 'postgresql' was loaded, version: 1
 ...
 2013-12-06 13:01:51.531 [23978] 1/sched C> entering event loop
 
 # Type 'Enter' and then start the Tarantool client.
-<prompt>$ </prompt><userinput>~/tarantool-stable/client/tarantool/tarantool</userinput>
+<prompt>$ </prompt><userinput>~/tarantool/client/tarantool/tarantool</userinput>
 <prompt>localhost&gt; </prompt>
 
 # Create a Lua function that will connect to the PostgreSQL server,
diff --git a/doc/user/preface.xml b/doc/user/preface.xml
index 96cd929902494088165fd5e4a5b0e255f4bc3cd9..c9e2285720c78b74bb872d9b617c22dfbdd38f29 100644
--- a/doc/user/preface.xml
+++ b/doc/user/preface.xml
@@ -8,8 +8,9 @@
 <section xml:id="tarantool-overview">
   <title>Tarantool: an overview</title>
   <para>
-    <productname>Tarantool</productname> is an in-memory NoSQL
-    database management system. The code is available for free under the terms of the
+    <productname>Tarantool</productname> is a NoSQL
+    database management system running in a Lua application
+    server. The code is available for free under the terms of the
     <citetitle
     xlink:href="http://www.gnu.org/licenses/license-list.html#ModifiedBSD">BSD
     license</citetitle>. Supported platforms are GNU/Linux, Mac OS
diff --git a/doc/user/tutorial.xml b/doc/user/tutorial.xml
index 8197556d797fa124e99d2b3d27c57a0b69f5ffc3..332b2a1d41bf80128bc985840fabbc34428fb22e 100644
--- a/doc/user/tutorial.xml
+++ b/doc/user/tutorial.xml
@@ -36,7 +36,7 @@ targetptr="getting-started-source"><quote>Downloading and building a source pack
 <para>
 If the installation has already been done, then you should try it out.
 	So we've provided some instructions that you can use to make a temporary <quote>sandbox</quote>.
-In a few minutes you can start the server, start the client, and type in some
+In a few minutes you can start the server and type in some
 database-manipulation statements.
 The section about sandbox is <olink
 targetptr="getting-started-start-stop"><quote>Starting Tarantool and making your first database</quote></olink>.
@@ -46,9 +46,9 @@ targetptr="getting-started-start-stop"><quote>Starting Tarantool and making your
 <title>Downloading and installing a binary package</title>
 
 <para>
-The repositories for the <quote>stable</quote> release are at <link xlink:href="http://tarantool.org/dist" xlink:title="tarantool.org/dist">tarantool.org/dist</link>.
+The repositories for the <quote>stable</quote> release are at <link xlink:href="http://tarantool.org/dist/stable" xlink:title="tarantool.org/dist/stble">tarantool.org/dist/stable</link>.
 The repositories for the <quote>master</quote> release are at <link xlink:href="http://tarantool.org/dist/master" xlink:title="tarantool.org/dist/master">tarantool.org/dist/master</link>.
-Since this is the manual for the <quote>master</quote> release, all instructions use <link xlink:href="http://tarantool.org/dist" xlink:title="tarantool.org/dist/master">tarantool.org/dist/master</link>.
+Since this is the manual for the <quote>master</quote> release, all instructions use <link xlink:href="http://tarantool.org/dist/master" xlink:title="tarantool.org/dist/master">tarantool.org/dist/master</link>.
 </para>
 
 <para>
@@ -87,7 +87,7 @@ release=`lsb_release -c -s`
 <command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename>
 # install
 <command>sudo apt-get update</command>
-<command>sudo apt-get install</command> tarantool tarantool-client
+<command>sudo apt-get install</command> tarantool
 </programlisting>
 </para>
 
@@ -114,7 +114,7 @@ release=`lsb_release -c -s`
 <command>sudo tee</command> <option>-a</option> <filename>/etc/apt/sources.list.d/tarantool.list</filename>
 # install
 <command>sudo apt-get update</command>
-<command>sudo apt-get install</command> tarantool tarantool-client
+<command>sudo apt-get install</command> tarantool
 </programlisting>
 </para>
 
@@ -162,27 +162,6 @@ release=`lsb_release -c -s`
 </programlisting>
 </para>
 
-<para>
-<programlisting>
-# ANY-LINUX commands for Tarantool master binary download:
-# If you have a GNU/Linux distribution which is not one of the above,
-# or if you want to install on your own subdirectory without affecting
-# /usr /etc etc., start your browser and go to
-# <link xlink:href="http://tarantool.org/dist/master">http://tarantool.org/dist/master</link> download page.
-# Look for a recent file whose name ends with "tar.gz". You will want the
-# right binary tarball (<filename>.tar.gz</filename>) file for your release architecture.
-# Click on either the one whose name ends with "linux-i686.tar.gz" or
-# the one whose name ends with "linux-x86_64.tar.gz" depending on your release architecture.
-# This will cause a download of the latest master tarball.
-# Suppose it is <filename>tarantool-1.6.1-9-g30fe8ae-linux-x86_64.tar.gz</filename>. Say:
-<command>tar</command> <option>zxvf</option> <filename>tarantool-1.6.1-9-g30fe8ae-linux-x86_64.tar.gz</filename>
-# You now have a directory named tarantool-1.6.1-9-g30fe8ae-linux-x86_64.
-# Let's move it to ~/tarantool, which is an easier name to remember.
-<command>mv</command> <filename>tarantool-1.6.1-9-g30fe8ae-linux-x86_64 ~/tarantool</filename>
-# Within it there is a subdirectory <filename>/bin</filename> containing both server and client.
-</programlisting>
-</para>
-
 <para>
 <programlisting>
 # FREEBSD commands for Tarantool master binary download:
@@ -213,7 +192,6 @@ More advice for binary downloads is at <link xlink:href="http://tarantool.org/do
 
 </section>
 
-
 <section xml:id="getting-started-source">
 <title>Downloading and building a source package</title>
 <para>
@@ -261,10 +239,10 @@ ones unless you intend to work on the documentation.
 <programlisting> binutils-dev or binutils-devel        # contains GNU bfd for printing stack traces
  gcc or clang                        # see above
  git                                 # see above
- uuid-dev                            # optional, for box_uuid_* functions
+ uuid-dev                            # for box.uuid_* functions and replication
  cmake                               # see above
- libreadline-dev                     # optional, for build with -DENABLE_CLIENT
- libncurses5-dev or ncurses-devel    # optional, for build with -DENABLE_CLIENT
+ libreadline-dev                     # for interactive mode
+ libncurses5-dev or ncurses-devel    # see above
  xsltproc                            # optional, for build with -DENABLE_DOC
  lynx                                # optional, for build with -DENABLE_DOC
  jing                                # optional, for build with -DENABLE_DOC
@@ -342,7 +320,7 @@ the tarantool download will go inside it, as <computeroutput><filename>~/taranto
 4. Use <productname>git</productname> to download from github.com.<programlisting>
 <command>cd</command> ~
 <command>git clone</command> <option>-b master</option> https://github.com/tarantool/tarantool.git <option>tarantool</option></programlisting>
-The optional argument <quote>-b master</quote> causes download from the stable branch instead of the stable branch,
+The optional argument <quote>-b master</quote> causes download from the master branch instead of the stable branch,
 and the optional last word on the line, <quote>tarantool</quote>, means download is to <computeroutput>~/tarantool</computeroutput>.
 </para>
 
@@ -360,15 +338,13 @@ but we prefer this method because it works with older versions of <productname>g
 6. Use CMake to initiate the build.<programlisting><command>cd</command> ~/tarantool
 <command>make clean</command>         # unnecessary, added for good luck
 <command>rm CMakeCache.txt</command>  # unnecessary, added for good luck
-<command>cmake .</command>            # Start build with build type=Debug, no client, no doc</programlisting>
+<command>cmake .</command>            # Start build with build type=Debug, no doc</programlisting>
 
 The option for specifying build type is <option>-DCMAKE_BUILD_TYPE=</option><replaceable>type</replaceable> where
    type = {None | Debug | Release | RelWithDebInfo | MinSizeRel} and a reasonable
 				choice for production is <option>-DCMAKE_BUILD_TYPE=RelWithDebInfo</option> (<quote>Debug</quote>
 				is used only by project maintainers and <quote>Release</quote> is used only when the
    highest performance is required).
-The option for asking to build client is <computeroutput><option>-DENABLE_CLIENT=</option><replaceable>{true|false}</replaceable></computeroutput>
-and a reasonable choice is <computeroutput><option>-DENABLE_CLIENT=true</option></computeroutput>.
 	The option for asking to build documentation is <computeroutput><option>-DENABLE_DOC=</option><replaceable>{true|false}</replaceable></computeroutput>
    and the assumption is that only a minority will need to rebuild the
    documentation (such as what you're reading now), so details about
@@ -433,7 +409,7 @@ For your added convenience, github.com has README files with example scripts:
 <productname xlink:href="https://github.com/tarantool/tarantool/blob/master/README.md">README.md</productname> for generic GNU/Linux.
 
 These example scripts assume that the intent is to download from the master branch, build
-the server and the client (but not the documentation), and run tests after build.
+the server (but not the documentation), and run tests after build.
 </para>
 
 <para>
@@ -458,45 +434,21 @@ Here is how to create a simple test database after installing.
 </para>
 
 <para>
-2. Create a configuration file. The Tarantool server can use a configuration
-file with some definitions of ports and database objects.
-  The server, by default, looks for its configuration file in
-  the current working directory and in <computeroutput><filename>etc/</filename></computeroutput>.
-Enter the following commands
-which make a minimal configuration file that will be suitable for day one.
-<programlisting>
-<command>echo</command> "slab_alloc_arena = 0.1" | <command>tee</command> tarantool.cfg
-<command>echo</command> "pid_file = \"box.pid\"" | <command>tee</command> <option>-a</option> tarantool.cfg
-<command>echo</command> "primary_port = 3301" | <command>tee</command> <option>-a</option> tarantool.cfg
-<command>echo</command> "admin_port = 33015" | <command>tee</command> <option>-a</option> tarantool.cfg
-<command>echo</command> "rows_per_wal = 50000" | <command>tee</command> <option>-a</option> tarantool.cfg
-<command>echo</command> "logger = \"tee -a tarantool.log\"" | <command>tee</command> <option>-a</option> tarantool.cfg
-<command>echo</command> "work_dir = \"work_dir\"" | <command>tee</command> <option>-a</option> tarantool.cfg
-# (With some downloads a tarantool.cfg file like this is already available
-# in a test subdirectory.)
-</programlisting>
-</para>
-
-<para>
-Initialize the storage area.
-You only have to do this once.<programlisting>
-#if you downloaded a binary with apt-get or yum, say this:
-  <command>/usr/bin/tarantool --init-storage</command>
-#if you downloaded and untarred a binary tarball to ~/tarantool, say this:
-  <command>~/tarantool/bin/tarantool --init-storage</command>
-#if you built from a source download, say this:
-  <command>~/tarantool/src/box/tarantool --init-storage</command></programlisting>
-</para>
-
-<para>
-Start the server.
+2. Start the server.
 The server name is <computeroutput><filename>tarantool</filename></computeroutput>.<programlisting>
 #if you downloaded a binary with apt-get or yum, say this:
   <command>/usr/bin/tarantool</command>
 #if you downloaded and untarred a binary tarball to ~/tarantool, say this:
   <command>~/tarantool/bin/tarantool</command>
 #if you built from a source download, say this:
-  <command>~/tarantool/src/box/tarantool</command> </programlisting>
+  <command>~/tarantool/src/tarantool</command> </programlisting>
+</para>
+
+<para>
+    The server starts in interactive mode and outputs a command prompt.
+    To turn on the database, configure it:
+<programlisting>tarantool> box.cfg{admin_port=3313}</programlisting>
+    (this minimal example is sufficient).
 </para>
 
 <para>
@@ -517,11 +469,13 @@ Now take the server down, with <programlisting><keycombo><keysym>Ctrl</keysym><k
 <para>
 Now start the server again. This time start it in the background.<programlisting>
 #if you downloaded a binary with apt-get or yum, say this:
-  <command>/usr/bin/tarantool --background</command>
+  <command>/usr/bin/tarantool</command>
 #if you downloaded and untarred a binary tarball to ~/tarantool, say this:
-  <command>~/tarantool/bin/tarantool --background</command>
+  <command>~/tarantool/bin/tarantool</command>
 #if you built from a source download, say this:
-  <command>~/tarantool/src/box/tarantool --background</command></programlisting>
+  <command>~/tarantool/src/box/tarantool</command>
+  <command>tarantool> box.cfg{admin_port=3313, background = true}</command>
+</programlisting>
 </para>
 
 <para>
@@ -536,28 +490,11 @@ Please follow
 instructions</olink> to find out how to manage
 Tarantool instances on your operating system.
 </para>
-<note>
-<para>
-  Alternatively, the server can be
-  started right out of the in-source build. Use the Tarantool
-  regression testing framework:
-  <programlisting><prompt>$ </prompt><command>./test/run</command> <option>--start-and-exit</option></programlisting>
-  It will create necessary files in directory
-  <filename>./test/var/</filename>, and start the server with
-  minimal configuration.
-</para>
-</note>
   
 <para>
-	Now that the server is up, you can start the client. The client name is tarantool.<programlisting>
-#if you downloaded a binary with apt-get or yum, say this:
-  <command>/usr/bin/tarantool</command>
-#if you downloaded and untarred a binary tarball to ~/tarantool, say this:
-  <command>~/tarantool/bin/tarantool</command>
-#if you built from a source download on ~tarantool, say this:
-  <command>~/tarantool/client/tarantool/tarantool</command></programlisting>
+	Now that the server is up, you can connect to its administrative port.<programlisting>
+  <command>telnet 0 3313</command></programlisting>
    
-If all goes well, a prompt will appear:<programlisting><prompt>localhost&gt;</prompt></programlisting>
 The client is waiting for the user to type instructions.
 </para>
 
@@ -574,7 +511,6 @@ try this:<programlisting><prompt>localhost&gt; </prompt><userinput>t = s:insert(
 <prompt>localhost&gt; </prompt><userinput>t = s:insert({3, 'Length', 93})</userinput>
 </programlisting>
 
-
 To select a tuple from the first space of the database,
 using the first defined key, try this:<programlisting><prompt>localhost&gt; </prompt><userinput>s:select({3})</userinput></programlisting>
 
@@ -617,7 +553,7 @@ To review ...
 If you followed all the instructions in this chapter, then
 so far you have: installed Tarantool from either a binary
 or a source repository, started up the Tarantool server,
-inserted and selected tuples with the Tarantool client.
+inserted and selected tuples.
 </para>
 </sidebar>
 
diff --git a/doc/www-data.in/_layout/index b/doc/www-data.in/_layout/index
index ad476f7a1f7da87a67f6721899f452f38802e0b0..c047f66c2474b5789e814abadddc429b9a7ddd57 100644
--- a/doc/www-data.in/_layout/index
+++ b/doc/www-data.in/_layout/index
@@ -1,5 +1,5 @@
 {% extends "base" %}
-{% set title = "Tarantool -- an in-memory NoSQL database" %}
+{% set title = "Tarantool -- a NoSQL database in a Lua script" %}
 {% block content %}
     {{ index.main }}
 {% endblock content %}
diff --git a/doc/www-data.in/_text/index.md b/doc/www-data.in/_text/index.md
index d37ad199da8b1546afdb87cf15b0e2746fcd534f..cf11e157caa28f616ccf47e15fa513e711d9910d 100644
--- a/doc/www-data.in/_text/index.md
+++ b/doc/www-data.in/_text/index.md
@@ -2,38 +2,45 @@ index:
     main: |
         # Introduction
 
-        Tarantool is an in-memory database designed to store the most volatile
-        and highly accessible web content. Tarantool has been extensively used
-        in production since 2009. It's **open source**, BSD licensed.
+        Tarantool is a NoSQL database running inside a Lua program. It's
+        created to store and process the most volatile and highly accessible
+        Web data. Tarantool has been extensively used in production since 2009.
+        It's **open source**, BSD licensed.
 
         # Features
 
+        - a drop-in replacement for Lua 5.1, based on LuaJIT 2.0;
+          simply use *#!/usr/bin/tarantool* instead of *#!/usr/bin/lua* in your script,
+        - [MsgPack](http://msgpack.org) data format and MsgPack based client-server
+          protocol,
+        - two data engines: 100% in-memory with optional persistence and a
+          [2-level disk-based B-tree](http://sphia.org), to use with large data
+          sets,
+        - secondary key and index iterators support,
+        - asynchronous master-master replication,
+        - authentication and access control,
         - [lowest CPU overhead](benchmark.html) to store or serve a
-        piece of content,
-        - optional Write Ahead Logging for persistency and reliability,
-        - universal data access with [rich Lua stored
-        procedures](http://github.com/mailru/tntlua), which can exchange messages
-        between each other or networked peers,
-        - asynchronous master-slave replication and hot backup.
+        piece of content.
 
         # Get started
 
         ``` bash
-        # apt-get install tarantool tarantool-client
-        # cd /etc/tarantool
-        # cp instances.available/example.cfg instances.enabled/fqueue.cfg
-        # cd /usr/share/tarantool/lua
-        # wget http://github.com/mailru/tntlua/raw/master/fqueue.lua -O init.lua
-        # service tarantool start
-        tarantool: Staring instances
-            Starting 'fqueue' ... ok
+        # apt-get install tarantool
+        # tarantool
+        # tarantool> box.cfg{{admin_port=3313}}
+        # tarantool> myspace = box.schema.create_space('myspace')
+        # tarantool> myspace:create_index('primary')
+        # tarantool> tuple = {{ name = 'Tarantool', release = box.info.version,
+        #            type = {{ 'NoSQL database', 'Lua interpreter'}}}}
+        # tarantool> myspace:auto_increment{{tuple}}
+        #   - [1, {{'release': '1.6.1-445-ge8d3201', 'name': 'Tarantool'
+        #          'type': ['NoSQL database', 'Lua interpreter']}}]
         ```
 
-        A fast and customizable message queue server is up and running.
-
         # Learn more
 
         - [YCSB benchmark results](benchmark.html)
         - [FAQ](faq.html)
         - [Source repository](http://github.com/tarantool/tarantool)
-        - [Lua stored procedures repository]( http://github.com/mailru/tntlua)
+        - [Lua stored procedures repository](http://github.com/mailru/tntlua)
+        - [1.5 web site and downloads](http://stable.tarantool.org)
diff --git a/doc/www-data.in/_text/intro.md b/doc/www-data.in/_text/intro.md
index f58d167f76ed9ce23e102a107c4e14666596d220..3cd7ec3b3504c26d83f8ab3fa82f25294edb24b2 100644
--- a/doc/www-data.in/_text/intro.md
+++ b/doc/www-data.in/_text/intro.md
@@ -2,7 +2,8 @@ intro:
     main: |
         ## What is Tarantool?
 
-        Tarantool is an in-memory NoSQL database. The code is available
+        Tarantool is a NoSQL database running and a Lua application server.
+        The code is available
         for free under the terms of [BSD license]. Supported platforms
         are GNU/Linux, Mac OS and FreeBSD.
 
diff --git a/doc/www-data.in/download.cmake b/doc/www-data.in/download.cmake
index 46f990ba4708cd0240c3ed4bce0ff10dbf529bc0..0cf3c2adb527e9b70ab053f1056169b6ba701330 100644
--- a/doc/www-data.in/download.cmake
+++ b/doc/www-data.in/download.cmake
@@ -15,10 +15,10 @@ download:
         The version string may also contain a git revision id, to ease
         identification of the unqiue commit used to generate the build.
 
-        The current version of the stable branch is **@PACKAGE_VERSION@**.
+        The current version of the master branch is **@PACKAGE_VERSION@**.
 
         An automatic build system creates, tests and publishes packages
-        for every push into the stable branch. All binary packages contain
+        for every push into the master branch. All binary packages contain
         symbol information. Additionally, **-debug-**
         packages contain asserts and are compiled without optimization.
 
@@ -47,7 +47,7 @@ download:
         It can be added to your apt sources list with:
         
         ```bash
-        wget http://tarantool.org/dist/master/public.key
+        wget http://tarantool.org/dist/public.key
         sudo apt-key add ./public.key
         release=`lsb_release -c -s`
 
@@ -60,11 +60,11 @@ download:
         # For Ubuntu:
         cat > /etc/apt/sources.list.d/tarantool.list <<- EOF
         deb http://tarantool.org/dist/master/ubuntu/ $release main
-        deb-src http://tarantool.org/dist/mater/ubuntu/ $release main
+        deb-src http://tarantool.org/dist/master/ubuntu/ $release main
         EOF
 
         sudo apt-get update
-        sudo apt-get install tarantool tarantool-client
+        sudo apt-get install tarantool
         ```
         
         ### CentOS 5-6 and RHEL 5-6
@@ -110,19 +110,6 @@ download:
         
         [layman]: http://wiki.gentoo.org/wiki/Layman
 
-        ### Other Linux distributions
-        <table border=1 title="Download the latest build, @PACKAGE_VERSION@" width=100%>
-        <th colspan=3>Static builds for Linux</th>
-        <tr>
-            <td> Binary tarball (**.tar.gz**) </td>
-            <td align=center> [32-bit] </td>
-            <td align=center> [64-bit] </td>
-        </tr>
-        </table>
-        
-        [32-bit]: http://tarantool.org/dist/master/tarantool-@PACKAGE_VERSION@-linux-i686.tar.gz
-        [64-bit]: http://tarantool.org/dist/master/tarantool-@PACKAGE_VERSION@-linux-x86_64.tar.gz
-
         ### FreeBSD
         
         Tarantool is available from the FreeBSD Ports collection
@@ -142,27 +129,27 @@ download:
 
         [Apple Developer]: https://developer.apple.com/downloads/
 
-        # Development branch
+        # Old master branch
 
-        In the same manner as for [the stable branch][stable], every push into
-        [the master branch][master] is [available online][builds-m].
-        The server roadmap is maintained on [Github][issues].
+        In the same manner as for [the master branch][master], every push into
+        [the old master][stable] is [available online][builds-s].
+        The server bugs database is maintained on [Github][issues].
         
         [stable]:   http://github.com/tarantool/tarantool/tree/stable
         [master]:   http://github.com/tarantool/tarantool/tree/master
-        [builds-m]: http://tarantool.org/dist/master
+        [builds-s]: http://tarantool.org/dist/stable
         [issues]:   http://github.com/tarantool/tarantool/issues
 
         ## Connectors
 
-        - Perl driver, [DR:Tarantool](http://search.cpan.org/~unera/DR-Tarantool-0.37/lib/DR/Tarantool.pm)
+        - Perl driver, [DR:Tarantool](http://search.cpan.org/~unera/DR-Tarantool-0.42/lib/DR/Tarantool.pm)
         - Java driver, [Maven repository](http://dgreenru.github.com/tarantool-java)
         - Ruby driver, [https://github.com/mailru/tarantool-ruby]
         - Python driver, [http://pypi.python.org/pypi/tarantool]
         - PHP driver, [https://github.com/tarantool/tarantool-php]
         - node.js driver, [https://github.com/devgru/node-tarantool]
         - Erlang driver, [https://github.com/rtsisyk/etarantool]
-        - C connector [is maintained in the server source tree](https://github.com/tarantool/tarantool/blob/master/connector/c)
+        - C connector [https://github.com/tarantool/tarantool-c]
         
         [http://tarantool.org/dist/master]: http://tarantool.org/dist/master
         [http://tarantool.org/dist/master/debian]: http://tarantool.org/dist/master/debian
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