Commit Diff


commit - 4a93f5baceb07caabfdfb803474932c5ab6aa473
commit + df48d3456605a4c42d61dc2ae55dc12196678931
blob - 36d748840d01bba90e3b5f6f72b69aa7a44e37ac
blob + cd2dcca7ddbf1b0076cdb2be49b1f7cf4ae4e596
--- .gitignore
+++ .gitignore
@@ -16,6 +16,7 @@ CMakeCache.txt
 CPackConfig.cmake
 CPackSourceConfig.cmake
 Makefile
+Doxyfile.API
 RPM
 *.src.rpm
 *.i386.rpm
@@ -26,6 +27,8 @@ extra/bin2c
 extra/dist/tarantoolctl.1
 cmake_install.cmake
 config.mk
+doc/doxygen/
+doc/api/
 doc/www/content/doc/dev_guide.html
 doc/www/content/doc/tnt.css
 doc/www/content/doc/user_guide.html
blob - 61b72dea17a9d8960954fa0968e280102f83ddd7
blob + 0e85bc9ab08d14d7720229e93971b1260224ae1e
--- CMakeLists.txt
+++ CMakeLists.txt
@@ -113,7 +113,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 "6")
+set (CPACK_PACKAGE_VERSION_PATCH "7")
 
 set (PACKAGE_VERSION "")
 
blob - 5d73bd8c5e94e603725ffc7bf4c5caf1f161deba
blob + a5669799836c4b15b756c6ea3edda649d03d4438
--- Doxyfile
+++ Doxyfile
@@ -26,7 +26,7 @@ DOXYFILE_ENCODING      = UTF-8
 # identify the project. Note that if you do not use Doxywizard you need
 # to put quotes around the project name if it contains spaces.
 
-PROJECT_NAME           = "Tarantool/Box"
+PROJECT_NAME           = "Tarantool"
 
 # The PROJECT_NUMBER tag can be used to enter a project or revision number.
 # This could be handy for archiving the generated documentation or
@@ -38,14 +38,14 @@ PROJECT_NUMBER         =
 # for a project that appears at the top of each page and should give viewer
 # a quick idea about the purpose of the project. Keep the description short.
 
-PROJECT_BRIEF          = "A transactional NoSQL database"
+PROJECT_BRIEF          = "Get your data in RAM. Get compute close to data. Enjoy the performance."
 
 # With the PROJECT_LOGO tag one can specify an logo or icon that is
 # included in the documentation. The maximum height of the logo should not
 # exceed 55 pixels and the maximum width should not exceed 200 pixels.
 # Doxygen will copy the logo to the output directory.
 
-PROJECT_LOGO           = doc/www-data/logo.png
+PROJECT_LOGO           = doc/www/theme/static/logo.png
 
 # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
 # base path where the generated documentation will be put.
@@ -655,7 +655,7 @@ WARN_LOGFILE           =
 # directories like "/usr/src/myproject". Separate the files or directories
 # with spaces.
 
-INPUT                  = include src
+INPUT                  = src/
 
 # This tag can be used to specify the character encoding of the source files
 # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is
@@ -1107,7 +1107,7 @@ GENERATE_ECLIPSEHELP   = NO
 # the directory name containing the HTML and XML files should also have
 # this name.
 
-ECLIPSE_DOC_ID         = org.doxygen.Project
+ECLIPSE_DOC_ID         = org.tarantool
 
 # The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs)
 # at top of each HTML page. The value NO (the default) enables the index and
@@ -1115,7 +1115,7 @@ ECLIPSE_DOC_ID         = org.doxygen.Project
 # navigation tree you can set this option to NO if you already set
 # GENERATE_TREEVIEW to YES.
 
-DISABLE_INDEX          = NO
+DISABLE_INDEX          = YES
 
 # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
 # structure should be generated to display hierarchical information.
@@ -1134,7 +1134,7 @@ GENERATE_TREEVIEW      = NO
 # documentation. Note that a value of 0 will completely suppress the enum
 # values from appearing in the overview section.
 
-ENUM_VALUES_PER_LINE   = 4
+ENUM_VALUES_PER_LINE   = 1
 
 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be
 # used to set the initial width (in pixels) of the frame in which the tree
@@ -1386,7 +1386,7 @@ MAN_LINKS              = NO
 # generate an XML file that captures the structure of
 # the code including all documentation.
 
-GENERATE_XML           = NO
+GENERATE_XML           = YES
 
 # The XML_OUTPUT tag is used to specify where the XML pages will be put.
 # If a relative path is entered the value of OUTPUT_DIRECTORY will be
@@ -1509,7 +1509,7 @@ INCLUDE_FILE_PATTERNS  =
 # undefined via #undef or recursively expanded use the := operator
 # instead of the = operator.
 
-PREDEFINED             = __attribute__(x)=
+PREDEFINED             = __attribute__(x)= API_EXPORT= LUA_API=
 
 # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
 # this tag can be used to specify a list of macro names that should be expanded.
blob - /dev/null
blob + 2faa825ca61e4c77f1384f80f5c05633239e6ca9 (mode 644)
--- /dev/null
+++ Doxyfile.API.in
@@ -0,0 +1,7 @@
+@INCLUDE = @PROJECT_SOURCE_DIR@/Doxyfile
+INPUT                  = @PROJECT_BINARY_DIR@/src/trivia/tarantool.h
+OUTPUT_DIRECTORY       = @PROJECT_BINARY_DIR@/doc/api/
+ENABLED_SECTIONS       = public
+DISABLE_INDEX          = YES
+GENERATE_TREEVIEW      = NO
+
blob - 6d72a6d2b97fa3482fcd1ff5badb5f877a327b32
blob + eeede13bffbdee0447f40d8c8b4faa6a1d80f85b
--- README.md
+++ README.md
@@ -78,7 +78,7 @@ To start the server, try:
 This will start Tarantool in interactive mode.
 
 To run Tarantool regression tests (test/test-run.py),
-a few additional Python modules are ncessary:
+a few additional Python modules are necessary:
  * daemon
  * pyyaml
  * msgpack-python
blob - 8338be7b6c1f1a2944bcc633113a93d997046994
blob + 8d6eafbcafe20fcdfff3a82bf68a13e751b80a2f
--- cmake/module.cmake
+++ cmake/module.cmake
@@ -27,7 +27,7 @@ function(rebuild_module_api)
                 ${CMAKE_CURRENT_SOURCE_DIR}/tarantool_footer.h
         )
 
-    add_custom_target(rebuild_module_api ALL DEPENDS ${srcfiles} ${dstfile})
+    add_custom_target(api ALL DEPENDS ${srcfiles} ${dstfile})
     install(FILES ${dstfile} DESTINATION ${MODULE_INCLUDEDIR})
 endfunction()
 set_source_files_properties("${CMAKE_CURRENT_BINARY_DIR}/tarantool.h" PROPERTIES GENERATED HEADER_FILE_ONLY)
blob - cbe16b27a66c88f44988f60d89398cfbf6f54376
blob + cb4a3e366543ea6b9d7c504c3a905b39a6e0db18
--- doc/CMakeLists.txt
+++ doc/CMakeLists.txt
@@ -9,5 +9,3 @@ if (ENABLE_DOC)
     add_subdirectory(sphinx)
     add_subdirectory(www)
 endif()
-
-
blob - e0b651417f5fb7afa8959d578ae832bbec106d70
blob + 1fb237e2c508774994ba472a45c4728f579c1927
--- doc/sphinx/CMakeLists.txt
+++ doc/sphinx/CMakeLists.txt
@@ -1,7 +1,16 @@
 find_package(Sphinx REQUIRED)
-
+find_package(Doxygen REQUIRED)
 #find_program(MKDIR mkdir)
 
+configure_file("${PROJECT_SOURCE_DIR}/Doxyfile.API.in"
+    "${PROJECT_BINARY_DIR}/Doxyfile.API")
+
+add_custom_target(api-doc
+    COMMAND ${DOXYGEN_EXECUTABLE} "${PROJECT_BINARY_DIR}/Doxyfile.API"
+    WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
+    DEPENDS api
+    COMMENT "Generating API documentation" VERBATIM)
+
 set(SPHINX_BUILD_SINGLE_DIR "${PROJECT_BINARY_DIR}/doc/sphinx/_single_build/")
 set(SPHINX_BUILD_HTML_DIR   "${PROJECT_BINARY_DIR}/doc/sphinx/_html_build/")
 set(SPHINX_HTML_DIR  "${PROJECT_BINARY_DIR}/doc/www/output/doc/")
@@ -20,6 +29,7 @@ add_custom_target(sphinx-html ALL
         "${PROJECT_SOURCE_DIR}/doc/sphinx"
         "${SPHINX_HTML_DIR}"
     COMMENT "Building HTML documentation with Sphinx"
+    DEPENDS api-doc
 )
 
 add_custom_target(sphinx-singlehtml ALL
@@ -31,4 +41,5 @@ add_custom_target(sphinx-singlehtml ALL
         "${SPHINX_HTML_DIR}"
         singlehtml.rst
     COMMENT "Building HTML documentation with Sphinx"
+    DEPENDS api-doc
 )
blob - 95ece5c1bfa6cbd4bb738bf1f22d3309d835e23b
blob + 95d7d8458eb632e43d5f766b57e8a82acb2fa84b
--- doc/sphinx/book/box/box_index.rst
+++ doc/sphinx/book/box/box_index.rst
@@ -455,7 +455,7 @@ API is a direct binding to corresponding methods of in
                                     field-values, or a tuple containing only
                                     the field-values.
 
-        :return: the number of matching index keys. The ``index`` function
+        :return: the number of matching index keys. The ``count`` function
                 is only applicable for the memtx storage engine.
         :rtype:  number
 
blob - 0b6d89082a357f973c8aec67f97a91b82e989365
blob + 23c1ac28329fb7edcfd42b860ca5c1317234c44c
--- doc/sphinx/book/box/box_space.rst
+++ doc/sphinx/book/box/box_space.rst
@@ -446,10 +446,6 @@ A list of all ``box.space`` functions follows, then co
 .. _space_object_len:
 
     space-object:len()
-
-        .. NOTE::
-
-            The ``len()`` function is only applicable for the memtx storage engine.
 
         :return: Number of tuples in the space.
 
blob - a4481d185118f487bb45c998e23df1b139920239
blob + 458a72ad79349fd0d24852c21ebecac0946ccc1b
--- doc/sphinx/book/box/index.rst
+++ doc/sphinx/book/box/index.rst
@@ -433,17 +433,13 @@ the same rules exist for the other data-manipulation f
 Every one of the examples does the same thing:
 select a tuple set from a space named tester where
 the primary-key field value equals 1.
-
-First there are "naming variations":
-
-    1. :codenormal:`box.space.tester:select{1}`
 
-    2. :codenormal:`box.space['tester']:select{1}`
-
-    3. :codenormal:`box.space[512]:select{1}`
-
-    4. :codenormal:`variable = 'tester'; box.space[variable]:select{1}`
-
+First there are "naming variations": |br|
+|nbsp| 1. :codenormal:`box.space.tester:select{1}` |br|
+|nbsp| 2. :codenormal:`box.space['tester']:select{1}` |br|
+|nbsp| 3. :codenormal:`box.space[512]:select{1}` |br|
+|nbsp| 4. :codenormal:`variable = 'tester'` |br|
+|nbsp| |nbsp| |nbsp| :codenormal:`box.space[variable]:select{1}` |br|
 ... There is an assumption that the numeric id of
 'tester' is 512, which happens to be the case in our
 sandbox example only. Literal values such as 'tester'
@@ -452,20 +448,15 @@ in this manual have the "box.space.space.tester:" form
 however, this is a matter of user preference and all
 the variants exist in the wild.
 
-Then there are "parameter variations":
-
-    1. :codenormal:`box.space.tester:select{1}`
-
-    2. :codenormal:`box.space.tester:select({1})`
-
-    3. :codenormal:`box.space.tester:select(1)`
-
-    4. :codenormal:`box.space.tester:select({1},{iterator='EQ'})`
-
-    5. :codenormal:`variable = 1; box.space.tester:select{variable}`
-
-    6. :codenormal:`variable = {1}; box.space.tester:select(variable)`
-
+Then there are "parameter variations": |br|
+|nbsp| 1. :codenormal:`box.space.tester:select{1}` |br|
+|nbsp| 2. :codenormal:`box.space.tester:select({1})` |br|
+|nbsp| 3. :codenormal:`box.space.tester:select(1)` |br|
+|nbsp| 4. :codenormal:`box.space.tester:select({1},{iterator='EQ'})` |br|
+|nbsp| 5. :codenormal:`variable = 1` |br|
+|nbsp| |nbsp| |nbsp| :codenormal:`box.space.tester:select{variable}` |br|
+|nbsp| 6. :codenormal:`variable = {1}` |br|
+|nbsp| |nbsp| |nbsp| :codenormal:`box.space.tester:select(variable)` |br|
 ... The primary-key value is enclosed in braces, and if
 it was a multi-part primary key then the value would be
 multi-part, for example "...select{1,2,3}". The braces
blob - 945ed7de6ac8694419bd31bd996830b0cc784ab8
blob + 47b375bd0703e8adb8b856d0352e574b371e8e68
--- doc/sphinx/book/box/limitations.rst
+++ doc/sphinx/book/box/limitations.rst
@@ -39,10 +39,9 @@ Length of an index name or space name or user name
     32 (``box.schema.NAME_MAX``).
 
 Limitations which are only applicable for the sophia storage engine
-    The maximum number of fields in an index is always 1, that is, multi-part
-    indexes are not supported. The maximum number of indexes in a space is
+    The maximum number of indexes in a space is
     always 1, that is, secondary indexes are not supported. Indexes must be
-    unique, that is, the options type=HASH or type=RTREE or type=BITSET are
+    type=TREE, that is, the options type=HASH or type=RTREE or type=BITSET are
     not supported. Indexes must be unique, that is, the option unique=false
-    is not supported. The ``alter()``, ``len()``, and ``count()`` functions
+    is not supported. The ``alter()`` and ``count()`` functions
     are not supported.
blob - b5b322c4125deb0f65d712eba8695be213d9a279
blob + 9e9f4f0347ee784942a74c794e8c37c3271161d4
--- doc/sphinx/book/configuration/cfg-basic.rst
+++ doc/sphinx/book/configuration/cfg-basic.rst
@@ -40,7 +40,7 @@
 .. confval:: sophia_dir
 
     A directory where sophia files will be stored. Can be relative to
-    :confval:`work_dir`. If not specified, defaults to :file:`work_dir/sophia`.
+    :confval:`work_dir`. If not specified, defaults to :file:`work_dir`.
 
     Type: string |br|
     Default: "sophia" |br|
blob - ecae307fabd13deb1bb021b428ebbc9f5d5bf616
blob + 35e50f363fdfc0b901757c5b4e70702fe433503e
--- doc/sphinx/conf.py
+++ doc/sphinx/conf.py
@@ -12,13 +12,17 @@ extensions = [
     'sphinx.ext.todo',
     'sphinx.ext.ifconfig',
     'ext.custom',
-    'ext.lua'
+    'ext.lua',
+    'breathe'
 ]
 primary_domain = 'lua'
 templates_path = ['../_templates']
 source_suffix = '.rst'
 
 project = u'Tarantool'
+breathe_projects = {
+    "api":"../../api/xml",
+}
 
 # |release| The full version, including alpha/beta/rc tags.
 release = open('../../../VERSION').read().strip()
blob - b88e0adfa8e4900575448ff3fa711d8799b92c94
blob + caa081d430143d834e5a5dbb5dce6384e7a24aaa
--- doc/sphinx/dev_guide/building_from_source.rst
+++ doc/sphinx/dev_guide/building_from_source.rst
@@ -25,8 +25,9 @@ explain what the steps are, then on the Internet you c
    Here are names of tools and libraries which may have to be installed in advance,
    using ``sudo apt-get`` (for Ubuntu), ``sudo yum install`` (for CentOS), or the
    equivalent on other platforms. Different platforms may use slightly different
-   names. Do not worry about the ones marked `optional, only in Mac OS scripts`
-   unless your platform is Mac OS.
+   names. Ignore the ones marked `optional, only in Mac OS scripts`
+   unless the platform is Mac OS. Ignore the one marked `optional,
+   only for documentation` unless the intent is to use the ``-DENABLE_DOC`` option in step 5.
 
    * **gcc and g++, or clang**                # see above
    * **git**                                  # see above
@@ -34,6 +35,7 @@ explain what the steps are, then on the Internet you c
    * **libreadline-dev or libreadline6-dev or readline-devel**  # for interactive mode
    * **autoconf**                             # optional, only in Mac OS scripts
    * **zlib1g** or **zlib**                   # optional, only in Mac OS scripts
+   * **doxygen**                              # optional, only for documentation
 
 2. Set up python modules for running the test suite or creating documentation.
    This step is optional. Python modules are not necessary for building Tarantool
@@ -108,6 +110,7 @@ explain what the steps are, then on the Internet you c
      pip install tarantool\>0.4 --user
      # For documentation
      sudo pip install pelican
+     sudo pip install breathe
      sudo pip install -U sphinx
 
 3. Use :code:`git` to download the latest source code from the
blob - /dev/null
blob + b4832ed084aad8d2cabba20e75bdec03221da7af (mode 644)
--- /dev/null
+++ doc/sphinx/reference/capi.rst
@@ -0,0 +1,6 @@
+-------------------------------------------------------------------------------
+                            Module C API
+-------------------------------------------------------------------------------
+
+.. doxygenfile:: tarantool.h
+   :project: api
blob - 9025ef6a01d36bf5d070729255023a5f70cde2ff
blob + 44c3b83d16d216af1c0580ff899455ab15495fb1
--- doc/sphinx/reference/index.rst
+++ doc/sphinx/reference/index.rst
@@ -25,4 +25,5 @@
     expirationd
     shard
     tarantool
+    capi
 
blob - 83fcd48238e3517e65fb5a51d7a9670bb495e311
blob + 1bafc7cacd52bfc5a805459cc841a191be5e4922
--- doc/www/content/newsite/index.yml
+++ doc/www/content/newsite/index.yml
@@ -32,6 +32,11 @@ blocks  :
     - "authentication and access control"
   news:
     -
+      - "Tarantool 1.6.6 is released"
+      - "https://groups.google.com/forum/#!topic/tarantool/4-RwTCVp2uQ"
+      - "28.08"
+      - "2015"
+    -
       - "Tarantool 1.6.5 is released"
       - "https://groups.google.com/d/msg/tarantool/zIM68T93Be0/nqwe6If74PwJ"
       - "22.04"
@@ -41,11 +46,6 @@ blocks  :
       - "http://www.meetup.com/Tarantool/events/220924229/"
       - "04.03"
       - "2015"
-    -
-      - "Tarantool 1.6.4 is released"
-      - "https://groups.google.com/forum/#!topic/tarantool/F3qc_zemEYg"
-      - "24.11"
-      - "2014"
   support:
     - format: rst
       content: >
blob - 3288794d3738458581e006cb88cebbd722835667
blob + d07b56a8467fd91aaf2418b996e0f309221aaa9b
--- src/box/box.cc
+++ src/box/box.cc
@@ -56,32 +56,58 @@
 #include "iobuf.h"
 #include "coio.h"
 
-static void process_ro(struct request *request, struct port *port);
-box_process_func box_process = process_ro;
-
 struct recovery_state *recovery;
 
 bool snapshot_in_progress = false;
 static bool box_init_done = false;
+bool is_ro = true;
 
-static void
-process_ro(struct request *request, struct port *port)
+void
+process_rw(struct request *request, struct tuple **result)
 {
-	if (!iproto_type_is_select(request->type))
-		tnt_raise(LoggedError, ER_READONLY);
-	return process_rw(request, port);
+	assert(iproto_type_is_dml(request->type));
+	rmean_collect(rmean_box, request->type, 1);
+	try {
+		struct space *space = space_cache_find(request->space_id);
+		struct txn *txn = txn_begin_stmt(request, space);
+		access_check_space(space, PRIV_W);
+		struct tuple *tuple;
+		switch (request->type) {
+		case IPROTO_INSERT:
+		case IPROTO_REPLACE:
+			tuple = space->handler->executeReplace(txn, space, request);
+			break;
+		case IPROTO_UPDATE:
+			tuple = space->handler->executeUpdate(txn, space, request);
+			break;
+		case IPROTO_DELETE:
+			tuple = space->handler->executeDelete(txn, space, request);
+			break;
+		case IPROTO_UPSERT:
+			space->handler->executeUpsert(txn, space, request);
+			/** fall through */
+		default:
+			 tuple = NULL;
+
+		}
+		if (result)
+			*result = tuple;
+	} catch (Exception *e) {
+		txn_rollback_stmt();
+		throw;
+	}
 }
 
 void
 box_set_ro(bool ro)
 {
-	box_process = ro ? process_ro : process_rw;
+	is_ro = ro;
 }
 
 bool
 box_is_ro(void)
 {
-	return box_process == process_ro;
+	return is_ro;
 }
 
 static void
@@ -96,7 +122,7 @@ recover_row(struct recovery_state *r, void *param, str
 	request_decode(&request, (const char *) row->body[0].iov_base,
 		row->body[0].iov_len);
 	request.header = row;
-	process_rw(&request, &null_port);
+	process_rw(&request, NULL);
 }
 
 /* {{{ configuration bindings */
@@ -301,7 +327,7 @@ boxk(enum iproto_type type, uint32_t space_id, const c
 	assert(data <= buf + sizeof(buf));
 	req.tuple = buf;
 	req.tuple_end = data;
-	process_rw(&req, &null_port);
+	process_rw(&req, NULL);
 }
 
 int
@@ -360,23 +386,15 @@ box_index_id_by_name(uint32_t space_id, const char *na
 }
 /** \endcond public */
 
-static inline int
+int
 box_process1(struct request *request, box_tuple_t **result)
 {
-	struct port_buf port_buf;
-	port_buf_create(&port_buf);
 	try {
-		box_process(request, &port_buf.base);
-		box_tuple_t *tuple = NULL;
-		/* Sophia: always bless tuple even if result is NULL */
-		if (port_buf.first != NULL)
-			tuple = tuple_bless(port_buf.first->tuple);
-		port_buf_destroy(&port_buf);
-		if (result)
-			*result = tuple;
+		if (is_ro)
+			tnt_raise(LoggedError, ER_READONLY);
+		process_rw(request, result);
 		return 0;
 	} catch (Exception *e) {
-		port_buf_destroy(&port_buf);
 		return -1;
 	}
 }
@@ -386,18 +404,19 @@ box_select(struct port *port, uint32_t space_id, uint3
 	   int iterator, uint32_t offset, uint32_t limit,
 	   const char *key, const char *key_end)
 {
-	struct request request;
-	request_create(&request, IPROTO_SELECT);
-	request.space_id = space_id;
-	request.index_id = index_id;
-	request.limit = limit;
-	request.offset = offset;
-	request.iterator = iterator;
-	request.key = key;
-	request.key_end = key_end;
+	rmean_collect(rmean_box, IPROTO_SELECT, 1);
 
 	try {
-		box_process(&request, port);
+		struct space *space = space_cache_find(space_id);
+		struct txn *txn = in_txn();
+		access_check_space(space, PRIV_R);
+		space->handler->executeSelect(txn, space, index_id, iterator,
+					      offset, limit, key, key_end, port);
+		if (txn == NULL) {
+			/* no txn is created, so simply collect garbage here */
+			fiber_gc();
+		}
+		port_eof(port);
 		return 0;
 	} catch (Exception *e) {
 		/* will be hanled by box.error() in Lua */
@@ -583,6 +602,7 @@ box_free(void)
 		tuple_free();
 		port_free();
 		engine_shutdown();
+		rmean_delete(rmean_error);
 		rmean_delete(rmean_box);
 	}
 }
@@ -607,6 +627,25 @@ engine_init()
 	engine_register(sophia);
 }
 
+/**
+ * @brief Reduce the current number of threads in the thread pool to the
+ * bare minimum. Doesn't prevent the pool from spawning new threads later
+ * if demand mounts.
+ */
+static void
+thread_pool_trim()
+{
+	/*
+	 * Trim OpenMP thread pool.
+	 * Though we lack the direct control the workaround below works for
+	 * GNU OpenMP library. The library stops surplus threads on entering
+	 * a parallel region. Can't go below 2 threads due to the
+	 * implementation quirk.
+	 */
+#pragma omp parallel num_threads(2)
+	;
+}
+
 static inline void
 box_init(void)
 {
@@ -616,6 +655,7 @@ box_init(void)
 		   cfg_getd("slab_alloc_factor"));
 
 	rmean_box = rmean_new(iproto_type_strs, IPROTO_TYPE_STAT_MAX);
+	rmean_error = rmean_new(rmean_error_strings, RMEAN_ERROR_LAST);
 
 	engine_init();
 
@@ -677,6 +717,11 @@ box_init(void)
 
 	engine_end_recovery();
 
+	/*
+	 * Recovery inflates the thread pool quite a bit (due to parallel sort).
+	 */
+	thread_pool_trim();
+
 	rmean_cleanup(rmean_box);
 
 	if (recovery_has_replica(recovery))
blob - 3ca093176c1f0b6d57c2d033a47cdcf8a4df493c
blob + 0f8038730ffbfb7dccf2f8a03969cba45f00aa79
--- src/box/box.h
+++ src/box/box.h
@@ -53,16 +53,6 @@ void box_free(void);
 void
 box_atfork();
 
-/**
- * The main entry point to the
- * Box: callbacks into the request processor.
- * These are function pointers since they can
- * change when entering/leaving read-only mode
- * (master->slave propagation).
- */
-typedef void (*box_process_func)(struct request *request, struct port *port);
-/** For read-write operations. */
-extern box_process_func box_process;
 
 void
 box_set_ro(bool ro);
@@ -133,33 +123,142 @@ box_select(struct port *port, uint32_t space_id, uint3
 	   const char *key, const char *key_end);
 
 /** \cond public */
+
+/*
+ * Opaque structure passed to the stored C procedure
+ */
 typedef struct box_function_ctx box_function_ctx_t;
+
+/**
+ * Return a tuple from stored C procedure.
+ *
+ * Returned tuple is automatically reference counted by Tarantool.
+ *
+ * \param ctx an opaque structure passed to the stored C procedure by
+ * Tarantool
+ * \param tuple a tuple to return
+ * \retval -1 on error (perhaps, out of memory; check box_error_last())
+ * \retval 0 otherwise
+ */
 API_EXPORT int
 box_return_tuple(box_function_ctx_t *ctx, box_tuple_t *tuple);
 
+/**
+ * Find space id by name.
+ *
+ * This function performs SELECT request to _vspace system space.
+ * \param name space name
+ * \param len length of \a name
+ * \retval BOX_ID_NIL on error or if not found (check box_error_last())
+ * \retval space_id otherwise
+ * \sa box_index_id_by_name
+ */
 API_EXPORT uint32_t
 box_space_id_by_name(const char *name, uint32_t len);
 
+/**
+ * Find index id by name.
+ *
+ * This function performs SELECT request to _vindex system space.
+ * \param space_id space identifier
+ * \param name index name
+ * \param len length of \a name
+ * \retval BOX_ID_NIL on error or if not found (check box_error_last())
+ * \retval index_id otherwise
+ * \sa box_space_id_by_name
+ */
 API_EXPORT uint32_t
 box_index_id_by_name(uint32_t space_id, const char *name, uint32_t len);
 
+/**
+ * Execute an INSERT request.
+ *
+ * \param space_id space identifier
+ * \param tuple encoded tuple in MsgPack Array format ([ field1, field2, ...])
+ * \param tuple_end end of @a tuple
+ * \param[out] result a new tuple. Can be set to NULL to discard result.
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id]:insert(tuple) \endcode
+ */
 API_EXPORT int
 box_insert(uint32_t space_id, const char *tuple, const char *tuple_end,
 	   box_tuple_t **result);
 
+/**
+ * Execute an REPLACE request.
+ *
+ * \param space_id space identifier
+ * \param tuple encoded tuple in MsgPack Array format ([ field1, field2, ...])
+ * \param tuple_end end of @a tuple
+ * \param[out] result a new tuple. Can be set to NULL to discard result.
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id]:replace(tuple) \endcode
+ */
 API_EXPORT int
 box_replace(uint32_t space_id, const char *tuple, const char *tuple_end,
 	    box_tuple_t **result);
 
+/**
+ * Execute an DELETE request.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * \param key_end the end of encoded \a key.
+ * \param[out] result an old tuple. Can be set to NULL to discard result.
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id].index[index_id]:delete(key) \endcode
+ */
 API_EXPORT int
 box_delete(uint32_t space_id, uint32_t index_id, const char *key,
 	   const char *key_end, box_tuple_t **result);
 
+/**
+ * Execute an UPDATE request.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * \param key_end the end of encoded \a key.
+ * \param ops encoded operations in MsgPack Arrat format, e.g.
+ * [ [ '=', field_id,  value ],  ['!', 2, 'xxx'] ]
+ * \param ops_end the end of encoded \a ops
+ * \param index_base 0 if field_ids in update operations are zero-based
+ * indexed (like C) or 1 if for one-based indexed field ids (like Lua).
+ * \param[out] result a new tuple. Can be set to NULL to discard result.
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id].index[index_id]:update(key, ops) \endcode
+ * \sa box_upsert()
+ */
 API_EXPORT int
 box_update(uint32_t space_id, uint32_t index_id, const char *key,
 	   const char *key_end, const char *ops, const char *ops_end,
 	   int index_base, box_tuple_t **result);
 
+/**
+ * Execute an UPSERT request.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * \param key_end the end of encoded \a key.
+ * \param ops encoded operations in MsgPack Arrat format, e.g.
+ * [ [ '=', field_id,  value ],  ['!', 2, 'xxx'] ]
+ * \param ops_end the end of encoded \a ops
+ * \param tuple encoded tuple in MsgPack Array format ([ field1, field2, ...])
+ * \param tuple_end end of @a tuple
+ * \param index_base 0 if field_ids in update operations are zero-based
+ * indexed (like C) or 1 if for one-based indexed field ids (like Lua).
+ * \param[out] result a new tuple. Can be set to NULL to discard result.
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id].index[index_id]:update(key, ops) \endcode
+ * \sa box_update()
+ */
 API_EXPORT int
 box_upsert(uint32_t space_id, uint32_t index_id, const char *key,
 	   const char *key_end, const char *ops, const char *ops_end,
@@ -168,4 +267,14 @@ box_upsert(uint32_t space_id, uint32_t index_id, const
 
 /** \endcond public */
 
+/**
+ * The main entry point to the
+ * Box: callbacks into the request processor.
+ * These are function pointers since they can
+ * change when entering/leaving read-only mode
+ * (master->slave propagation).
+ */
+int
+box_process1(struct request *request, box_tuple_t **result);
+
 #endif /* INCLUDES_TARANTOOL_BOX_H */
blob - d65cc6c729b0f7fadb13b9fea40f95030ea9bd4b
blob + cd342f6616e630797e37c34c129fdba468e3d4dd
--- src/box/engine.cc
+++ src/box/engine.cc
@@ -157,30 +157,27 @@ Handler::Handler(Engine *f)
 {
 }
 
-void
+struct tuple *
 Handler::executeReplace(struct txn *, struct space *,
-                        struct request *, struct port *)
+                        struct request *)
 {
 	tnt_raise(ClientError, ER_UNSUPPORTED, engine->name, "replace");
 }
 
-void
-Handler::executeDelete(struct txn*, struct space *, struct request *,
-                       struct port *)
+struct tuple *
+Handler::executeDelete(struct txn*, struct space *, struct request *)
 {
 	tnt_raise(ClientError, ER_UNSUPPORTED, engine->name, "delete");
 }
 
-void
-Handler::executeUpdate(struct txn*, struct space *, struct request *,
-                       struct port *)
+struct tuple *
+Handler::executeUpdate(struct txn*, struct space *, struct request *)
 {
 	tnt_raise(ClientError, ER_UNSUPPORTED, engine->name, "update");
 }
 
 void
-Handler::executeUpsert(struct txn *, struct space *, struct request *,
-                       struct port *)
+Handler::executeUpsert(struct txn *, struct space *, struct request *)
 {
 	tnt_raise(ClientError, ER_UNSUPPORTED, engine->name, "upsert");
 }
@@ -191,25 +188,19 @@ Handler::onAlter(Handler *)
 }
 
 void
-Handler::executeSelect(struct txn* /* txn */, struct space *space,
-                       struct request *request,
-                       struct port *port)
+Handler::executeSelect(struct txn *, struct space *space,
+		       uint32_t index_id, uint32_t iterator,
+		       uint32_t offset, uint32_t limit,
+		       const char *key, const char * /* key_end */,
+		       struct port *port)
 {
-	/*
-	tnt_raise(ClientError, ER_UNSUPPORTED, engine->name, "select()");
-	*/
-	Index *index = index_find(space, request->index_id);
+	Index *index = index_find(space, index_id);
 
-	ERROR_INJECT_EXCEPTION(ERRINJ_TESTING);
-
 	uint32_t found = 0;
-	uint32_t offset = request->offset;
-	uint32_t limit = request->limit;
-	if (request->iterator >= iterator_type_MAX)
+	if (iterator >= iterator_type_MAX)
 		tnt_raise(IllegalParams, "Invalid iterator type");
-	enum iterator_type type = (enum iterator_type) request->iterator;
+	enum iterator_type type = (enum iterator_type) iterator;
 
-	const char *key = request->key;
 	uint32_t part_count = key ? mp_decode_array(&key) : 0;
 	key_validate(index->key_def, type, key, part_count);
 
blob - a83919a776fc8cbdd94d765e11e906e9e4bd35c0
blob + 2b881ab78d0802057eef048821e81655df4f22fd
--- src/box/engine.h
+++ src/box/engine.h
@@ -180,17 +180,26 @@ struct Handler: public Object { (public)
 	Handler(Engine *f);
 	virtual ~Handler() {}
 
-	virtual void executeReplace(struct txn *, struct space *,
-				    struct request *, struct port *);
-	virtual void executeDelete(struct txn *, struct space *,
-				   struct request *, struct port *);
-	virtual void executeUpdate(struct txn *, struct space *,
-				   struct request *, struct port *);
-	virtual void executeUpsert(struct txn *, struct space *,
-				   struct request *, struct port *);
-	virtual void executeSelect(struct txn *, struct space *,
-				   struct request *, struct port *);
+	virtual struct tuple *
+	executeReplace(struct txn *, struct space *,
+		       struct request *);
+	virtual struct tuple *
+	executeDelete(struct txn *, struct space *,
+		      struct request *);
+	virtual struct tuple *
+	executeUpdate(struct txn *, struct space *,
+		      struct request *);
+	virtual void
+	executeUpsert(struct txn *, struct space *,
+		      struct request *);
 
+	virtual void
+	executeSelect(struct txn *, struct space *,
+		      uint32_t index_id, uint32_t iterator,
+		      uint32_t offset, uint32_t limit,
+		      const char *key, const char *key_end,
+		      struct port *);
+
 	virtual void onAlter(Handler *old);
 	Engine *engine;
 };
blob - 789ffc6f6287941fb7fbb7e0d06b9f9180d14e6f
blob + d50c0f33d2614e7e21afd80c9cabc316b92f110b
--- src/box/error.cc
+++ src/box/error.cc
@@ -32,7 +32,12 @@
 #include <stdio.h>
 
 #include <fiber.h>
+struct rmean *rmean_error = NULL;
 
+const char *rmean_error_strings[RMEAN_ERROR_LAST] = {
+	"ERROR"
+};
+
 static struct method clienterror_methods[] = {
 	make_method(&type_ClientError, "code", &ClientError::errcode),
 	METHODS_SENTINEL
@@ -49,6 +54,8 @@ ClientError::ClientError(const char *file, unsigned li
 	va_start(ap, errcode);
 	vsnprintf(m_errmsg, sizeof(m_errmsg),
 		  tnt_errcode_desc(m_errcode), ap);
+	if (rmean_error)
+		rmean_collect(rmean_error, RMEAN_ERROR, 1);
 	va_end(ap);
 }
 
@@ -59,6 +66,8 @@ ClientError::ClientError(const char *file, unsigned li
 	m_errcode = errcode;
 	strncpy(m_errmsg, msg, sizeof(m_errmsg) - 1);
 	m_errmsg[sizeof(m_errmsg) - 1] = 0;
+	if (rmean_error)
+		rmean_collect(rmean_error, RMEAN_ERROR, 1);
 }
 
 void
blob - 1e3deab61be4ee4e725fff40a113ef180a165c5b
blob + 9aa96b4ffaf29a9884251059850216c03ee62000
--- src/box/error.h
+++ src/box/error.h
@@ -32,7 +32,16 @@
  */
 #include "errcode.h"
 #include "exception.h"
+#include "rmean.h"
 
+extern struct rmean *rmean_error;
+
+enum rmean_error_name {
+	RMEAN_ERROR,
+	RMEAN_ERROR_LAST
+};
+extern const char *rmean_error_strings[RMEAN_ERROR_LAST];
+
 extern const struct type type_ClientError;
 class ClientError: public Exception {
 public:
@@ -86,45 +95,77 @@ class ErrorInjection: public LoggedError { (public)
 };
 
 /** \cond public */
+
 struct box_error;
+/**
+ * Error - contains information about error.
+ */
 typedef struct box_error box_error_t;
 
 /**
- * Return error type, e.g. "ClientError", "SocketError", etc.
+ * Return the error type, e.g. "ClientError", "SocketError", etc.
+ * \param error
+ * \return not-null string
  */
 API_EXPORT const char *
 box_error_type(const box_error_t *error);
 
-/*
+/**
  * Return IPROTO error code
+ * \param error error
+ * \return enum box_error_code
  */
 API_EXPORT uint32_t
 box_error_code(const box_error_t *error);
 
-/*
- * Return error message
+/**
+ * Return the error message
+ * \param error error
+ * \return not-null string
  */
 API_EXPORT const char *
 box_error_message(const box_error_t *error);
 
 /**
- * Return last error
+ * Get the information about the last API call error.
+ *
+ * The Tarantool error handling works most like libc's errno. All API calls
+ * return -1 or NULL in the event of error. An internal pointer to
+ * box_error_t type is set by API functions to indicate what went wrong.
+ * This value is only significant if API call failed (returned -1 or NULL).
+ *
+ * Successed function can also touch last error in some cases. You don't
+ * have to clear last error before calling API functions. The returned object
+ * is valid only until next call to **any** API function.
+ *
+ * You must set the last error using box_error_raise() in your stored C
+ * procedures if you want to return custom error message. You can re-throw
+ * the last API error to IPROTO client by keeping the current value and
+ * returning -1 to Tarantool from your stored procedure.
+ *
+ * \return last error.
  */
 API_EXPORT const box_error_t *
 box_error_last(void);
 
-/*
- * Clear last error
+/**
+ * Clear the last error.
  */
 API_EXPORT void
 box_error_clear(void);
 
-/*
- * Set last error
- * \param code IPROTO error code
+/**
+ * Set the last error.
+ *
+ * \param code IPROTO error code (enum \link box_error_code \endlink)
+ * \param format (const char * ) - printf()-like format string
+ * \param ... - format arguments
+ * \returns -1 for convention use
+ *
+ * \sa enum box_error_code
  */
 API_EXPORT int
-box_error_raise(uint32_t code, const char *fmt, ...);
+box_error_raise(uint32_t code, const char *format, ...);
 /** \endcond public */
 
 #endif /* TARANTOOL_BOX_ERROR_H_INCLUDED */
blob - 8d52481c73aa7f028169348242a90686c0299cac
blob + b0519d774590ee3df1699d1e81f3f71d1f236820
--- src/box/index.cc
+++ src/box/index.cc
@@ -34,6 +34,8 @@
 #include "schema.h"
 #include "user_def.h"
 #include "space.h"
+#include "iproto_constants.h"
+#include "request.h"
 
 const char *iterator_type_strs[] = {
 	/* [ITER_EQ]  = */ "EQ",
@@ -257,7 +259,7 @@ tuple_bless_null(struct tuple *tuple)
 	return NULL;
 }
 
-size_t
+ssize_t
 box_index_len(uint32_t space_id, uint32_t index_id)
 {
 	try {
@@ -267,7 +269,7 @@ box_index_len(uint32_t space_id, uint32_t index_id)
 	}
 }
 
-size_t
+ssize_t
 box_index_bsize(uint32_t space_id, uint32_t index_id)
 {
        try {
@@ -305,6 +307,9 @@ box_index_get(uint32_t space_id, uint32_t index_id, co
 		uint32_t part_count = key ? mp_decode_array(&key) : 0;
 		primary_key_validate(index->key_def, key, part_count);
 		struct tuple *tuple = index->findByKey(key, part_count);
+		/* Count statistics */
+		rmean_collect(rmean_box, IPROTO_SELECT, 1);
+
 		*result = tuple_bless_null(tuple);
 		return 0;
 	}  catch (Exception *) {
blob - b00c4c3b2345157e81c2098de35efa3f26b71dfb
blob + a97c1d9a2f32231ac5e524320494124fe5e03beb
--- src/box/index.h
+++ src/box/index.h
@@ -38,10 +38,10 @@
 
 /** \cond public */
 typedef struct tuple box_tuple_t;
+/** A space iterator */
 typedef struct iterator box_iterator_t;
 
 /**
- * @abstract Iterator type
  * Controls how to iterate over tuples in an index.
  * Different index types support different iterator types.
  * For example, one can start iteration from a particular value
@@ -79,14 +79,42 @@ enum iterator_type {
 	iterator_type_MAX     = ITER_NEIGHBOR + 1
 };
 
+/**
+ * Allocate and initialize iterator for space_id, index_id.
+ *
+ * A returned iterator must be destroyed by box_iterator_free().
+ *
+ * \param space_id space identifier.
+ * \param index_id index identifier.
+ * \param type \link iterator_type iterator type \endlink
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * \param key_end the end of encoded \a key
+ * \retval NULL on error (check box_error_last())
+ * \retval iterator otherwise
+ * \sa box_iterator_next()
+ * \sa box_iterator_free()
+ */
 API_EXPORT box_iterator_t *
 box_index_iterator(uint32_t space_id, uint32_t index_id, int type,
 		   const char *key, const char *key_end);
+/**
+ * Retrive the next item from the \a iterator.
+ *
+ * \param iterator an iterator returned by box_index_iterator().
+ * \param[out] result a tuple or NULL if there is no more data.
+ * \retval -1 on error (check box_error_last() for details)
+ * \retval 0 on success. The end of data is not an error.
+ */
 API_EXPORT int
-box_iterator_next(box_iterator_t *itr, box_tuple_t **result);
+box_iterator_next(box_iterator_t *iterator, box_tuple_t **result);
 
+/**
+ * Destroy and deallocate iterator.
+ *
+ * \param iterator an interator returned by box_index_iterator()
+ */
 API_EXPORT void
-box_iterator_free(box_iterator_t *itr);
+box_iterator_free(box_iterator_t *iterator);
 
 /** \endcond public */
 
@@ -264,28 +292,114 @@ index_is_primary(const Index *index)
 
 /** \cond public */
 
-API_EXPORT size_t
+/**
+ * Return the number of element in the index.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \retval -1 on error (check box_error_last())
+ * \retval >= 0 otherwise
+ */
+API_EXPORT ssize_t
 box_index_len(uint32_t space_id, uint32_t index_id);
 
-API_EXPORT size_t
+/**
+ * Return the number of bytes used in memory by the index.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \retval -1 on error (check box_error_last())
+ * \retval >= 0 otherwise
+ */
+API_EXPORT ssize_t
 box_index_bsize(uint32_t space_id, uint32_t index_id);
 
+/**
+ * Return a random tuple from the index (useful for statistical analysis).
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param rnd random seed
+ * \param[out] result a tuple or NULL if index is empty
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id].index[index_id]:random(rnd) \endcode
+ */
 API_EXPORT int
 box_index_random(uint32_t space_id, uint32_t index_id, uint32_t rnd,
 		box_tuple_t **result);
 
+/**
+ * Get a tuple from index by the key.
+ *
+ * Please note that this function works much more faster than
+ * box_select() or box_index_iterator() + box_iterator_next().
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * \param key_end the end of encoded \a key
+ * \param[out] result a tuple or NULL if index is empty
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \pre key != NULL
+ * \sa \code box.space[space_id].index[index_id]:get(key) \endcode
+ */
 API_EXPORT int
 box_index_get(uint32_t space_id, uint32_t index_id, const char *key,
 	      const char *key_end, box_tuple_t **result);
 
+/**
+ * Return a first (minimal) tuple matched the provided key.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * If NULL then equvivalent to an empty array.
+ * \param key_end the end of encoded \a key.
+ * Must be NULL if \a key is NULL.
+ * \param[out] result a tuple or NULL if index is empty
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id].index[index_id]:min(key) \endcode
+ */
 API_EXPORT int
 box_index_min(uint32_t space_id, uint32_t index_id, const char *key,
 	      const char *key_end, box_tuple_t **result);
 
+/**
+ * Return a last (maximal) tuple matched the provided key.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * If NULL then equvivalent to an empty array.
+ * \param key_end the end of encoded \a key.
+ * Must be NULL if \a key is NULL.
+ * \param[out] result a tuple or NULL if index is empty
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa \code box.space[space_id].index[index_id]:max(key) \endcode
+ */
 API_EXPORT int
 box_index_max(uint32_t space_id, uint32_t index_id, const char *key,
 	      const char *key_end, box_tuple_t **result);
 
+/**
+ * Count the number of tuple matched the provided key.
+ *
+ * \param space_id space identifier
+ * \param index_id index identifier
+ * \param type iterator type - enum \link iterator_type \endlink
+ * \param key encoded key in MsgPack Array format ([part1, part2, ...]).
+ * If NULL then equvivalent to an empty array.
+ * \param key_end the end of encoded \a key.
+ * Must be NULL if \a key is NULL.
+ * \retval -1 on error (check box_error_last())
+ * \retval >=0 on success
+ * \sa \code box.space[space_id].index[index_id]:count(key,
+ *     { iterator = type }) \endcode
+ */
 API_EXPORT ssize_t
 box_index_count(uint32_t space_id, uint32_t index_id, int type,
 		const char *key, const char *key_end);
blob - f5796b96f6893ce70280bdf8c8613f6c02714b29
blob + be8026c38526d8fd0632e376becffe64549ca244
--- src/box/iproto.cc
+++ src/box/iproto.cc
@@ -54,6 +54,7 @@
 #include "lua/call.h"
 
 /* {{{ iproto_msg - declaration */
+
 
 /**
  * A single msg from io thread. All requests
@@ -513,6 +514,9 @@ iproto_connection_on_input(ev_loop *loop, struct ev_io
 			iproto_connection_close(con);
 			return;
 		}
+		/* Count statistics */
+		rmean_collect(rmean_net, RMEAN_NET_RECEIVED, nrd);
+
 		/* Update the read position and connection state. */
 		in->wpos += nrd;
 		con->parse_size += nrd;
@@ -564,6 +568,9 @@ iproto_flush(struct iobuf *iobuf, struct iproto_connec
 	iov[iovcnt-1].iov_len = end->iov_len - begin->iov_len * (iovcnt == 1);
 
 	ssize_t nwr = sio_writev(fd, iov, iovcnt);
+
+	/* Count statistics */
+	rmean_collect(rmean_net, RMEAN_NET_SENT, nwr);
 	if (nwr > 0) {
 		if (begin->used + nwr == end->used) {
 			if (ibuf_used(&iobuf->in) == 0) {
@@ -626,17 +633,16 @@ tx_process_msg(struct cmsg *m)
 	try {
 		switch (msg->header.type) {
 		case IPROTO_SELECT:
-		case IPROTO_INSERT:
-		case IPROTO_REPLACE:
-		case IPROTO_UPDATE:
-		case IPROTO_DELETE:
-		case IPROTO_UPSERT:
-			assert(msg->request.type == msg->header.type);
+		{
 			struct iproto_port port;
 			iproto_port_init(&port, out, msg->header.sync);
-			try {
-				box_process(&msg->request, (struct port *) &port);
-			} catch (Exception *e) {
+			struct request *req = &msg->request;
+			int rc = box_select((struct port *) &port,
+					    req->space_id, req->index_id,
+					    req->iterator,
+					    req->offset, req->limit,
+					    req->key, req->key_end);
+			if (rc < 0) {
 				/*
 				 * This only works if there are no
 				 * yields between the moment the
@@ -646,9 +652,27 @@ tx_process_msg(struct cmsg *m)
 				 */
 				if (port.found)
 					obuf_rollback_to_svp(out, &port.svp);
-				throw;
+				throw (Exception *) box_error_last();
 			}
+			break;
+		}
+		case IPROTO_INSERT:
+		case IPROTO_REPLACE:
+		case IPROTO_UPDATE:
+		case IPROTO_DELETE:
+		case IPROTO_UPSERT:
+		{
+			assert(msg->request.type == msg->header.type);
+			struct tuple *tuple;
+			if (box_process1(&msg->request, &tuple) < 0)
+				throw (Exception *) box_error_last();
+			struct obuf_svp svp = iproto_prepare_select(out);
+			if (tuple)
+				tuple_to_obuf(tuple, out);
+			iproto_reply_select(out, &svp, msg->header.sync,
+					    tuple != 0);
 			break;
+		}
 		case IPROTO_CALL:
 			assert(msg->request.type == msg->header.type);
 			rmean_collect(rmean_box, msg->request.type, 1);
@@ -772,8 +796,11 @@ net_send_greeting(struct cmsg *m)
 	if (msg->close_connection) {
 		struct obuf *out = &msg->iobuf->out;
 		try {
-			sio_writev(con->output.fd, out->iov,
-				   obuf_iovcnt(out));
+			int64_t nwr = sio_writev(con->output.fd, out->iov,
+						 obuf_iovcnt(out));
+
+			/* Count statistics */
+			rmean_collect(rmean_net, RMEAN_NET_SENT, nwr);
 		} catch (Exception *e) {
 			e->log();
 		}
@@ -846,13 +873,27 @@ net_cord_f(va_list /* ap */)
 	evio_service_init(loop(), &binary, "binary",
 			  iproto_on_accept, NULL);
 
+
+	/* Init statistics counter */
+	rmean_net = rmean_new(rmean_net_strings, RMEAN_NET_LAST);
+
+	if (rmean_net == NULL)
+		tnt_raise(OutOfMemory,
+			  sizeof(*rmean_net) +
+			  RMEAN_NET_LAST * sizeof(stats),
+			  "rmean", "struct rmean");
+
+
 	cbus_join(&net_tx_bus, &net_pipe);
+
 	/*
 	 * Nothing to do in the fiber so far, the service
 	 * will take care of creating events for incoming
 	 * connections.
 	 */
 	fiber_yield();
+
+	rmean_delete(rmean_net);
 }
 
 /** Initialize the iproto subsystem and start network io thread */
@@ -873,6 +914,7 @@ iproto_init()
 	if (cord_costart(&net_cord, "iproto", net_cord_f, NULL))
 		panic("failed to initialize iproto thread");
 
+
 	cbus_join(&net_tx_bus, &tx_pipe);
 }
 
blob - 254e64528e8a069d0bffb747857ca3b44a93d2fd
blob + f1b62b7bd82c9e4e0f19188fe489c2c6446e69d1
--- src/box/iproto_port.h
+++ src/box/iproto_port.h
@@ -57,7 +57,7 @@
  */
 struct iproto_port
 {
-	struct port_vtab *vtab;
+	struct port base;
 	/** Output buffer. */
 	struct obuf *buf;
 	/** Reply header. */
@@ -74,7 +74,7 @@ static inline void
 iproto_port_init(struct iproto_port *port, struct obuf *buf,
 		 uint64_t sync)
 {
-	port->vtab = &iproto_port_vtab;
+	port->base.vtab = &iproto_port_vtab;
 	port->buf = buf;
 	port->sync = sync;
 	port->found = 0;
blob - 8a5968b442b5196f33b60b4f96b66bcef7436c72
blob + 4d933247e20d690833af0ecf842aa5be0ef46fcd
--- src/box/lua/call.cc
+++ src/box/lua/call.cc
@@ -153,45 +153,7 @@ port_lua_table_create(struct port_lua *port, struct lu
 
 /* }}} */
 
-/**
- * The main extension provided to Lua by Tarantool/Box --
- * ability to call INSERT/UPDATE/SELECT/DELETE from within
- * a Lua procedure.
- *
- * This is a low-level API, and it expects
- * all arguments to be packed in accordance
- * with the binary protocol format (iproto
- * header excluded).
- *
- * Signature:
- * box.process(op_code, request)
- */
 static int
-lbox_process(lua_State *L)
-{
-	uint32_t op = lua_tointeger(L, 1); /* Get the first arg. */
-	size_t sz;
-	const char *req = luaL_checklstring(L, 2, &sz); /* Second arg. */
-	if (op == IPROTO_CALL) {
-		/*
-		 * We should not be doing a CALL from within a CALL.
-		 * To invoke one stored procedure from another, one must
-		 * do it in Lua directly. This deals with
-		 * infinite recursion, stack overflow and such.
-		 */
-		return luaL_error(L, "box.process(CALL, ...) is not allowed");
-	}
-	/* Capture all output into a Lua table. */
-	struct port_lua port_lua;
-	struct request request;
-	request_create(&request, op);
-	request_decode(&request, req, sz);
-	port_lua_table_create(&port_lua, L);
-	box_process(&request, (struct port *) &port_lua);
-	return 1;
-}
-
-static int
 lbox_select(lua_State *L)
 {
 	if (lua_gettop(L) != 6 || !lua_isnumber(L, 1) || !lua_isnumber(L, 2) ||
@@ -730,7 +692,6 @@ static const struct luaL_reg boxlib[] = {
 };
 
 static const struct luaL_reg boxlib_internal[] = {
-	{"process", lbox_process},
 	{"call_loadproc",  lbox_call_loadproc},
 	{"select", lbox_select},
 	{"insert", lbox_insert},
blob - 9c9994c5b2360daa746c1bffb0f132feed4a2f58
blob + 3275f86352fd844d5bc5687a19cab3321915a7c5
--- src/box/lua/schema.lua
+++ src/box/lua/schema.lua
@@ -289,7 +289,7 @@ end
 
 -- space format - the metadata about space fields
 function box.schema.space.format(id, format)
-    _space = box.space._space
+    local _space = box.space._space
     check_param(id, 'id', 'number')
     check_param(format, 'format', 'table')
     if format == nil then
blob - 7aa56497429d432b4280cad414d1269ee5b04016
blob + efdc1d3121de07c0fda45aa7239361c53ad7187b
--- src/box/lua/stat.cc
+++ src/box/lua/stat.cc
@@ -34,6 +34,8 @@
 #include <string.h>
 #include <rmean.h>
 #include <box/request.h>
+#include <cbus.h>
+#include <box/error.h>
 
 extern "C" {
 #include <lua.h>
@@ -91,7 +93,10 @@ static int
 lbox_stat_index(struct lua_State *L)
 {
 	luaL_checkstring(L, -1);
-	return rmean_foreach(rmean_box, seek_stat_item, L);
+	int res = rmean_foreach(rmean_box, seek_stat_item, L);
+	if (res)
+		return res;
+	return rmean_foreach(rmean_error, seek_stat_item, L);
 }
 
 static int
@@ -99,16 +104,38 @@ lbox_stat_call(struct lua_State *L)
 {
 	lua_newtable(L);
 	rmean_foreach(rmean_box, set_stat_item, L);
+	rmean_foreach(rmean_error, set_stat_item, L);
 	return 1;
 }
 
+static int
+lbox_stat_net_index(struct lua_State *L)
+{
+	luaL_checkstring(L, -1);
+	return rmean_foreach(rmean_net, seek_stat_item, L);
+}
+
+static int
+lbox_stat_net_call(struct lua_State *L)
+{
+	lua_newtable(L);
+	rmean_foreach(rmean_net, set_stat_item, L);
+	return 1;
+}
+
 static const struct luaL_reg lbox_stat_meta [] = {
 	{"__index", lbox_stat_index},
 	{"__call",  lbox_stat_call},
 	{NULL, NULL}
 };
 
-/** Initialize bos.stat package. */
+static const struct luaL_reg lbox_stat_net_meta [] = {
+	{"__index", lbox_stat_net_index},
+	{"__call",  lbox_stat_net_call},
+	{NULL, NULL}
+};
+
+/** Initialize box.stat package. */
 void
 box_lua_stat_init(struct lua_State *L)
 {
@@ -121,7 +148,14 @@ box_lua_stat_init(struct lua_State *L)
 	lua_newtable(L);
 	luaL_register(L, NULL, lbox_stat_meta);
 	lua_setmetatable(L, -2);
-
 	lua_pop(L, 1); /* stat module */
+
+
+	luaL_register_module(L, "box.stat.net", statlib);
+
+	lua_newtable(L);
+	luaL_register(L, NULL, lbox_stat_net_meta);
+	lua_setmetatable(L, -2);
+	lua_pop(L, 1); /* stat net module */
 }
 
blob - 6fa312f0345618eb049e2fb9586280a63a8f87f6
blob + f0dee94b03e91f585782a34a0a31b74ac9d56e84
--- src/box/memtx_engine.cc
+++ src/box/memtx_engine.cc
@@ -112,16 +112,24 @@ struct MemtxSpace: public Handler {
 		/* do nothing */
 		/* engine->close(this); */
 	}
-	virtual void executeReplace(struct txn *txn, struct space *space,
-				    struct request *request, struct port *port);
-	virtual void executeDelete(struct txn *txn, struct space *space,
-				   struct request *request, struct port *port);
-	virtual void executeUpdate(struct txn *txn, struct space *space,
-				   struct request *request, struct port *port);
-	virtual void executeUpsert(struct txn *txn, struct space *space,
-				   struct request *request, struct port *port);
-	virtual void executeSelect(struct txn *txn, struct space *space,
-				   struct request *request, struct port *port);
+	virtual struct tuple *
+	executeReplace(struct txn *txn, struct space *space,
+		       struct request *request);
+	virtual struct tuple *
+	executeDelete(struct txn *txn, struct space *space,
+		      struct request *request);
+	virtual struct tuple *
+	executeUpdate(struct txn *txn, struct space *space,
+		      struct request *request);
+	virtual void
+	executeUpsert(struct txn *txn, struct space *space,
+		      struct request *request);
+	virtual void
+	executeSelect(struct txn *, struct space *space,
+		      uint32_t index_id, uint32_t iterator,
+		      uint32_t offset, uint32_t limit,
+		      const char *key, const char * /* key_end */,
+		      struct port *port);
 	virtual void onAlter(Handler *old);
 public:
 	/**
@@ -217,9 +225,9 @@ dup_replace_mode(uint32_t op)
 	return op == IPROTO_INSERT ? DUP_INSERT : DUP_REPLACE_OR_INSERT;
 }
 
-void
+struct tuple *
 MemtxSpace::executeReplace(struct txn *txn, struct space *space,
-			   struct request *request, struct port *port)
+			   struct request *request)
 {
 	struct tuple *new_tuple = tuple_new(space->format, request->tuple,
 					    request->tuple_end);
@@ -233,12 +241,13 @@ MemtxSpace::executeReplace(struct txn *txn, struct spa
 	 * The reason is that any yield between port_add_tuple and port_eof
 	 * calls could lead to sending not finished response to iproto socket.
 	 */
-	port_add_tuple(port, new_tuple);
+	tuple_bless(new_tuple);
+	return new_tuple;
 }
 
-void
+struct tuple *
 MemtxSpace::executeDelete(struct txn *txn, struct space *space,
-			  struct request *request, struct port *port)
+			  struct request *request)
 {
 	/* Try to find the tuple by unique key. */
 	Index *pk = index_find_unique(space, request->index_id);
@@ -248,7 +257,7 @@ MemtxSpace::executeDelete(struct txn *txn, struct spac
 	struct tuple *old_tuple = pk->findByKey(key, part_count);
 	if (old_tuple == NULL) {
 		txn_commit_stmt(txn);
-		return;
+		return NULL;
 	}
 	TupleGuard old_guard(old_tuple);
 	this->replace(txn, space, old_tuple, NULL,
@@ -259,12 +268,13 @@ MemtxSpace::executeDelete(struct txn *txn, struct spac
 	 * The reason is that any yield between port_add_tuple and port_eof
 	 * calls could lead to sending not finished response to iproto socket.
 	 */
-	port_add_tuple(port, old_tuple);
+	tuple_bless(old_tuple);
+	return old_tuple;
 }
 
-void
+struct tuple *
 MemtxSpace::executeUpdate(struct txn *txn, struct space *space,
-			  struct request *request, struct port *port)
+			  struct request *request)
 {
 	/* Try to find the tuple by unique key. */
 	Index *pk = index_find_unique(space, request->index_id);
@@ -275,7 +285,7 @@ MemtxSpace::executeUpdate(struct txn *txn, struct spac
 
 	if (old_tuple == NULL) {
 		txn_commit_stmt(txn);
-		return;
+		return NULL;
 	}
 	TupleGuard old_guard(old_tuple);
 
@@ -295,14 +305,14 @@ MemtxSpace::executeUpdate(struct txn *txn, struct spac
 	 * The reason is that any yield between port_add_tuple and port_eof
 	 * calls could lead to sending not finished response to iproto socket.
 	 */
-	port_add_tuple(port, new_tuple);
+	tuple_bless(new_tuple);
+	return new_tuple;
 }
 
 void
 MemtxSpace::executeUpsert(struct txn *txn, struct space *space,
-			  struct request *request, struct port *port)
+			  struct request *request)
 {
-	(void)port;
 	Index *pk = index_find_unique(space, request->index_id);
 	/* Try to find the tuple by primary key. */
 	const char *key = request->key;
@@ -344,21 +354,20 @@ MemtxSpace::onAlter(Handler *old)
 }
 
 void
-MemtxSpace::executeSelect(struct txn * txn, struct space *space,
-			  struct request *request, struct port *port)
+MemtxSpace::executeSelect(struct txn *, struct space *space,
+			  uint32_t index_id, uint32_t iterator,
+			  uint32_t offset, uint32_t limit,
+			  const char *key, const char * /* key_end */,
+			  struct port *port)
 {
-	(void) txn;
-	MemtxIndex *index = (MemtxIndex *)index_find(space, request->index_id);
+	MemtxIndex *index = (MemtxIndex *) index_find(space, index_id);
 
 	ERROR_INJECT_EXCEPTION(ERRINJ_TESTING);
 
 	uint32_t found = 0;
-	uint32_t offset = request->offset;
-	uint32_t limit = request->limit;
-	if (request->iterator >= iterator_type_MAX)
+	if (iterator >= iterator_type_MAX)
 		tnt_raise(IllegalParams, "Invalid iterator type");
-	enum iterator_type type = (enum iterator_type) request->iterator;
-	const char *key = request->key;
+	enum iterator_type type = (enum iterator_type) iterator;
 
 	uint32_t part_count = key ? mp_decode_array(&key) : 0;
 	key_validate(index->key_def, type, key, part_count);
blob - a1bd31849ab9558084a966171cea9a1176bc4bec
blob + 46da90df7a120473abf3497270b44b352502fbd0
--- src/box/request.cc
+++ src/box/request.cc
@@ -54,51 +54,6 @@ request_create(struct request *request, uint32_t type)
 }
 
 void
-process_rw(struct request *request, struct port *port)
-{
-	assert(iproto_type_is_dml(request->type));
-	rmean_collect(rmean_box, request->type, 1);
-	try {
-		struct space *space = space_cache_find(request->space_id);
-		struct txn *txn;
-		if (request->type == IPROTO_SELECT) {
-			txn = in_txn();
-			access_check_space(space, PRIV_R);
-		} else {
-			txn = txn_begin_stmt(request, space);
-			access_check_space(space, PRIV_W);
-		}
-		switch (request->type) {
-		case IPROTO_SELECT:
-			space->handler->executeSelect(txn, space, request, port);
-			if (txn == NULL) {
-				/* no txn is created, so simply collect garbage here */
-				fiber_gc();
-			}
-			break;
-		case IPROTO_INSERT:
-		case IPROTO_REPLACE:
-			space->handler->executeReplace(txn, space, request, port);
-			break;
-		case IPROTO_UPDATE:
-			space->handler->executeUpdate(txn, space, request, port);
-			break;
-		case IPROTO_DELETE:
-			space->handler->executeDelete(txn, space, request, port);
-			break;
-		case IPROTO_UPSERT:
-			space->handler->executeUpsert(txn, space, request, port);
-			break;
-		default: break;
-		}
-		port_eof(port);
-	} catch (Exception *e) {
-		txn_rollback_stmt();
-		throw;
-	}
-}
-
-void
 request_decode(struct request *request, const char *data, uint32_t len)
 {
 	const char *end = data + len;
blob - 5732e135e08ec66674bec9e0e424ec2e25c380b6
blob + 3028cb56ea9edc52bf1cdddb6459caa50ef645e6
--- src/box/request.h
+++ src/box/request.h
@@ -82,9 +82,6 @@ void
 request_create(struct request *request, uint32_t code);
 
 void
-process_rw(struct request *request, struct port *port);
-
-void
 request_decode(struct request *request, const char *data, uint32_t len);
 
 int
blob - 89096a7362aa29485ec113eb6e7986e2de35fa40
blob + 5c010c87c85c939584f959a0188e11189ffef393
--- src/box/schema.h
+++ src/box/schema.h
@@ -41,18 +41,23 @@ enum {
 	BOX_SCHEMA_ID = 272,
 	/** Space id of _space. */
 	BOX_SPACE_ID = 280,
+	/** Space id of _vspace view. */
 	BOX_VSPACE_ID = 281,
 	/** Space id of _index. */
 	BOX_INDEX_ID = 288,
+	/** Space id of _vindex view. */
 	BOX_VINDEX_ID = 289,
 	/** Space id of _func. */
 	BOX_FUNC_ID = 296,
+	/** Space id of _vfunc view. */
 	BOX_VFUNC_ID = 297,
 	/** Space id of _user. */
 	BOX_USER_ID = 304,
+	/** Space id of _vuser view. */
 	BOX_VUSER_ID = 305,
 	/** Space id of _priv. */
 	BOX_PRIV_ID = 312,
+	/** Space id of _vpriv view. */
 	BOX_VPRIV_ID = 313,
 	/** Space id of _cluster. */
 	BOX_CLUSTER_ID = 320,
blob - dc8595ba38aaf755bcdbf14302d8c6577a9d9941
blob + 561f528217913f7d6d78baa65f0250e96d1ffe33
--- src/box/sophia_engine.cc
+++ src/box/sophia_engine.cc
@@ -79,24 +79,23 @@ void sophia_info(void (*cb)(const char*, const char*, 
 
 struct SophiaSpace: public Handler {
 	SophiaSpace(Engine*);
-	virtual void
+	virtual struct tuple *
 	executeReplace(struct txn*, struct space *space,
-	               struct request *request, struct port *port);
-	virtual void
+	               struct request *request);
+	virtual struct tuple *
 	executeDelete(struct txn*, struct space *space,
-	              struct request *request, struct port *port);
-	virtual void
+	              struct request *request);
+	virtual struct tuple *
 	executeUpdate(struct txn*, struct space *space,
-	              struct request *request, struct port *port);
+	              struct request *request);
 	virtual void
 	executeUpsert(struct txn*, struct space *space,
-	              struct request *request, struct port *port);
+	              struct request *request);
 };
 
-void
+struct tuple *
 SophiaSpace::executeReplace(struct txn *txn, struct space *space,
-                            struct request *request,
-                            struct port * /* port */)
+                            struct request *request)
 {
 	space_validate_tuple_raw(space, request->tuple);
 	tuple_field_count_validate(space->format, request->tuple);
@@ -116,12 +115,12 @@ SophiaSpace::executeReplace(struct txn *txn, struct sp
 		(SophiaIndex *)index_find(space, 0);
 	index->replace_or_insert(request->tuple, request->tuple_end, mode);
 	txn_commit_stmt(txn);
+	return NULL;
 }
 
-void
+struct tuple *
 SophiaSpace::executeDelete(struct txn *txn, struct space *space,
-                           struct request *request,
-                           struct port* /* port */)
+                           struct request *request)
 {
 	SophiaIndex *index = (SophiaIndex *)index_find(space, request->index_id);
 	const char *key = request->key;
@@ -129,12 +128,12 @@ SophiaSpace::executeDelete(struct txn *txn, struct spa
 	primary_key_validate(index->key_def, key, part_count);
 	index->remove(key);
 	txn_commit_stmt(txn);
+	return NULL;
 }
 
-void
+struct tuple *
 SophiaSpace::executeUpdate(struct txn *txn, struct space *space,
-                           struct request *request,
-                           struct port* /* port */)
+                           struct request *request)
 {
 	/* Try to find the tuple by unique key */
 	SophiaIndex *index = (SophiaIndex *)index_find(space, request->index_id);
@@ -145,7 +144,7 @@ SophiaSpace::executeUpdate(struct txn *txn, struct spa
 
 	if (old_tuple == NULL) {
 		txn_commit_stmt(txn);
-		return;
+		return NULL;
 	}
 	TupleGuard old_guard(old_tuple);
 
@@ -166,12 +165,12 @@ SophiaSpace::executeUpdate(struct txn *txn, struct spa
 	                         new_tuple->data + new_tuple->bsize,
 	                         DUP_REPLACE);
 	txn_commit_stmt(txn);
+	return NULL;
 }
 
 void
 SophiaSpace::executeUpsert(struct txn *txn, struct space *space,
-                           struct request *request,
-                           struct port* /* port */)
+                           struct request *request)
 {
 	SophiaIndex *index = (SophiaIndex *)index_find(space, request->index_id);
 	const char *key = request->key;
blob - c80554afacd229aefe1ba5b6ab770a0e8899e177
blob + 315f6eb410d853d31908cfa9bb678a7b27ce784d
--- src/box/tuple.h
+++ src/box/tuple.h
@@ -143,8 +143,7 @@ tuple_format_ref(struct tuple_format *format, int coun
 };
 
 /**
- * An atom of Tarantool/Box storage. Consists of a list of fields.
- * The first field is always the primary key.
+ * An atom of Tarantool storage. Represents MsgPack Array.
  */
 struct tuple
 {
@@ -183,6 +182,8 @@ tuple_alloc(struct tuple_format *format, size_t size);
  * tuple->refs is 0.
  *
  * Throws an exception if tuple format is incorrect.
+ *
+ * \sa box_tuple_new()
  */
 struct tuple *
 tuple_new(struct tuple_format *format, const char *data, const char *end);
@@ -554,12 +555,7 @@ void
 tuple_to_obuf(struct tuple *tuple, struct obuf *buf);
 
 /**
- * Store tuple fields in the memory buffer.
- * \retval -1 on error.
- * \retval number of bytes written on success.
- * Upon successful return, the function returns the number of bytes written.
- * If buffer size is not enough then the return value is the number of bytes
- * which would have been written if enough space had been available.
+ * \copydoc box_tuple_to_buf()
  */
 ssize_t
 tuple_to_buf(const struct tuple *tuple, char *buf, size_t size);
@@ -582,52 +578,203 @@ tuple_end_snapshot();
 /** \cond public */
 typedef struct tuple_format box_tuple_format_t;
 
+/**
+ * Tuple Format.
+ *
+ * Each Tuple has associated format (class). Default format is used to
+ * create tuples which are not attach to any particular space.
+ */
 API_EXPORT box_tuple_format_t *
 box_tuple_format_default(void);
 
+/**
+ * Tuple
+ */
 typedef struct tuple box_tuple_t;
 
+/**
+ * Allocate and initialize a new tuple from a raw MsgPack Array data.
+ *
+ * \param format tuple format.
+ * Use box_tuple_format_default() to create space-independent tuple.
+ * \param data tuple data in MsgPack Array format ([field1, field2, ...]).
+ * \param end the end of \a data
+ * \retval NULL on out of memory
+ * \retval tuple otherwise
+ * \pre data, end is valid MsgPack Array
+ * \sa \code box.tuple.new(data) \endcode
+ */
 API_EXPORT box_tuple_t *
 box_tuple_new(box_tuple_format_t *format, const char *data, const char *end);
 
+/**
+ * Increase the reference counter of tuple.
+ *
+ * Tuples are reference counted. All functions that return tuples guarantee
+ * that the last returned tuple is refcounted internally until the next
+ * call to API function that yields or returns another tuple.
+ *
+ * You should increase the reference counter before taking tuples for long
+ * processing in your code. Such tuples will not be garbage collected even
+ * if another fiber remove they from space. After processing please
+ * decrement the reference counter using box_tuple_unref(), otherwise the
+ * tuple will leak.
+ *
+ * \param tuple a tuple
+ * \retval -1 on error (check box_error_last())
+ * \retval 0 on success
+ * \sa box_tuple_unref()
+ */
 API_EXPORT int
 box_tuple_ref(box_tuple_t *tuple);
 
+/**
+ * Decrease the reference counter of tuple.
+ *
+ * \param tuple a tuple
+ * \sa box_tuple_ref()
+ */
 API_EXPORT void
 box_tuple_unref(box_tuple_t *tuple);
 
+/**
+ * Return the number of fields in tuple (the size of MsgPack Array).
+ * \param tuple a tuple
+ */
 API_EXPORT uint32_t
 box_tuple_field_count(const box_tuple_t *tuple);
 
+/**
+ * Return the number of bytes used to store internal tuple data (MsgPack Array).
+ * \param tuple a tuple
+ */
 API_EXPORT size_t
 box_tuple_bsize(const box_tuple_t *tuple);
 
+/**
+ * Dump raw MsgPack data to the memory byffer \a buf of size \a size.
+ *
+ * Store tuple fields in the memory buffer.
+ * \retval -1 on error.
+ * \retval number of bytes written on success.
+ * Upon successful return, the function returns the number of bytes written.
+ * If buffer size is not enough then the return value is the number of bytes
+ * which would have been written if enough space had been available.
+ */
 API_EXPORT ssize_t
 box_tuple_to_buf(const box_tuple_t *tuple, char *buf, size_t size);
 
+/**
+ * Return the associated format.
+ * \param tuple tuple
+ * \return tuple_format
+ */
 API_EXPORT box_tuple_format_t *
 box_tuple_format(const box_tuple_t *tuple);
 
-API_EXPORT const char *
-box_tuple_field(const box_tuple_t *tuple, uint32_t i);
+/**
+ * Return the raw tuple field in MsgPack format.
+ *
+ * The buffer is valid until next call to box_tuple_* functions.
+ *
+ * \param tuple a tuple
+ * \param field_id zero-based index in MsgPack array.
+ * \retval NULL if i >= box_tuple_field_count(tuple)
+ * \retval msgpack otherwise
+ */
+API_EXPORT const char *
+box_tuple_field(const box_tuple_t *tuple, uint32_t field_id);
 
+/**
+ * Tuple iterator
+ */
 typedef struct tuple_iterator box_tuple_iterator_t;
 
+/**
+ * Allocate and initialize a new tuple iterator. The tuple iterator
+ * allow to iterate over fields at root level of MsgPack array.
+ *
+ * Example:
+ * \code
+ * box_tuple_iterator *it = box_tuple_iterator(tuple);
+ * if (it == NULL) {
+ *      // error handling using box_error_last()
+ * }
+ * const char *field;
+ * while (field = box_tuple_next(it)) {
+ *      // process raw MsgPack data
+ * }
+ *
+ * // rewind iterator to first position
+ * box_tuple_rewind(it);
+ * assert(box_tuple_position(it) == 0);
+ *
+ * // rewind iterator to first position
+ * field = box_tuple_seek(it, 3);
+ * assert(box_tuple_position(it) == 4);
+ *
+ * box_iterator_free(it);
+ * \endcode
+ *
+ * \post box_tuple_position(it) == 0
+ */
 API_EXPORT box_tuple_iterator_t *
 box_tuple_iterator(box_tuple_t *tuple);
 
+/**
+ * Destroy and free tuple iterator
+ */
 API_EXPORT void
 box_tuple_iterator_free(box_tuple_iterator_t *it);
 
+/**
+ * Return zero-based next position in iterator.
+ * That is, this function return the field id of field that will be
+ * returned by the next call to box_tuple_next(it). Returned value is zero
+ * after initialization or rewind and box_tuple_field_count(tuple)
+ * after the end of iteration.
+ *
+ * \param it tuple iterator
+ * \returns position.
+ */
 API_EXPORT uint32_t
 box_tuple_position(box_tuple_iterator_t *it);
 
+/**
+ * Rewind iterator to the initial position.
+ *
+ * \param it tuple iterator
+ * \post box_tuple_position(it) == 0
+ */
 API_EXPORT void
 box_tuple_rewind(box_tuple_iterator_t *it);
 
+/**
+ * Seek the tuple iterator.
+ *
+ * The returned buffer is valid until next call to box_tuple_* API.
+ * Requested field_no returned by next call to box_tuple_next(it).
+ *
+ * \param it tuple iterator
+ * \param field_no field no - zero-based position in MsgPack array.
+ * \post box_tuple_position(it) == field_no if returned value is not NULL
+ * \post box_tuple_position(it) == box_tuple_field_count(tuple) if returned
+ * value is NULL.
+ */
 API_EXPORT const char *
 box_tuple_seek(box_tuple_iterator_t *it, uint32_t field_no);
 
+/**
+ * Return the next tuple field from tuple iterator.
+ * The returned buffer is valid until next call to box_tuple_* API.
+ *
+ * \param it tuple iterator.
+ * \retval NULL if there are no more fields.
+ * \retval MsgPack otherwise
+ * \pre box_tuple_position(it) is zerod-based id of returned field
+ * \post box_tuple_position(it) == box_tuple_field_count(tuple) if returned
+ * value is NULL.
+ */
 API_EXPORT const char *
 box_tuple_next(box_tuple_iterator_t *it);
 
blob - 9442981ad2310e5bfbf3db13de211987413b976b
blob + 418415858b610467c8e721fd8f5d4ee6ae52bc6c
--- src/box/txn.h
+++ src/box/txn.h
@@ -166,6 +166,11 @@ txn_stmt(struct txn *txn)
 /** \cond public */
 
 /**
+ * Begin a transaction in the current fiber.
+ *
+ * A transaction is attached to caller fiber, therefore one fiber can have
+ * only one active transaction.
+ *
  * @retval 0 - success
  * @retval -1 - failed, perhaps a transaction has already been
  * started
@@ -173,9 +178,18 @@ txn_stmt(struct txn *txn)
 API_EXPORT int
 box_txn_begin(void);
 
+/**
+ * Commit the current transaction.
+ * @retval 0 - success
+ * @retval -1 - failed, perhaps a disk write failure.
+ * started
+ */
 API_EXPORT int
 box_txn_commit(void);
 
+/**
+ * Rollback the current transaction.
+ */
 API_EXPORT void
 box_txn_rollback(void);
 
@@ -184,7 +198,7 @@ box_txn_rollback(void);
  * The memory is automatically deallocated when the transaction
  * is committed or rolled back.
  *
- * @retval 0  out of memory
+ * @retval NULL out of memory
  */
 API_EXPORT void *
 box_txn_alloc(size_t size);
blob - 131a7f9d57c4400423d5d934d71bb7daae0c5f49
blob + fd4a3f002ddc8615e47a425aec3277f2b9b372fd
--- src/cbus.cc
+++ src/cbus.cc
@@ -31,6 +31,14 @@
 #include "cbus.h"
 #include "scoped_guard.h"
 
+struct rmean *rmean_net = NULL;
+const char *rmean_net_strings[RMEAN_NET_LAST] = {
+	"EVENTS",
+	"LOCKS",
+	"RECEIVED",
+	"SENT"
+};
+
 static void
 cbus_flush_cb(ev_loop * /* loop */, struct ev_async *watcher,
 	      int /* events */);
@@ -183,9 +191,14 @@ cbus_flush_cb(ev_loop * /* loop */, struct ev_async *w
 	STAILQ_CONCAT(&peer->output, &peer->pipe);
 	cbus_unlock(pipe->bus);
 
+
 	pipe->n_input = 0;
-	if (pipe_was_empty)
+	if (pipe_was_empty) {
+		/* Count statistics */
+		rmean_collect(rmean_net, RMEAN_NET_EVENTS, 1);
+
 		ev_async_send(pipe->consumer, &pipe->fetch_output);
+	}
 	if (peer_output_was_empty && !STAILQ_EMPTY(&peer->output))
 		ev_feed_event(peer->consumer, &peer->fetch_output, EV_CUSTOM);
 }
@@ -200,6 +213,8 @@ cpipe_peek_impl(struct cpipe *pipe)
 	assert(peer->producer == loop());
 
 	bool peer_pipe_was_empty = false;
+
+
 	cbus_lock(pipe->bus);
 	STAILQ_CONCAT(&pipe->output, &pipe->pipe);
 	if (! STAILQ_EMPTY(&peer->input)) {
@@ -209,8 +224,12 @@ cpipe_peek_impl(struct cpipe *pipe)
 	cbus_unlock(pipe->bus);
 	peer->n_input = 0;
 
-	if (peer_pipe_was_empty)
+	if (peer_pipe_was_empty) {
+		/* Count statistics */
+		rmean_collect(rmean_net, RMEAN_NET_EVENTS, 1);
+
 		ev_async_send(peer->consumer, &peer->fetch_output);
+	}
 	return STAILQ_FIRST(&pipe->output);
 }
 
blob - 4985a36e3e1b15cc9d28d6d339846b22e52c9464
blob + 3b717d369950e79ea007dd7211e3a98b420ab343
--- src/cbus.h
+++ src/cbus.h
@@ -32,6 +32,7 @@
  */
 #include "fiber.h"
 #include "coio.h"
+#include "rmean.h"
 
 /** cbus, cmsg - inter-cord bus and messaging */
 
@@ -39,6 +40,20 @@ struct cmsg;
 struct cpipe;
 typedef void (*cmsg_f)(struct cmsg *);
 
+/** rmean_net - network statistics (iproto & cbus) */
+extern struct rmean *rmean_net;
+
+enum rmean_net_name {
+	RMEAN_NET_EVENTS,
+	RMEAN_NET_LOCKS,
+	RMEAN_NET_RECEIVED,
+	RMEAN_NET_SENT,
+
+	RMEAN_NET_LAST
+};
+
+extern const char *rmean_net_strings[RMEAN_NET_LAST];
+
 /**
  * One hop in a message travel route.  A message may need to be
  * delivered to many destinations before it can be dispensed with.
@@ -364,6 +379,10 @@ cbus_join(struct cbus *bus, struct cpipe *pipe);
 static inline void
 cbus_lock(struct cbus *bus)
 {
+	/* Count statistics */
+	if (rmean_net)
+		rmean_collect(rmean_net, RMEAN_NET_LOCKS, 1);
+
 	tt_pthread_mutex_lock(&bus->mutex);
 }
 
blob - 627df66c3da73e1e87625f2f4272cfede66a6150
blob + 47ed33fa7594667bee4c9f9f59a13322184b6834
--- src/coeio.cc
+++ src/coeio.cc
@@ -187,31 +187,6 @@ coio_on_call(eio_req *req)
 	req->result = task->call_cb(task->ap);
 }
 
-/**
- * Create new eio task with specified function and
- * arguments. Yield and wait until the task is complete
- * or a timeout occurs.
- *
- * This function doesn't throw exceptions to avoid double error
- * checking: in most cases it's also necessary to check the return
- * value of the called function and perform necessary actions. If
- * func sets errno, the errno is preserved across the call.
- *
- * @retval -1 and errno = ENOMEM if failed to create a task
- * @retval the function return (errno is preserved).
- *
- * @code
- *	static ssize_t openfile_cb(va_list ap)
- *	{
- *	         const char *filename = va_arg(ap);
- *	         int flags = va_arg(ap);
- *	         return open(filename, flags);
- *	}
- *
- *	 if (coio_call(openfile_cb, 0.10, "/tmp/file", 0) == -1)
- *		// handle errors.
- *	...
- */
 ssize_t
 coio_call(ssize_t (*func)(va_list ap), ...)
 {
blob - 1523f4aac9ea9a61aca69c168aa68b09c400e2ac
blob + 374d6db4c5cb1080e64c84985ec697c8712125b2
--- src/coeio.h
+++ src/coeio.h
@@ -84,11 +84,42 @@ coio_task(struct coio_task *task, coio_task_cb func,
 	  coio_task_timeout_cb on_timeout, double timeout);
 
 /** \cond public */
+
+/**
+ * Create new eio task with specified function and
+ * arguments. Yield and wait until the task is complete
+ * or a timeout occurs.
+ *
+ * This function doesn't throw exceptions to avoid double error
+ * checking: in most cases it's also necessary to check the return
+ * value of the called function and perform necessary actions. If
+ * func sets errno, the errno is preserved across the call.
+ *
+ * @retval -1 and errno = ENOMEM if failed to create a task
+ * @retval the function return (errno is preserved).
+ *
+ * @code
+ *	static ssize_t openfile_cb(va_list ap)
+ *	{
+ *	         const char *filename = va_arg(ap);
+ *	         int flags = va_arg(ap);
+ *	         return open(filename, flags);
+ *	}
+ *
+ *	if (coio_call(openfile_cb, 0.10, "/tmp/file", 0) == -1)
+ *		// handle errors.
+ *	...
+ * @endcode
+ */
 ssize_t
 coio_call(ssize_t (*func)(va_list ap), ...);
 
 struct addrinfo;
 
+/**
+ * Fiber-friendly version of getaddrinfo(3).
+ * \sa getaddrinfo().
+ */
 int
 coio_getaddrinfo(const char *host, const char *port,
 		 const struct addrinfo *hints, struct addrinfo **res,
blob - 449ce3e4b8d730c6270b9f1b22c51b9714231d5b
blob + 7b392020a111b86bf20ae7f941046cb793d56958
--- src/latch.h
+++ src/latch.h
@@ -120,8 +120,7 @@ latch_lock_timeout(struct latch *l, ev_tstamp timeout)
 }
 
 /**
- * Lock a latch (no timeout). Waits indefinitely until
- * the current fiber can gain access to the latch.
+ * \copydoc box_latch_lock
  */
 static inline void
 latch_lock(struct latch *l)
@@ -130,9 +129,7 @@ latch_lock(struct latch *l)
 }
 
 /**
- * Try to lock a latch. Return immediately if the latch is locked.
- * @retval 0 - success
- * @retval 1 - the latch is locked.
+ * \copydoc box_latch_trylock
  */
 static inline int
 latch_trylock(struct latch *l)
@@ -141,8 +138,7 @@ latch_trylock(struct latch *l)
 }
 
 /**
- * Unlock a latch. The fiber calling this function must
- * own the latch.
+ * \copydoc box_latch_unlock
  */
 static inline void
 latch_unlock(struct latch *l)
@@ -157,26 +153,52 @@ latch_unlock(struct latch *l)
 }
 
 /** \cond public */
+
 /**
- * API of C stored function.
+ * A lock for cooperative multitasking environment
  */
-
 typedef struct box_latch box_latch_t;
 
+/**
+ * Allocate and initialize the new latch.
+ * \returns latch
+ */
 API_EXPORT box_latch_t*
 box_latch_new(void);
 
+/**
+ * Destroy and free the latch.
+ * \param latch latch
+ */
 API_EXPORT void
-box_latch_delete(box_latch_t* bl);
+box_latch_delete(box_latch_t *latch);
 
+/**
+* Lock a latch. Waits indefinitely until the current fiber can gain access to
+* the latch.
+*
+* \param latch a latch
+*/
 API_EXPORT void
-box_latch_lock(box_latch_t* bl);
+box_latch_lock(box_latch_t *latch);
 
+/**
+ * Try to lock a latch. Return immediately if the latch is locked.
+ * \param latch a latch
+ * \retval 0 - success
+ * \retval 1 - the latch is locked.
+ */
 API_EXPORT int
-box_latch_trylock(box_latch_t* bl);
+box_latch_trylock(box_latch_t *latch);
 
+/**
+ * Unlock a latch. The fiber calling this function must
+ * own the latch.
+ *
+ * \param latch a ltach
+ */
 API_EXPORT void
-box_latch_unlock(box_latch_t* bl);
+box_latch_unlock(box_latch_t *latch);
 
 /** \endcond public */
 
blob - 51d0f603d7433f5df30cbbb26d4804c5ec58165e
blob + 8c23809dc7e4184b17ac440e30a04569d289d809
--- src/lua/utils.cc
+++ src/lua/utils.cc
@@ -669,7 +669,7 @@ luaL_register_module(struct lua_State *L, const char *
 	luaL_register(L, NULL, methods);
 }
 
-int
+void
 luaL_pushuint64(struct lua_State *L, uint64_t val)
 {
 	if (val < (1ULL << 52)) {
@@ -680,10 +680,9 @@ luaL_pushuint64(struct lua_State *L, uint64_t val)
 		*(uint64_t *) luaL_pushcdata(L, CTID_UINT64,
 					     sizeof(uint64_t)) = val;
 	}
-	return 1;
 }
 
-int
+void
 luaL_pushint64(struct lua_State *L, int64_t val)
 {
 	if (val > (-1LL << 52) && val < (1LL << 52)) {
@@ -694,7 +693,6 @@ luaL_pushint64(struct lua_State *L, int64_t val)
 		*(int64_t *) luaL_pushcdata(L, CTID_INT64,
 					    sizeof(int64_t)) = val;
 	}
-	return 1;
 }
 
 static inline int
blob - 2cfce28b5f7da95ba88878372996cfcccfc57f48
blob + a8420c02da62d308b8e0dd9a6f66c9ee6f10dd24
--- src/lua/utils.h
+++ src/lua/utils.h
@@ -106,7 +106,7 @@ luaL_ctypeid(struct lua_State *L, const char *ctypenam
 /**
 * @brief Declare symbols for FFI
 * @param L Lua State
-* @param what C definitions
+* @param ctypename C definitions, e.g "struct stat"
 * @sa ffi.cdef(def)
 * @retval 0 on success
 * @retval LUA_ERRRUN, LUA_ERRMEM, LUA_ERRERR otherwise
@@ -372,19 +372,21 @@ luaL_register_module(struct lua_State *L, const char *
 /** \cond public */
 
 /**
- * push uint64_t to Lua stack
+ * Push uint64_t onto the stack
  *
  * @param L is a Lua State
  * @param val is a value to push
- *
- */
-LUA_API int
+ */
+LUA_API void
 luaL_pushuint64(struct lua_State *L, uint64_t val);
 
 /**
- * @copydoc luaL_pushnumber64
+ * Push int64_t onto the stack
+ *
+ * @param L is a Lua State
+ * @param val is a value to push
  */
-LUA_API int
+LUA_API void
 luaL_pushint64(struct lua_State *L, int64_t val);
 
 /**
blob - b16c20a78b5ac9a32377a94a9e97415a8cbe9532
blob + 0720bf01749f66c56cf92a1b00ce969d04a153bc
--- src/rmean.cc
+++ src/rmean.cc
@@ -38,6 +38,8 @@
 void
 rmean_collect(struct rmean *rmean, size_t name, int64_t value)
 {
+	assert(name < rmean->stats_n);
+
 	rmean->stats[name].value[0] += value;
 	rmean->stats[name].total += value;
 }
@@ -70,8 +72,6 @@ rmean_age(ev_loop * /* loop */,
 	 ev_timer *timer, int /* events */)
 {
 	struct rmean *rmean = (struct rmean *) timer->data;
-	if (rmean->stats == NULL)
-		return;
 
 	for (size_t i = 0; i < rmean->stats_n; i++) {
 		if (rmean->stats[i].name == NULL)
@@ -96,10 +96,11 @@ struct rmean *
 rmean_new(const char **name, size_t n)
 {
 	struct rmean *rmean = (struct rmean *) realloc(NULL,
-				sizeof(rmean) + sizeof(stats) * (n + 1));
+				sizeof(struct rmean) +
+				sizeof(struct stats) * n);
 	if (rmean == NULL)
 		return NULL;
-	memset(rmean, 0, sizeof(rmean) + sizeof(stats) * n);
+	memset(rmean, 0, sizeof(struct rmean) + sizeof(struct stats) * n);
 	rmean->stats_n = n;
 	rmean->timer.data = (void *)rmean;
 	ev_timer_init(&rmean->timer, rmean_age, 0, 1.);
@@ -116,10 +117,10 @@ rmean_new(const char **name, size_t n)
 void
 rmean_delete(struct rmean *rmean)
 {
-	if (rmean) {
-		ev_timer_stop(loop(), &rmean->timer);
-		free(rmean);
-	}
+
+	ev_timer_stop(loop(), &rmean->timer);
+	free(rmean);
+	rmean = 0;
 }
 
 void
blob - 5b980b620c06d03694ef658ca9a5b4de54203275
blob + b6dd5848a933aa40a14abf8775a19ffcd094a037
--- src/rmean.h
+++ src/rmean.h
@@ -39,6 +39,7 @@
 
 #define PERF_SECS 5
 
+
 struct stats {
 	const char *name;
 	int64_t value[PERF_SECS + 1];
blob - d3241d4ac5945cd2a38ca0d7daa62fbfc7984e1f
blob + bb32329b5fff985e19fb8400a95c636858d6a86a
--- src/say.h
+++ src/say.h
@@ -41,6 +41,7 @@ extern "C" {
 
 /** \cond public */
 
+/** Log levels */
 enum say_level {
 	S_FATAL,		/* do not this value use directly */
 	S_SYSERROR,
@@ -78,20 +79,50 @@ void vsay(int level, const char *filename, int line, c
 typedef void (*sayfunc_t)(int level, const char *filename, int line, const char *error,
                           const char *format, ...);
 
+/** Internal function used to implement say() macros */
 extern sayfunc_t _say __attribute__ ((format(printf, 5, 6)));
 
-#define say(level, ...) ({ _say(level, __FILE__, __LINE__, __VA_ARGS__); })
+/**
+ * Format and print a message to Tarantool log file.
+ *
+ * \param level (int) - log level (see enum \link say_level \endlink)
+ * \param format (const char * ) - printf()-like format string
+ * \param ... - format arguments
+ * \sa printf()
+ * \sa enum say_level
+ */
+#define say(level, format, ...) ({ _say(level, __FILE__, __LINE__, format, \
+	##__VA_ARGS__); })
 
+/**
+ * Format and print a message to Tarantool log file.
+ *
+ * \param format (const char * ) - printf()-like format string
+ * \param ... - format arguments
+ * \sa printf()
+ * \sa enum say_level
+ * Example:
+ * \code
+ * say_info("Some useful information: %s", status);
+ * \endcode
+ */
+#define say_error(format, ...) say(S_ERROR, NULL, format, ##__VA_ARGS__)
+/** \copydoc say_error() */
+#define say_crit(format, ...) say(S_CRIT, NULL, format, ##__VA_ARGS__)
+/** \copydoc say_error() */
+#define say_warn(format, ...) say(S_WARN, NULL, format, ##__VA_ARGS__)
+/** \copydoc say_error() */
+#define say_info(format, ...) say(S_INFO, NULL, format, ##__VA_ARGS__)
+/** \copydoc say_error() */
+#define say_debug(format, ...) say(S_DEBUG, NULL, format, ##__VA_ARGS__)
+/** \copydoc say_error(). */
+#define say_syserror(format, ...) say(S_SYSERROR, strerror(errno), format, \
+	##__VA_ARGS__)
+/** \endcond public */
+
 #define panic_status(status, ...)	({ say(S_FATAL, NULL, __VA_ARGS__); exit(status); })
 #define panic(...)			panic_status(EXIT_FAILURE, __VA_ARGS__)
 #define panic_syserror(...)		({ say(S_FATAL, strerror(errno), __VA_ARGS__); exit(EXIT_FAILURE); })
-#define say_syserror(...)		say(S_SYSERROR, strerror(errno), __VA_ARGS__)
-#define say_error(...)			say(S_ERROR, NULL, __VA_ARGS__)
-#define say_crit(...)			say(S_CRIT, NULL, __VA_ARGS__)
-#define say_warn(...)			say(S_WARN, NULL, __VA_ARGS__)
-#define say_info(...)			say(S_INFO, NULL, __VA_ARGS__)
-#define say_debug(...)			say(S_DEBUG, NULL, __VA_ARGS__)
-/** \endcond public */
 
 #if defined(__cplusplus)
 } /* extern "C" */
blob - fd3f035ee910bfcb8a75edd9478f7ac207ce4e6d
blob + 7f18dd749a3c03aeae4738e6ce0773b9c9c3ea5b
--- src/trivia/config.h.cmake
+++ src/trivia/config.h.cmake
@@ -5,14 +5,24 @@
  * config.h.cmake. Please do not modify.
  */
 /** \cond public */
-/*
- * A string with major-minor-patch-commit-id identifier of the
- * release.
+
+/**
+ * Package major version - 1 for 1.6.7
  */
-#define PACKAGE_VERSION "@PACKAGE_VERSION@"
 #define PACKAGE_VERSION_MAJOR @CPACK_PACKAGE_VERSION_MAJOR@
+/**
+ * Package minor version - 6 for 1.6.7
+ */
 #define PACKAGE_VERSION_MINOR @CPACK_PACKAGE_VERSION_MINOR@
+/**
+ * Package patch version - 7 for 1.6.7
+ */
 #define PACKAGE_VERSION_PATCH @CPACK_PACKAGE_VERSION_PATCH@
+/**
+ * A string with major-minor-patch-commit-id identifier of the
+ * release, e.g. 1.6.6-113-g8399d0e.
+ */
+#define PACKAGE_VERSION "@PACKAGE_VERSION@"
 
 /** \endcond public */
 
@@ -161,25 +171,32 @@
 
 /** \cond public */
 
-/*
- * predefined /etc directory prefix.
- */
+/** System configuration dir (e.g /etc) */
 #define SYSCONF_DIR "@CMAKE_INSTALL_SYSCONFDIR@"
+/** Install prefix (e.g. /usr) */
 #define INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@"
+/** Build type, e.g. Debug or Release */
 #define BUILD_TYPE "@CMAKE_BUILD_TYPE@"
+/** CMake build type signature, e.g. Linux-x86_64-Debug */
 #define BUILD_INFO "@TARANTOOL_BUILD@"
+/** Command line used to run CMake */
 #define BUILD_OPTIONS "cmake . @TARANTOOL_OPTIONS@"
+/** Pathes to C and CXX compilers */
 #define COMPILER_INFO "@CMAKE_C_COMPILER@ @CMAKE_CXX_COMPILER@"
+/** C compile flags used to build Tarantool */
 #define TARANTOOL_C_FLAGS "@TARANTOOL_C_FLAGS@"
+/** CXX compile flags used to build Tarantool */
 #define TARANTOOL_CXX_FLAGS "@TARANTOOL_CXX_FLAGS@"
 
-/*
- * Modules
- */
+/** A path to install *.lua module files */
 #define MODULE_LIBDIR "@MODULE_FULL_LIBDIR@"
+/** A path to install *.so / *.dylib module files */
 #define MODULE_LUADIR "@MODULE_FULL_LUADIR@"
+/** A path to Lua includes (the same directory where this file is contained) */
 #define MODULE_INCLUDEDIR "@MODULE_FULL_INCLUDEDIR@"
+/** A constant added to package.path in Lua to find *.lua module files */
 #define MODULE_LUAPATH "@MODULE_LUAPATH@"
+/** A constant added to package.cpath in Lua to find *.so module files */
 #define MODULE_LIBPATH "@MODULE_LIBPATH@"
 
 /** \endcond public */
blob - c6944e5820347a6f01f352c82df6e5eb0062dcba
blob + 2223de9a6c115c0fdef98d01a73fcd0d14dbacce
--- src/trivia/tarantool_header.h
+++ src/trivia/tarantool_header.h
@@ -14,6 +14,7 @@
  * Tarantool Module API
  */
 
+/** Extern modificator for all public functions */
 #if defined(__cplusplus)
 #define API_EXPORT extern "C" __attribute__ ((visibility ("default")))
 #else
blob - 4cf0ba46b990146170c9132c1c7185e857a2b6ac
blob + d1561644a8b321253ca40a3c821da0b034a71984
--- test/CMakeLists.txt
+++ test/CMakeLists.txt
@@ -4,7 +4,7 @@ include_directories(${CMAKE_BINARY_DIR}/src/trivia)
 function(build_module module files)
     add_library(${module} SHARED ${files})
     set_target_properties(${module} PROPERTIES PREFIX "")
-    add_dependencies(${module} rebuild_module_api)
+    add_dependencies(${module} api)
     if(TARGET_OS_DARWIN)
         set_target_properties(${module} PROPERTIES LINK_FLAGS "-undefined dynamic_lookup")
     endif(TARGET_OS_DARWIN)
blob - 24a3d4705eb3004b0849050db3f0f2dbce7285fb
blob + 0a4bd84df56668356e4478b72fc3fdfbbecf6c08
--- test/box/misc.result
+++ test/box/misc.result
@@ -146,6 +146,7 @@ t;
   - REPLACE
   - UPSERT
   - AUTH
+  - ERROR
   - UPDATE
   - total
   - rps
blob - f497bce2950ac9ed7361cea9924209f0c141f798
blob + 4ea136fccd32e24696f54bc0557b7b12015f7c29
--- test/box/stat.result
+++ test/box/stat.result
@@ -21,6 +21,10 @@ box.stat.SELECT.total
 ---
 - 0
 ...
+box.stat.ERROR.total
+---
+- 0
+...
 space = box.schema.space.create('tweedledum')
 ---
 ...
@@ -50,6 +54,15 @@ box.stat.REPLACE.total
 ...
 box.stat.SELECT.total
 ---
+- 2
+...
+-- check exceptions
+space:get('Impossible value')
+---
+- error: 'Supplied key type of part 0 does not match index part type: expected NUM'
+...
+box.stat.ERROR.total
+---
 - 1
 ...
 --# stop server default
@@ -75,6 +88,10 @@ box.stat.SELECT.total
 ---
 - 0
 ...
+box.stat.ERROR.total
+---
+- 0
+...
 -- cleanup
 box.space.tweedledum:drop()
 ---
blob - 5ff384a4d19608c9320d71fc863e4dd644691b95
blob + 7484c781cfd41becb98fb988e7ff48d8e4f8cc24
--- test/box/stat.test.lua
+++ test/box/stat.test.lua
@@ -6,6 +6,7 @@ box.stat.DELETE.total
 box.stat.UPDATE.total
 box.stat.REPLACE.total
 box.stat.SELECT.total
+box.stat.ERROR.total
 
 space = box.schema.space.create('tweedledum')
 index = space:create_index('primary', { type = 'hash' })
@@ -19,6 +20,10 @@ box.stat.UPDATE.total
 box.stat.REPLACE.total
 box.stat.SELECT.total
 
+-- check exceptions
+space:get('Impossible value')
+box.stat.ERROR.total
+
 --# stop server default
 --# start server default
 
@@ -28,6 +33,7 @@ box.stat.DELETE.total
 box.stat.UPDATE.total
 box.stat.REPLACE.total
 box.stat.SELECT.total
+box.stat.ERROR.total
 
 -- cleanup
 box.space.tweedledum:drop()
blob - /dev/null
blob + fa3ca518dc1e1bcc7e60df7182aead09184c664e (mode 644)
--- /dev/null
+++ test/box/stat_net.result
@@ -0,0 +1,57 @@
+-- clear statistics
+--# stop server default
+--# start server default
+box.stat.net.SENT -- zero
+---
+- total: 0
+  rps: 0
+...
+box.stat.net.RECEIVED -- zero
+---
+- total: 0
+  rps: 0
+...
+space = box.schema.space.create('tweedledum')
+---
+...
+box.schema.user.grant('guest','read,write,execute','universe')
+---
+...
+index = space:create_index('primary', { type = 'hash' })
+---
+...
+remote = require 'net.box'
+---
+...
+LISTEN = require('uri').parse(box.cfg.listen)
+---
+...
+cn = remote:new(LISTEN.host, LISTEN.service)
+---
+...
+cn.space.tweedledum:select() --small request
+---
+- []
+...
+box.stat.net.SENT.total > 0
+---
+- true
+...
+box.stat.net.RECEIVED.total > 0
+---
+- true
+...
+box.stat.net.EVENTS.total > 0
+---
+- true
+...
+box.stat.net.LOCKS.total > 0
+---
+- true
+...
+space:drop()
+---
+...
+cn:close()
+---
+...
blob - /dev/null
blob + 62eaf09ecf3425de172711d5671be0ef5680f751 (mode 644)
--- /dev/null
+++ test/box/stat_net.test.lua
@@ -0,0 +1,24 @@
+-- clear statistics
+--# stop server default
+--# start server default
+
+box.stat.net.SENT -- zero
+box.stat.net.RECEIVED -- zero
+
+space = box.schema.space.create('tweedledum')
+box.schema.user.grant('guest','read,write,execute','universe')
+index = space:create_index('primary', { type = 'hash' })
+remote = require 'net.box'
+
+LISTEN = require('uri').parse(box.cfg.listen)
+cn = remote:new(LISTEN.host, LISTEN.service)
+
+cn.space.tweedledum:select() --small request
+
+box.stat.net.SENT.total > 0
+box.stat.net.RECEIVED.total > 0
+box.stat.net.EVENTS.total > 0
+box.stat.net.LOCKS.total > 0
+
+space:drop()
+cn:close()
blob - 384fc7674ffa4fd93e752fe4cbf54a9ecb3bce75
blob + c86a062ad7d52fce7758d5b0d33995a342e1032e
--- test/unit/CMakeLists.txt
+++ test/unit/CMakeLists.txt
@@ -60,7 +60,8 @@ target_link_libraries(light.test small)
 add_executable(vclock.test vclock.cc unit.c
     ${CMAKE_SOURCE_DIR}/src/box/vclock.c
     ${CMAKE_SOURCE_DIR}/src/box/errcode.c
-    ${CMAKE_SOURCE_DIR}/src/box/error.cc)
+    ${CMAKE_SOURCE_DIR}/src/box/error.cc
+    ${CMAKE_SOURCE_DIR}/src/rmean.cc)
 target_link_libraries(vclock.test core small)
 add_executable(quota.test quota.cc unit.c)
 target_link_libraries(quota.test pthread)