Skip to content
Snippets Groups Projects
Commit a8c67d40 authored by bigbes's avatar bigbes
Browse files

Finished with chapter Databases

parent 15eb5148
No related branches found
No related tags found
No related merge requests found
.. 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.
.. 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('')!
.. 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}
.. 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'
...
.. 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
...
.. include:: ../directives.rst
.. include:: ../../directives.rst
.. highlight:: lua
-------------------------------------------------------------------------------
......
.. 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
...
......@@ -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
......
.. 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.
.. 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']
...
......@@ -19,7 +19,6 @@ Lua functions can run in the background and perform administrative tasks.
msgpack
fiber
fiber-ipc
box_session
socket
fio
console
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment