commit df48d3456605a4c42d61dc2ae55dc12196678931 from: Konstantin Osipov date: Mon Aug 31 20:44:33 2015 UTC Merge remote-tracking branch 'origin/1.6' into gh-845-coeio-less-cord-cojoin 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 #include +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 #include #include +#include +#include extern "C" { #include @@ -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)