commit - 4a93f5baceb07caabfdfb803474932c5ab6aa473
commit + df48d3456605a4c42d61dc2ae55dc12196678931
blob - 36d748840d01bba90e3b5f6f72b69aa7a44e37ac
blob + cd2dcca7ddbf1b0076cdb2be49b1f7cf4ae4e596
--- .gitignore
+++ .gitignore
CPackConfig.cmake
CPackSourceConfig.cmake
Makefile
+Doxyfile.API
RPM
*.src.rpm
*.i386.rpm
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
#
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
# 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
# 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.
# 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
# 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
# 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.
# 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
# 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
# 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
+@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
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
${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
add_subdirectory(sphinx)
add_subdirectory(www)
endif()
-
-
blob - e0b651417f5fb7afa8959d578ae832bbec106d70
blob + 1fb237e2c508774994ba472a45c4728f579c1927
--- doc/sphinx/CMakeLists.txt
+++ doc/sphinx/CMakeLists.txt
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/")
"${PROJECT_SOURCE_DIR}/doc/sphinx"
"${SPHINX_HTML_DIR}"
COMMENT "Building HTML documentation with Sphinx"
+ DEPENDS api-doc
)
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
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
.. _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
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'
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
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
.. 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
'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
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
* **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
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
+-------------------------------------------------------------------------------
+ Module C API
+-------------------------------------------------------------------------------
+
+.. doxygenfile:: tarantool.h
+ :project: api
blob - 9025ef6a01d36bf5d070729255023a5f70cde2ff
blob + 44c3b83d16d216af1c0580ff899455ab15495fb1
--- doc/sphinx/reference/index.rst
+++ doc/sphinx/reference/index.rst
expirationd
shard
tarantool
+ capi
blob - 83fcd48238e3517e65fb5a51d7a9670bb495e311
blob + 1bafc7cacd52bfc5a805459cc841a191be5e4922
--- doc/www/content/newsite/index.yml
+++ doc/www/content/newsite/index.yml
- "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"
- "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
#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
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 */
assert(data <= buf + sizeof(buf));
req.tuple = buf;
req.tuple_end = data;
- process_rw(&req, &null_port);
+ process_rw(&req, NULL);
}
int
}
/** \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;
}
}
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 */
tuple_free();
port_free();
engine_shutdown();
+ rmean_delete(rmean_error);
rmean_delete(rmean_box);
}
}
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)
{
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();
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
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);
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,
/** \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
{
}
-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");
}
}
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
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
#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
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);
}
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
*/
#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:
};
/** \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
#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",
return NULL;
}
-size_t
+ssize_t
box_index_len(uint32_t space_id, uint32_t index_id)
{
try {
}
}
-size_t
+ssize_t
box_index_bsize(uint32_t space_id, uint32_t index_id)
{
try {
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
/** \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
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 */
/** \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
#include "lua/call.h"
/* {{{ iproto_msg - declaration */
+
/**
* A single msg from io thread. All requests
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;
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) {
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
*/
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);
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();
}
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 */
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
*/
struct iproto_port
{
- struct port_vtab *vtab;
+ struct port base;
/** Output buffer. */
struct obuf *buf;
/** Reply header. */
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
/* }}} */
-/**
- * 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) ||
};
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
-- 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
#include <string.h>
#include <rmean.h>
#include <box/request.h>
+#include <cbus.h>
+#include <box/error.h>
extern "C" {
#include <lua.h>
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
{
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)
{
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
/* 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:
/**
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);
* 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);
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,
* 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);
if (old_tuple == NULL) {
txn_commit_stmt(txn);
- return;
+ return NULL;
}
TupleGuard old_guard(old_tuple);
* 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;
}
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
}
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
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
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
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);
(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;
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);
if (old_tuple == NULL) {
txn_commit_stmt(txn);
- return;
+ return NULL;
}
TupleGuard old_guard(old_tuple);
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
};
/**
- * 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
{
* 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);
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);
/** \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
/** \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
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);
* 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
#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 */);
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);
}
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)) {
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
*/
#include "fiber.h"
#include "coio.h"
+#include "rmean.h"
/** cbus, cmsg - inter-cord bus and messaging */
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.
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
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
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
}
/**
- * 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)
}
/**
- * 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)
}
/**
- * Unlock a latch. The fiber calling this function must
- * own the latch.
+ * \copydoc box_latch_unlock
*/
static inline void
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
luaL_register(L, NULL, methods);
}
-int
+void
luaL_pushuint64(struct lua_State *L, uint64_t val)
{
if (val < (1ULL << 52)) {
*(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)) {
*(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
/**
* @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
/** \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
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;
}
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)
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.);
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
#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
/** \cond public */
+/** Log levels */
enum say_level {
S_FATAL, /* do not this value use directly */
S_SYSERROR,
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
* 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 */
/** \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
* 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
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
- REPLACE
- UPSERT
- AUTH
+ - ERROR
- UPDATE
- total
- rps
blob - f497bce2950ac9ed7361cea9924209f0c141f798
blob + 4ea136fccd32e24696f54bc0557b7b12015f7c29
--- test/box/stat.result
+++ test/box/stat.result
---
- 0
...
+box.stat.ERROR.total
+---
+- 0
+...
space = box.schema.space.create('tweedledum')
---
...
...
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
---
- 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
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' })
box.stat.REPLACE.total
box.stat.SELECT.total
+-- check exceptions
+space:get('Impossible value')
+box.stat.ERROR.total
+
--# stop server default
--# start server default
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
+-- 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
+-- 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
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)