From a8c67d40f019203e1cc674f6a1881ee94f8725d4 Mon Sep 17 00:00:00 2001 From: bigbes <bigbes@gmail.com> Date: Fri, 13 Mar 2015 02:31:30 +0300 Subject: [PATCH] Finished with chapter Databases --- doc/sphinx/reference/box/admin.rst | 63 ++++ doc/sphinx/reference/box/atomic.rst | 90 +++++ doc/sphinx/reference/box/authentication.rst | 345 ++++++++++++++++++ doc/sphinx/reference/box/box_error.rst | 61 ++++ .../reference/box/box_introspection.rst | 222 +++++++++++ .../reference/{ => box}/box_session.rst | 2 +- doc/sphinx/reference/box/box_tuple.rst | 307 ++++++++++++++++ doc/sphinx/reference/box/index.rst | 12 +- doc/sphinx/reference/box/limitations.rst | 49 +++ doc/sphinx/reference/box/net_box.rst | 241 ++++++++++++ doc/sphinx/reference/index.rst | 1 - 11 files changed, 1387 insertions(+), 6 deletions(-) create mode 100644 doc/sphinx/reference/box/admin.rst create mode 100644 doc/sphinx/reference/box/atomic.rst create mode 100644 doc/sphinx/reference/box/authentication.rst create mode 100644 doc/sphinx/reference/box/box_error.rst create mode 100644 doc/sphinx/reference/box/box_introspection.rst rename doc/sphinx/reference/{ => box}/box_session.rst (98%) create mode 100644 doc/sphinx/reference/box/box_tuple.rst create mode 100644 doc/sphinx/reference/box/limitations.rst create mode 100644 doc/sphinx/reference/box/net_box.rst diff --git a/doc/sphinx/reference/box/admin.rst b/doc/sphinx/reference/box/admin.rst new file mode 100644 index 0000000000..b95bbd3743 --- /dev/null +++ b/doc/sphinx/reference/box/admin.rst @@ -0,0 +1,63 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Administrative requests +------------------------------------------------------------------------------- + +To learn which functions are considered to be administrative, type ``help()``. +A reference description also follows below: + +.. function:: box.snapshot() + + Take a snapshot of all data and store it in _`snap_dir`/<latest-lsn>.snap. + To take a snapshot, Tarantool first enters the delayed garbage collection + mode for all data. In this mode, tuples which were allocated before the + snapshot has started are not freed until the snapshot has finished. To + preserve consistency of the primary key, used to iterate over tuples, a + copy-on-write technique is employed. If the master process changes part + of a primary key, the corresponding process page is split, and the snapshot + process obtains an old copy of the page. Since a snapshot is written + sequentially, one can expect a very high write performance (averaging to + 80MB/second on modern disks), which means an average database instance gets + saved in a matter of minutes. Note, that as long as there are any changes to + the parent index memory through concurrent updates, there are going to be + page splits, and therefore one needs to have some extra free memory to run + this command. 10% of _`slab_alloc_arena` is, on average, sufficient. This + statement waits until a snapshot is taken and returns operation result. + + .. code-block:: lua + + tarantool> box.info.version + --- + - 1.6.3-439-g7e1011b + ... + tarantool> box.snapshot() + --- + - ok + ... + tarantool> box.snapshot() + --- + error: can't save snapshot, errno 17 (File exists) + ... + + + Taking a snapshot does not cause the server to start a new write-ahead log. + Once a snapshot is taken, old WALs can be deleted as long as all replicas + are up to date. But the WAL which was current at the time **box.snapshot()** + started must be kept for recovery, since it still contains log records + written after the start of **box.snapshot()**. + + An alternative way to save a snapshot is to send the server SIGUSR1 UNIX + signal. While this approach could be handy, it is not recommended for use + in automation: a signal provides no way to find out whether the snapshot + was taken successfully or not. + +.. function:: coredump() + + Fork and dump a core. Since Tarantool stores all tuples in memory, it can + take some time. Mainly useful for debugging. + +.. function:: require('fiber').info() + + Show all running fibers, with their stack. Mainly useful for debugging. diff --git a/doc/sphinx/reference/box/atomic.rst b/doc/sphinx/reference/box/atomic.rst new file mode 100644 index 0000000000..fad312d475 --- /dev/null +++ b/doc/sphinx/reference/box/atomic.rst @@ -0,0 +1,90 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Atomic execution +------------------------------------------------------------------------------- + +In several places it's been noted that Lua processes occur in fibers on a +single thread. That is why there can be a guarantee of execution atomicity. +That requires emphasis. + +=========================================================== + Cooperative multitasking environment +=========================================================== + +Tarantool core is built around a cooperative multi-tasking paradigm: unless a +running fiber deliberately yields control to some other fiber, it is not +preempted. “Yield points†are built into all calls from Tarantool core to the +operating system. Any system call which can block is performed in an +asynchronous manner and the fiber waiting on the system call is preempted with +a fiber ready to run. This model makes all programmatic locks unnecessary: +cooperative multitasking ensures that there is no concurrency around a resource, +no race conditions and no memory consistency issues. + +When requests are small, e.g. simple UPDATE, INSERT, DELETE, SELECT, fiber +scheduling is fair: it takes only a little time to process the request, schedule +a disk write, and yield to a fiber serving the next client. + +A function, however, can perform complex computations, or be written in such a +way that control is not given away for a long time. This can lead to unfair +scheduling, when a single client throttles the rest of the system, or to +apparent stalls in request processing. Avoiding this situation is the +responsibility of the function's author. Most of the box calls, such as +``box.space...insert``, ``box.space...update``, ``box.space...delete`` are yield +points; ``box.space...select``, however, is not. + +It should also be noted that, in the absence of transactions, any yield in a +function is a potential change in the database state. Effectively, it's only +possible to have CAS (compare-and-swap) -like atomic stored procedures: i.e. +functions which select and then modify a record. Multiple data change requests +always run through a built-in yield point. + +At this point an objection could arise: "It's good that a single data-change +request will commit and yield, but surely there are times when multiple +data-change requests must happen without yielding." The standard example is the +money-transfer, where $1 is withdrawn from account #1 and deposited into +account #2. If something interrupted after the withdrawal, then the institution +would be out of balance. For such cases, the ``begin ... commit|rollback`` block +was designed. + +.. function:: box.begin() + + From this point, implicit yields are suspended. In effect the fiber which + executes ``box.begin()`` is starting an "active multi-request transaction", + blocking all other fibers until the transaction ends. All operations within + this transaction should use the same storage engine. + +.. function:: box.commit() + + End the currently active transaction, and make all its data-change + operations permanent. + +.. function:: box.rollback() + + End the currently active transaction, but cancel all its data-change + operations. An explicit call to functions outside ``box.space`` that always + yield, such as ``fiber.yield`` or ``fiber.sleep``, will have the same effect. + +The *requests in a transaction must be sent to the server as a single block*. +It is not enough to enclose them between ``begin`` and ``commit`` or ``rollback``. +To ensure they are sent as a single block: put them in a function, or put them all +on one line, or use a delimiter so that multi-line requests are handled together. + +=========================================================== + Example +=========================================================== + +Assuming that in tuple set 'tester' there are tuples in which the third +field represents a positive dollar amount ... Start a transaction, withdraw from +tuple#1, deposit in tuple#2, and end the transaction, making its effects permanent. + +.. code-block:: lua + + console = require('console'); console.delimiter('!') + box.begin() + amount_of_money = 1.00 + box.space.tester:update({999}, {{'-', 3, amount_of_money}}) + box.space.tester:update({1000}, {{'+', 3, amount_of_money}}) + box.commit() + console.delimiter('')! diff --git a/doc/sphinx/reference/box/authentication.rst b/doc/sphinx/reference/box/authentication.rst new file mode 100644 index 0000000000..bd887dac03 --- /dev/null +++ b/doc/sphinx/reference/box/authentication.rst @@ -0,0 +1,345 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Authentication and authorization +------------------------------------------------------------------------------- + +Understanding the details of security is primarily an issue for administrators, +but ordinary users should at least skim this section so that they will have an +idea of how Tarantool makes it possible for administrators to prevent +unauthorized access to the database and to certain functions. + +Briefly: there is a method to guarantee with password checks that users really +are who they say they are ("authentication"). There is a _user space where user +names and password-hashes are stored. There are functions for saying that +certain users are allowed to do certain things ("privileges"). There is a _priv +space where privileges are stored. Whenever a user tries to do an operation, +there is a check whether the user has the privilege to do the operation +("access control"). + +=========================================================== + Passwords +=========================================================== + +Each user may have a password. The password is any alphanumeric string. +Administrators should advise users to choose long unobvious passwords, but it +is ultimately up to the users to choose or change their own passwords. + +Tarantool passwords are stored in the ``_user`` space with a `Cryptographic hash function`_ +so that, if the password is 'x', the stored hashed-password is a long string +like '``lL3OvhkIPOKh+Vn9Avlkx69M/Ck=``'. When a client connects to a Tarantool +server, the server sends a random `Salt Value`_ which the client must mix with the +hashed-password before sending to the server. Thus the original value 'x' is +never stored anywhere except in the user's head, and the hashed value is never +passed down a network wire except when mixed with a random salt. This system +prevents malicious onlookers from finding passwords by snooping in the log +files or snooping on the wire. It is the same system that `MySQL introduced +several years ago`_ which has proved adequate for medium-security installations. +Nevertheless administrators should warn users that no system is foolproof against +determined long-term attacks, so passwords should be guarded and changed occasionally. + +.. NOTE: + + To get the hash-password of a string '``X``', say ``box.schema.user.password('X')``. + To see more about the details of the algorithm for the purpose of writing a new + client application, read the `scramble.h`_ header file. + +=========================================================== + Users and the _user space +=========================================================== + +The fields in the ``_user`` space are: the numeric id of the tuple, the numeric +id of the tuple's creator, the user name, the type, and the optional password. + +There are three special tuples in the _user space: 'guest', 'admin', and 'public'. + +.. container:: table + + +--------+----+------+--------------------------------------------------------+ + | Name | ID | Type | Description | + +========+====+======+========================================================+ + | guest | 0 | user | Default when connecting remotely. Usually an untrusted | + | | | | user with few privileges. | + +--------+----+------+--------------------------------------------------------+ + | admin | 1 | user | Default when using ``tarantool`` as a console. Usually | + | | | | an administrative user with all privileges. | + +--------+----+------+--------------------------------------------------------+ + | public | 2 | role | Not a user in the usual sense. Described later in | + | | | | section `Roles`_. | + +--------+----+------+--------------------------------------------------------+ + +To select a row from the ``_user`` space, use ``box.space._user:select``. For +example, here is what happens with a select for user id = 0, which is the +'guest' user, which by default has no password: + +.. code-block:: lua + + tarantool> box.space._user:select{0} + --- + - - [0, 1, 'guest', 'user'] + ... + +To change tuples in the ``_user`` space, do not use ordinary ``box.space`` +functions for insert or update or delete - the _user space is special so +there are special functions which have appropriate error checking. + +To create a new user, say ``box.schema.user.create(user-name)`` or +``box.schema.user.create(user-name, {password=password})``. The form +``box.schema.user.create(user-name, {password=password})`` is better because +in a `URI`_ (Uniform Resource Identifier) it is usually illegal to include a +user-name without a password. + +To change the current user's password, say ``box.schema.user.passwd(password)``. + +To change a different user's password, say ``box.schema.user.passwd(user-name, password)``. +(Only the admin user can perform this function.) + +To drop a user, say ``box.schema.user.drop(user-name)``. + +To check whether a user exists, say ``box.schema.user.exists(user-name)``, +which returns true or false. + +For example, here is a session which creates a new user with a strong password, +selects a tuple in the ``_user`` space, and then drops the user. + +.. code-block:: lua + + tarantool> box.schema.user.create('JeanMartin', {password = 'Iwtso_6_os$$'}) + --- + ... + + tarantool> box.space._user.index.name:select{'JeanMartin'} + --- + - - [17, 1, 'JeanMartin', 'user', {'chap-sha1': 't3xjUpQdrt857O+YRvGbMY5py8Q='}] + ... + + tarantool> box.schema.user.drop('JeanMartin') + --- + ... + +.. NOTE:: + + The maximum number of users is 32. + +=========================================================== + Priveleges and _priv space +=========================================================== + +The fields in the ``_priv`` space are: the numeric id of the user who gave the +privilege ("grantor_id"), the numeric id of the user who received the +privilege ("grantee_id"), the id of the object, the type of object - "space" +or "function" or "universe", the type of operation - "read" or "write" or +"execute" or a combination such as "read,write,execute". + +The function for granting a privilege is: +``box.schema.user.grant(user-name-of-grantee, operation-type, object-type, object-name)`` or +``box.schema.user.grant(user-name-of-grantee, operation-type, 'universe')``. + +The function for revoking a privilege is: +``box.schema.user.revoke(user-name-of-grantee, operation-type, object-type, object-name)`` or +``box.schema.user.revoke(user-name-of-grantee, operation-type, 'universe')``. + +For example, here is a session where the admin user gave the guest user the +privilege to read from a space named space55, and then took the privilege away: + +.. code-block:: lua + + tarantool> box.schema.user.grant('guest', 'read', 'space', 'space55') + --- + ... + tarantool> box.schema.user.revoke('guest', 'read', 'space', 'space55') + --- + ... + +.. NOTE:: + + Generally privileges are granted or revoked by the owner of the object (the + user who created it), or by the 'admin' user. Before dropping any objects + or users, steps should be taken to ensure that all their associated + privileges have been revoked. Only the 'admin' user can grant privileges + for the 'universe'. + + +=========================================================== + Functions and _func space +=========================================================== + +The fields in the ``_func`` space are: the numeric function id, a number, +and the function name. + +The ``_func`` space does not include the function's body. One continues to +create Lua functions in the usual way, by saying +"``function function_name () ... end``", without adding anything in the +``_func`` space. The _func space only exists for storing function tuples so +that their names can be used within grant/revoke functions. + +The function for creating a ``_func`` tuple is: +``box.schema.func.create(function-name)``. + +The function for dropping a ``_func`` tuple is: +``box.schema.func.drop(function-name)``. + +The function for checking whether a ``_func`` tuple exists is: +``box.schema.func.exists(function-name)``. + +In the following example, a function named 'f7' is created, then it is put in +the ``_func`` space, then it is used in a ``box.schema.user.grant`` function, +then it is dropped: + +.. code-block:: lua + + tarantool> function f7() box.session.uid() end + --- + ... + tarantool> box.schema.func.create('f7') + --- + ... + tarantool> box.schema.user.grant('guest', 'execute', 'function', 'f7') + --- + ... + tarantool> box.schema.user.revoke('guest', 'execute', 'function', 'f7') + --- + ... + tarantool> box.schema.func.drop('f7') + --- + ... + +=========================================================== + ``box.session`` and security +=========================================================== + +After a connection has taken place, the user has access to a "session" object +which has several functions. The ones which are of interest for security +purposes are: + +.. code-block:: lua + + box.session.uid() -- returns the id of the current user + box.session.user() -- returns the name of the current user + box.session.su(user-name) -- allows changing current user to 'user-name' + +If a user types requests directly on the Tarantool server in its interactive +mode, or if a user connects via telnet to the administrative port (using `admin`_ +instead of listen), then the user by default is 'admin' and has many privileges. +If a user connects from an application program via one of the `connectors`_, then +the user by default is 'guest' and has few privileges. Typically an admin user +will set up and configure objects, then grant privileges to appropriate non-admin +users. Typically a guest user will use ``box.session.su()`` to change into a non-generic +user to whom admin has granted more than the default privileges. For example, +admin might say: + +.. code-block:: lua + + box.space._user:insert{123456,0,'manager'} + box.schema.user.grant('manager', 'read', 'space', '_space') + box.schema.user.grant('manager', 'read', 'space', 'payroll') + +and later a guest user, who wishes to see the payroll, might say: + +.. code-block:: lua + + box.session.su('manager') + box.space.payroll:select{'Jones'} + +=========================================================== + Roles +=========================================================== + +A role is a container for privileges which can be granted to regular users. +Instead of granting and revoking individual privileges, one can put all the +privileges in a role and then grant or revoke the role. Role information is +in the ``_user`` space but the third field - the type field - is 'role' rather +than 'user'. + +If a role R1 is granted a privilege X, and user U1 is granted a privilege +"role R1", then user U1 in effect has privilege X. Then if a role R2 is +granted a privilege Y, and role R1 is granted a privilege "role R2", +then user U1 in effect has both privilege X and privilege Y. In other words, +a user gets all the privileges that are granted to a user's roles, directly +or indirectly. + +.. module:: box.schema.role + +.. function:: create(role-name) + + Create a new role. + +.. function:: grant(role-name, privilege) + + Put a privilege in a role. + +.. function:: revoke(role-name, privilege) + + Take a privilege out of a role. + +.. function:: drop(role-name) + + Drop a role. + +.. function:: info() + + Get information about a role. + +.. function:: grant(role-name, 'execute', 'role', role-name) + + Grant a role to a role. + +.. function:: revoke(role-name, 'execute', 'role', role-name) + + Revoke a role from a role. + +.. function:: exists(role-name) + + Check whether a role exists. + :return: true if role-name identifies a role, otherwise false. + :rtype: boolean + +.. module:: box.schema.user + +.. function:: grant(user-name, 'execute', 'role', role-name) + + Grant a role to a user. + +.. function:: revoke(user-name, 'execute', 'role', role-name) + + Revoke a role from a user. + +There is one predefined role, named 'public', which is automatically assigned +to new users when they are created with ``box.schema.user.create(user-name)``. +Therefore a convenient way to grant 'read' on space '``t``' to every user that +will ever exist is: + +.. code-block:: lua + + box.schema.role.grant('public','read','space','t'). + +=========================================================== + Example +=========================================================== + +In this example, a new user named U1 will insert a new tuple into a new space +named T, and will succeed even though user U1 has no direct privilege to do +such an insert -- that privilege is inherited from role R1, which in turn +inherits from role R2. + +.. code-block:: lua + + -- This example will work for a user with many privileges, such as 'admin' + box.schema.space.create('T') + box.space.T:create_index('primary',{}) + -- Create a user U1 so that later it's possible to say box.session.su('U1') + box.schema.user.create('U1') + -- Create two roles, R1 and R2 + box.schema.role.create('R1') + box.schema.role.create('R2') + -- Grant role R2 to role R1 and role R1 to U1 (order doesn't matter) + box.schema.role.grant('R1','execute','role','R2') + box.schema.role.grant('U1','execute','role','R1') + -- Grant read and execute privileges to R2 (but not to R1 and not to U1) + box.schema.role.grant('R2','read,write','space','T') + box.schema.role.grant('R2','execute','universe') + -- Use box.session.su to say "now become user U1" + box.session.su('U1') + -- The following insert succeeds because U1 in effect has write privilege on T + box.space.T:insert{1} diff --git a/doc/sphinx/reference/box/box_error.rst b/doc/sphinx/reference/box/box_error.rst new file mode 100644 index 0000000000..f900b5c7ec --- /dev/null +++ b/doc/sphinx/reference/box/box_error.rst @@ -0,0 +1,61 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Package `box.error` +------------------------------------------------------------------------------- + +The ``box.error`` function is for raising an error. The difference between this +function and Lua's built-in ``error()`` function is that when the error reaches +the client, its error code is preserved. In contrast, a Lua error would always +be presented to the client as ``ER_PROC_LUA``. + +.. module:: box.error + +.. function:: box.error{reason=string [, code=number]} + + When called with a Lua-table argument, the code and reason have any + user-desired values. The result will be those values. + + :param integer code: + :param string reason: + +.. function:: box.error() + + When called without arguments, ``box.error()`` re-throws whatever the last + error was. + +.. function:: box.error(code, errtext [, errtext ...]) + + Emulate a request error, with text based on one of the pre-defined Tarantool + errors defined in the file _`errcode.h` in the source tree. Lua constants + which correspond to those Tarantool errors are defined as members of + ``box.error``, for example ``box.error.NO_SUCH_USER == 45``. + + :param number code: number of a pre-defined error + :param string errtext(s): part of the message which will accompany the error + + For example: + + the ``NO_SUCH_USER`` message is "``User '%s' is not found``" -- it includes + one "``%s``" component which will be replaced with errtext. Thus a call to + ``box.error(box.error.NO_SUCH_USER, 'joe')`` or ``box.error(45, 'joe')`` + will result in an error with the accompanying message + "``User 'joe' is not found``". + + :except: whatever is specified in errcode-number. + + .. code-block:: lua + + tarantool> box.error({code=555, reason='Arbitrary message'}) + --- + - error: Arbitrary message + ... + tarantool> box.error() + --- + - error: Arbitrary message + ... + tarantool> box.error(box.error.FUNCTION_ACCESS_DENIED, 'A', 'B', 'C') + --- + - error: A access denied for user 'B' to function 'C' + ... diff --git a/doc/sphinx/reference/box/box_introspection.rst b/doc/sphinx/reference/box/box_introspection.rst new file mode 100644 index 0000000000..b2aa690fa1 --- /dev/null +++ b/doc/sphinx/reference/box/box_introspection.rst @@ -0,0 +1,222 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Server Introspection +------------------------------------------------------------------------------- + +===================================================================== + Package `box.cfg` +===================================================================== + +.. module:: box.cfg + +The ``box.cfg`` package is for administrators to specify all the server +configuration parameters; the full description of the parameters is in +section Configuration_. Use ``box.cfg`` without braces to get read-only +access to those parameters. + +.. data:: box.cfg + + .. code-block:: lua + + tarantool> box.cfg + --- + - too_long_threshold: 0.5 + slab_alloc_factor: 2 + slab_alloc_minimal: 64 + background: false + slab_alloc_arena: 1 + log_level: 5 + ... + ... + +.. _Configuration: http://tarantool.org/doc/user_guide.html#configuration-file + +===================================================================== + Package `box.info` +===================================================================== + +.. module:: box.info + +The ``box.info`` package provides access to information about server variables +-- ``pid``, ``uptime``, ``version`` and others. + +**recovery_lag** holds the difference (in seconds) between the current time on +the machine (wall clock time) and the time stamp of the last applied record. +In replication setup, this difference can indicate the delay taking place +before a change is applied to a replica. + +**recovery_last_update** is the wall clock time of the last change recorded in +the write-ahead log. To convert it to human-readable time, +you can use **date -d@** 1306964594.980. + +**status** is either "primary" or "replica/<hostname>". + +.. function:: box.info() + + Since ``box.info`` contents are dynamic, it's not possible to iterate over + keys with the Lua ``pairs()`` function. For this purpose, ``box.info()`` + builds and returns a Lua table with all keys and values provided in the + package. + + :return: keys and values in the package. + :rtype: table + + .. code-block:: yaml + + tarantool> box.info() + --- + - server: + lsn: 158 + ro: false + uuid: 75967704-0115-47c2-9d03-bd2bdcc60d64 + id: 1 + pid: 32561 + version: 1.6.4-411-gcff798b + snapshot_pid: 0 + status: running + vclock: {1: 158} + replication: + status: off + uptime: 2778 + ... + +.. data:: status + pid + version + ... + + .. code-block:: lua + + tarantool> box.info.pid + --- + - 1747 + ... + tarantool> box.info.logger_pid + --- + - 1748 + ... + tarantool> box.info.version + --- + - 1.6.4-411-gcff798b + ... + tarantool> box.info.uptime + --- + - 3672 + ... + tarantool> box.info.status + --- + - running + ... + tarantool> box.info.recovery_lag + --- + - 0.000 + ... + tarantool> box.info.recovery_last_update + --- + - 1306964594.980 + ... + tarantool> box.info.snapshot_pid + --- + - 0 + ... + +===================================================================== + Package `box.slab` +===================================================================== + +.. module:: box.slab + +The ``box.slab`` package provides access to slab allocator statistics. The +slab allocator is the main allocator used to store tuples. This can be used +to monitor the total memory use and memory fragmentation. + +The display of slabs is broken down by the slab size -- 64-byte, 136-byte, +and so on. The example omits the slabs which are empty. The example display +is saying that: there are 16 items stored in the 64-byte slab (and 16*64=102 +so bytes_used = 1024); there is 1 item stored in the 136-byte slab +(and 136*1=136 so bytes_used = 136); the arena_used value is the total of all +the bytes_used values (1024+136 = 1160); the arena_size value is the arena_used +value plus the total of all the bytes_free values (1160+4193200+4194088 = 8388448). +The arena_size and arena_used values are the amount of the % of slab_alloc_arena +that is already distributed to the slab allocator. + +.. data:: slab + + .. code-block:: lua + + tarantool> box.slab.info().arena_used + --- + - 4194304 + ... + tarantool> box.slab.info().arena_size + --- + - 104857600 + ... + tarantool> box.slab.info().slabs + --- + - - {mem_free: 9320, mem_used: 6976, 'item_count': 109, + 'item_size': 64, 'slab_count': 1, 'slab_size': 16384} + - {mem_free: 16224, mem_used: 72, 'item_count': 1, + 'item_size': 72, 'slab_count': 1,'slab_size': 16384} + etc. + ... + tarantool> box.slab.info().slabs[1] + --- + - {mem_free: 9320, mem_used: 6976, 'item_count': 109, + 'item_size': 64, 'slab_count': 1, 'slab_size': 16384} + ... + +===================================================================== + Package `box.stat` +===================================================================== + +.. module:: box.stat + +The ``box.stat`` package provides access to request statistics. Show the +average number of requests per second, and the total number of requests +since startup, broken down by request type. + +.. data:: box.stat + + .. code-block:: lua + + tarantool> box.stat, type(box.stat) -- a virtual table + --- + - [] + - table + ... + tarantool> box.stat() -- the full contents of the table + --- + - DELETE: + total: 48902544 + rps: 147 + EVAL: + total: 0 + rps: 0 + SELECT: + total: 388322317 + rps: 1246 + REPLACE: + total: 4 + rps: 0 + INSERT: + total: 48207694 + rps: 139 + AUTH: + total: 0 + rps: 0 + CALL: + total: 8 + rps: 0 + UPDATE: + total: 743350520 + rps: 1874 + ... + tarantool> box.stat().DELETE -- a selected item of the table + --- + - total: 48902544 + rps: 0 + ... + diff --git a/doc/sphinx/reference/box_session.rst b/doc/sphinx/reference/box/box_session.rst similarity index 98% rename from doc/sphinx/reference/box_session.rst rename to doc/sphinx/reference/box/box_session.rst index e11522b0e4..49cd6c2d16 100644 --- a/doc/sphinx/reference/box_session.rst +++ b/doc/sphinx/reference/box/box_session.rst @@ -1,4 +1,4 @@ -.. include:: ../directives.rst +.. include:: ../../directives.rst .. highlight:: lua ------------------------------------------------------------------------------- diff --git a/doc/sphinx/reference/box/box_tuple.rst b/doc/sphinx/reference/box/box_tuple.rst new file mode 100644 index 0000000000..b2912b5c50 --- /dev/null +++ b/doc/sphinx/reference/box/box_tuple.rst @@ -0,0 +1,307 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Package `box.tuple` +------------------------------------------------------------------------------- + +.. module:: box.tuple + +The ``box.tuple`` package provides read-only access for the ``box.tuple`` +userdata type. It allows, for a single tuple: selective retrieval of the field +contents, retrieval of information about size, iteration over all the fields, +and conversion to a Lua table. + +.. function:: new(value) + + Construct a new tuple from either a scalar or a Lua table. Alternatively, + one can get new tuples from tarantool's SQL-like statements: SELECT, + INSERT, UPDATE, REPLACE, which can be regarded as statements that do + ``new()`` implicitly. + + :param lua-value value: the value that will become the tuple contents. + + :return: a new tuple + :rtype: tuple + + In the following example, ``x`` will be a new table object containing one + tuple and ``t`` will be a new tuple object. Saying ``t`` returns the + entire tuple ``t``. + + .. code-block:: lua + + tarantool> x = box.space.tester:insert{33,tonumber('1'),tonumber64('2')}:totable() + --- + ... + tarantool> t = box.tuple.new({'abc', 'def', 'ghi', 'abc'}) + --- + ... + tarantool> t + --- + - ['abc', 'def', 'ghi', 'abc'] + ... + +.. class:: tuple + + .. method:: __len() + + The ``#`` operator in Lua means "return count of components". So, + if ``t`` is a tuple instance, ``#t`` will return the number of fields. + + :rtype: number + + In the following example, a tuple named ``t`` is created and then the + number of fields in ``t`` is returned. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4'}) + --- + ... + tarantool> #t + --- + - 4 + ... + + .. method:: bsize() + + If ``t`` is a tuple instance, ``t:bsize()`` will return the number of + bytes in the tuple. It is useful to check this number when making + changes to data, because there is a fixed maximum: one megabyte. Every + field has one or more "length" bytes preceding the actual contents, so + ``bsize()`` returns a value which is slightly greater than the sum of + the lengths of the contents. + + :return: number of bytes + :rtype: number + + In the following example, a tuple named ``t`` is created which has + three fields, and for each field it takes one byte to store the length + and three bytes to store the contents, and a bit for overhead, so + ``bsize()`` returns ``3*(1+3)+1``. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'aaa','bbb','ccc'}) + --- + ... + tarantool> t:bsize() + --- + - 13 + ... + + .. method:: __index(key) + + If ``t`` is a tuple instance, ``t[field-number]`` will return the field + numbered field-number in the tuple. The first field is ``t[1]``. + + :return: field value. + :rtype: lua-value + + In the following example, a tuple named ``t`` is created and then the + second field in ``t`` is returned. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4'}) + --- + ... + tarantool> t[2] + --- + - Fld#2 + ... + + .. method:: find([field-number,] field-value) + findall([field-number,] field-value) + + If ``t`` is a tuple instance, ``t:find(field-value)`` will return the + number of the first field in ``t`` that matches the field value), + and ``t:findall(field-value [, field-value ...])`` will return numbers + of all fields in ``t`` that match the field value. Optionally one can + put a numeric argument ``field-number`` before the search-value to + indicate “start searching at field number ``field-number``.†+ + :return: the number of the field in the tuple. + :rtype: number + + In the following example, a tuple named ``t`` is created and then: the + number of the first field in ``t`` which matches 'a' is returned, then + the numbers of all the fields in ``t`` which match 'a' are returned, + then the numbers of all the fields in t which match 'a' and are at or + fter the second field are returned. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'a','b','c','a'}) + --- + ... + tarantool> t:find('a') + --- + - 1 + ... + tarantool> t:findall('a') + --- + - 1 + - 4 + ... + tarantool> t:findall(2, 'a') + --- + - 4 + ... + + .. method:: transform(start-field-number, fields-to-remove [, field-value ...]) + + If ``t`` is a tuple instance, ``t:transform(start-field-number,fields-to-remove)`` + will return a tuple where, starting from field ``start-field-number``, + a number of fields (``fields-to-remove``) are removed. Optionally one + can add more arguments after ``fields-to-remove`` to indicate new + values that will replace what was removed. + + :param integer start-field-number: base 1, may be negative + :param integer fields-to-remove: + :param lua-value field-value(s): + :return: tuple + :rtype: tuple + + In the following example, a tuple named ``t`` is created and then, + starting from the second field, two fields are removed but one new + one is added, then the result is returned. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'}) + --- + ... + tarantool> t:transform(2,2,'x') + --- + - ['Fld#1', 'x', 'Fld#4', 'Fld#5'] + ... + + .. method:: unpack() + + If ``t`` is a tuple instance, ``t:unpack(n)`` will return all fields. + + :return: field(s) from the tuple. + :rtype: lua-value(s) + + In the following example, a tuple named ``t`` is created and then all + its fields are selected, then the result is returned. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'}) + --- + ... + tarantool> t:unpack() + --- + - Fld#1 + - Fld#2 + - Fld#3 + - Fld#4 + - Fld#5 + ... + + .. method:: pairs() + + In Lua, ``lua-table-value:pairs()`` is a method which returns: + ``function``, ``lua-table-value``, ``nil``. Tarantool has extended + this so that ``tuple-value:pairs()`` returns: ``function``, + ``tuple-value``, ``nil``. It is useful for Lua iterators, because Lua + iterators traverse a value's components until an end marker is reached. + + :return: function, tuple-value, nil + :rtype: function, lua-value, nil + + In the following example, a tuple named ``t`` is created and then all + its fields are selected using a Lua for-end loop. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'}) + --- + ... + tarantool> tmp = ''; for k, v in t:pairs() do tmp = tmp .. v end + --- + ... + tarantool> tmp + --- + - Fld#1Fld#2Fld#3Fld#4Fld#5 + ... + + .. method:: update({{format, field_number, value}...}) + + Update a tuple. + + This function updates a tuple which is not in a space. Compare the function + ``box.space.space-name:update{key, format, {field_number, value}...)``, + which updates a tuple in a space. + + Parameters: briefly: format indicates the type of update operation such as '``=``' + for 'assign new value', ``field_number`` indicates the field number to change such + as 2 for field number 2, value indicates the string which operates on the field such + as 'B' for a new assignable value = 'B'. + + For details: see the description for ``format``, ``field_number``, and ``value`` in + the section ``box.space.space-name:update{key, format, {field_number, value}...)``. + + :return: new tuple + :rtype: tuple + + In the following example, a tuple named ``t`` is created and then its second field is + updated to equal 'B'. + + .. code-block:: lua + + tarantool> t = box.tuple.new({'Fld#1','Fld#2','Fld#3','Fld#4','Fld#5'}) + --- + ... + tarantool> t:update({{'=',2,'B'}}) + --- + - ['Fld#1', 'B', 'Fld#3', 'Fld#4', 'Fld#5'] + ... + +=========================================================== + Example +=========================================================== + +This function will illustrate how to convert tuples to/from Lua tables and +lists of scalars: + +.. code-block:: lua + + tuple = box.tuple.new({scalar1, scalar2, ... scalar_n}) -- scalars to tuple + lua_table = {tuple:unpack()} -- tuple to Lua table + scalar1, scalar2, ... scalar_n = tuple:unpack() -- tuple to scalars + tuple = box.tuple.new(lua_table) -- Lua table to tuple + +Then it will find the field that contains 'b', remove that field from the tuple, +and display how many bytes remain in the tuple. The function uses Tarantool +``box.tuple`` functions ``new()``, ``unpack()``, ``find()``, ``transform()``, +``bsize()``. + +.. code-block:: lua + + console = require('console'); console.delimiter('!') + function example() + local tuple1, tuple2, lua_table_1, scalar1, scalar2, scalar3, field_number + tuple1 = box.tuple.new({'a', 'b', 'c'}) + luatable1 = {tuple1:unpack()} + scalar1, scalar2, scalar3 = tuple1:unpack() + tuple2 = box.tuple.new(luatable1) + field_number = tuple2:find('b') + tuple2 = tuple2:transform(field_number, 1) + return 'tuple2 = ' , tuple2 , ' # of bytes = ' , tuple2:bsize() + end! + console.delimiter('')! + +... And here is what happens when one invokes the function: + +.. code-block:: yaml + + tarantool> example() + --- + - 'tuple2 = ' + - ['a', 'c'] + - ' # of bytes = ' + - 5 + ... diff --git a/doc/sphinx/reference/box/index.rst b/doc/sphinx/reference/box/index.rst index f2726d03d9..ee5f827a0d 100644 --- a/doc/sphinx/reference/box/index.rst +++ b/doc/sphinx/reference/box/index.rst @@ -19,11 +19,15 @@ no arguments. The packages inside the box library are: box_tuple box_space box_index + box_session + box_error + box_tuple + box_introspection net_box - box_cfg - box_info - box_slab - box_stat + admin + atomic + authentication + limitations Every package contains one or more Lua functions. A few packages contain members as well as functions. The functions allow data definition (create diff --git a/doc/sphinx/reference/box/limitations.rst b/doc/sphinx/reference/box/limitations.rst new file mode 100644 index 0000000000..c1c279df7a --- /dev/null +++ b/doc/sphinx/reference/box/limitations.rst @@ -0,0 +1,49 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Limitations +------------------------------------------------------------------------------- + +Number of fields in an index + For BITSET indexes, the maximum is 1. For TREE or HASH indexes, the maximum + is 255 (``box.schema.INDEX_PART_MAX``). For RTREE indexes, the number of + fields must be either 2 or 4. + +Number of indexes in a space + 10 (``box.schema.INDEX_MAX``). + +Number of fields in a tuple + The theoretical maximum is 2147483647 (``box.schema.FIELD_MAX``). The + practical maximum is whatever is specified by the space's field_count + member, or the maximum tuple length. + +Number of spaces + The theoretical maximum is 2147483647 (``box.schema.SPACE_MAX``). + +Number of connections + The practical limit is the number of file descriptors that one can set + with the operating system. + +Space size + The total maximum size for all spaces is in effect set by + `slab_alloc_arena`_, which in turn is limited by the total available memory. + +Update operations count + The maximum number of operations that can be in a single update + is 4000 (``BOX_UPDATE_OP_CNT_MAX``). + +Number of users and roles + 32 (). + +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 + 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 + not supported. Indexes must be unique, that is, the option unique=false + is not supported. The ``alter()``, ``len()``, and ``count()`` functions + are not supported. diff --git a/doc/sphinx/reference/box/net_box.rst b/doc/sphinx/reference/box/net_box.rst new file mode 100644 index 0000000000..98ef99f5ce --- /dev/null +++ b/doc/sphinx/reference/box/net_box.rst @@ -0,0 +1,241 @@ +.. include:: ../../directives.rst +.. highlight:: lua + +------------------------------------------------------------------------------- + Package `net.box` +------------------------------------------------------------------------------- + +The ``net.box`` package contains connectors to remote database systems. One +variant, ``box.net.sql``, is for connecting to MySQL or MariaDB or PostgreSQL — +that variant is the subject of the `SQL DBMS plugins`_ appendix. In this section +the subject is the built-in variant, ``box.net``. This is for connecting to +tarantool servers via a network. + +Call ``require('net.box')`` to get a ``net.box`` object, which will be called +``net_box`` for examples in this section. Call ``net_box.new()`` to connect and +get a connection object, which will be called conn for examples in this section. +Call the other ``net.box()`` routines, passing ``conn:``, to execute requests on +the remote box. Call `conn:close`_ to disconnect. + +.. _conn:close: :func:`connection_object:close` + +All `net.box`` methods are fiber-safe, that is, it is safe to share and use the +same connection object across multiple concurrent fibers. In fact, it's perhaps +the best programming practice with Tarantool. When multiple fibers use the same +connection, all requests are pipelined through the same network socket, but each +fiber gets back a correct response. Reducing the number of active sockets lowers +the overhead of system calls and increases the overall server performance. There +are, however, cases when a single connection is not enough — for example when it's +necessary to prioritize requests or to use different authentication ids. + +.. module:: net_box + +.. function:: new(host, port [, {params}]) + + Create a new connection. The connection is established on demand, at the + time of the first request. It is re-established automatically after a + disconnect. The returned conn object supports methods for making remote + requests, such as select, update or delete. + + For the local tarantool server there is a pre-created always-established + connection object named net_box.self. Its purpose is to make polymorphic + use of the ``net_box`` API easier. Therefore ``conn = net_box:new('localhost', 3301)`` + can be replaced by ``conn = net_box.self``. However, there is an important + difference between the embedded connection and a remote one. With the + embedded connection, requests which do not modify data do not yield. + When using a remote connection, any request can yield, and local database + state may have changed by the time it returns. + + :param string host: + :param number port: + :param boolean wait_connect: + :param string user: + :param string password: + :return: conn object + :rtype: userdata + + .. code-block:: lua + + conn = net_box:new('localhost', 3301) + conn = net_box:new('127.0.0.1', box.cfg.listen, { + wait_connect = false, + user = 'guest', + password = '' + }) + +.. class:: connection + + .. method:: ping() + + Execute a PING command. + + :return: true on success, false on error + :rtype: boolean + + .. code-block:: lua + + net_box.self:ping() + + .. method:: wait_connected([timeout]) + + Wait for connection to be active or closed. + + :param number timeout: + :return: true when connected, false on failure. + :rtype: boolean + + .. code-block:: lua + + net_box.self:wait_connected(). + + .. method:: close() + + Close a connection. + + Connection objects are garbage collected just like any other objects in Lua, so + an explicit destruction is not mandatory. However, since close() is a system + call, it is good programming practice to close a connection explicitly when it + is no longer needed, to avoid lengthy stalls of the garbage collector. + + .. code-block:: lua + + conn:close() + + .. method:: connection.space.<space-name>:select{field-value, ...} + + ``conn.space.space-name:select{...}`` is the remote-call equivalent + of the local call ``box.space.space-name:select{...}``. Please note + this difference: a local ``box.space.space-name:select{...}`` does + not yield, but a remote ``conn.space.space-name:select{...}`` call + does yield, so local data may change while a remote + ``conn.space.space-name:select{...}`` is running. + + .. method:: connection.space.<space-name>:insert{field-value, ...} + + ``conn.space.space-name:insert(...)`` is the remote-call equivalent + of the local call ``box.space.space-name:insert(...)``. + + .. method:: connection.space.<space-name>:replace{field-value, ...} + + ``conn.space.space-name:replace(...)`` is the remote-call equivalent + of the local call ``box.space.space-name:replace(...)``. + + .. method:: connection.space.<space-name>:update{field-value, ...} + + ``conn.space.space-name:update(...)`` is the remote-call equivalent + of the local call ``box.space.space-name:update(...)``. + + .. method:: connection.space.<space-name>:delete{field-value, ...} + + ``conn.space.space-name:delete(...)`` is the remote-call equivalent + of the local call ``box.space.space-name:delete(...)``. + + .. method:: call(function-name [, arguments]) + + ``conn:call('func', '1', '2', '3')`` is the remote-call equivalent of + ``func('1', '2', '3')``. That is, ``conn:call`` is a remote + stored-procedure call. + + .. code-block:: lua + + conn:call('function5'). + + .. method:: timeout(timeout) + + ``timeout(...)`` is a wrapper which sets a timeout for the request that + follows it. + + .. code-block:: lua + + conn:timeout(0.5).space.tester:update({1}, {{'=', 2, 15}}). + + All remote calls support execution timeouts. Using a wrapper object makes + the remote connection API compatible with the local one, removing the need + for a separate timeout argument, which the local version would ignore. Once + a request is sent, it cannot be revoked from the remote server even if a + timeout expires: the timeout expiration only aborts the wait for the remote + server response, not the request itself. + +.. _SQL DBMS plugins: http://tarantool.org/doc/user_guide.html#plugins + +=========================================================== + Example +=========================================================== + +This example will work with the sandbox configuration described in the preface. +That is, there is a space named tester with a numeric primary key. Assume that +the database is nearly empty. Assume that the tarantool server is running on +``localhost 127.0.0.1:3301``. + +.. code-block:: lua + + tarantool> box.schema.user.grant('guest', 'read,write,execute', 'universe') + --- + ... + tarantool> console = require('console'); console.delimiter('!') + --- + ... + tarantool> net_box = require('net.box')! + --- + ... + tarantool> function example() + > if net_box.self:ping() then + > table.insert(ta, 'self:ping() succeeded') + > table.insert(ta, ' (no surprise -- self connection is pre-established)') + > end + > if box.cfg.listen == '3301' then + > table.insert(ta,'The local server listen address = 3301') + > else + > table.insert(ta, 'The local server listen address is not 3301') + > table.insert(ta, '( (maybe box.cfg{...listen="3301"...} was not stated)') + > table.insert(ta, '( (so connect will fail)') + > end + > conn = net_box:new('127.0.0.1', 3301) + > conn.space.tester:delete{800} + > table.insert(ta, 'conn delete done on tester.') + > conn.space.tester:insert{800, 'data'} + > table.insert(ta, 'conn insert done on tester, index 0') + > table.insert(ta, ' primary key value = 800.') + > wtuple = conn.space.tester:select{800} + > table.insert(ta, 'conn select done on tester, index 0') + > table.insert(ta, ' number of fields = ' .. #wtuple) + > conn.space.tester:delete{800} + > table.insert(ta, 'conn delete done on tester') + > conn.space.tester:replace{800, 'New data', 'Extra data'} + > table.insert(ta, 'conn:replace done on tester') + > conn:timeout(0.5).space.tester:update({800}, {{'=', 2, 'Fld#1'}}) + > table.insert(ta, 'conn update done on tester') + > conn:close() + > table.insert(ta, 'conn close done') + > end! + --- + ... + tarantool> console.delimiter('')! + --- + ... + tarantool> ta = {} + --- + ... + tarantool> example() + --- + ... + tarantool> ta + --- + - - self:ping() succeeded + - ' (no surprise -- self connection is pre-established)' + - The local server listen address = 3301 + - conn delete done on tester. + - conn insert done on tester, index 0 + - ' primary key value = 800.' + - conn select done on tester, index 0 + - ' number of fields = 1' + - conn delete done on tester + - conn:replace done on tester + - conn update done on tester + - conn close done + ... + tarantool> box.space.tester:select{800} -- Prove that the update succeeded. + --- + - [800, 'Fld#1', 'Extra data'] + ... + diff --git a/doc/sphinx/reference/index.rst b/doc/sphinx/reference/index.rst index 7782683952..5b7fa52119 100644 --- a/doc/sphinx/reference/index.rst +++ b/doc/sphinx/reference/index.rst @@ -19,7 +19,6 @@ Lua functions can run in the background and perform administrative tasks. msgpack fiber fiber-ipc - box_session socket fio console -- GitLab