box
box.tuple
box.cjson
box.space
box.index
box.fiber
box.session
box.ipc
— inter procedure communicationbox.socket
— TCP and UDP socketsbox.net.box
— working with networked Tarantool peersbox.cfg
,
box.info
, box.slab
and
box.stat
: server introspectionTarantool is an in-memory NoSQL database management system. The code is available for free under the terms of the BSD license. Supported platforms are GNU/Linux, Mac OS and FreeBSD.
The server maintains all its data in random-access memory, and therefore has very low read latency. The server keeps persistent copies of the data in non-volatile storage, such as disk, when users request "snapshots". The server maintains a write-ahead log (WAL) to ensure consistency and crash safety of the persistent copies. The server performs inserts and updates atomically -- changes are not considered complete until the WAL is written. The logging subsystem supports group commit.
When the rate of data changes is high, the write-ahead log file (or files) can grow quickly. This uses up disk space, and increases the time necessary to restart the server (because the server must start with the last snapshot, and then replay the transactions that are in the log). The solution is to make snapshots frequently. Therefore the server ensures that snapshots are quick, resource-savvy, and non-blocking . To accomplish this, the server uses delayed garbage collection for data pages and uses a copy-on-write technique for index pages. This ensures that the snapshot process has a consistent read view.
Tarantool is lock-free. Instead of the operating system's concurrency primitives, such as mutexes, Tarantool uses cooperative multitasking to handle thousands of connections simultaneously. There is a fixed number of independent execution threads. The threads do not share state. Instead they exchange data using low-overhead message queues. While this approach limits the number of cores that the server will use, it removes competition for the memory bus and ensures peak scalability of memory access and network throughput. CPU utilization of a typical highly-loaded Tarantool server is under 10%.
Unlike most NoSQL DBMSs, Tarantool supports secondary index keys and multi-part index keys as well as primary keys. The possible index types are HASH, TREE, and BITSET.
As its key feature, Tarantool supports Lua stored procedures, which can access and modify data atomically. Users can create, modify and drop Lua procedures at runtime.
There is a role not only for Lua procedures, but also for Lua programs. During startup, Lua programs can be used to define triggers and background tasks, or interact with networked peers. Unlike popular application development frameworks based on a "reactor" pattern, networking in server-side Lua is sequential, yet very efficient, as it is built on top of the cooperative multitasking environment that Tarantool itself uses.
Extended with Lua, Tarantool typically replaces multiple components of an existing system. Complex multi-tier Web application architectures become simpler, and performance is good.
Tarantool supports asynchronous replication , locally or to a remote host. Replication does not cause blocking of writes to the master database. If the master becomes unavailable, a replica can assume the master role without requiring a restart.
Tarantool is in production today. Tarantool was created by and is actively used at Mail.Ru, one of the leading Russian web content providers. At Mail.Ru the software serves the “hottest” data, such as online users and their sessions, online application properties, mapping between users and their serving shards, and so on.
Outside Mail.Ru the software is used by a growing number of projects in online gaming, digital marketing, and social media industries. While product development is sponsored by Mail.Ru, the roadmap, the bugs database and the development process are fully open. The software incorporates patches from dozens of community contributors. The Tarantool community writes and maintains most of the drivers for programming languages.
This manual is written in DocBook 5 XML markup language and is using the standard DocBook XSL formatting conventions:
UNIX shell command input is prefixed with '$ ' and is in a fixed-width font:
$
tarantool_box--background
File names are also in a fixed-width font:
/path/to/var/dir
Text that represents user input is in boldface:
$
your input here
Within user input, replaceable items are in italics:
$
tarantool_box
--option
Please report bugs in Tarantool at http://github.com/tarantool/tarantool/issues. You can contact developers directly on #tarantool IRC channel or via a mailing list, Tarantool Google group.
This chapter shows how to download, how to install, and how to start Tarantool for the first time.
For production, if possible, you should download a binary (executable) package. This will ensure that you have the same build of the same version that the developers have. That makes analysis easier if later you need to report a problem, and avoids subtle problems that might happen if you used different tools or different parameters when building from source. All programs in the binary tarballs are linked statically so there will be no external dependencies. The section about binaries is “Downloading and installing a binary package”.
For development, you will want to download a source package and make the binary by yourself using a C/C++ compiler and common tools. Although this is a bit harder, it gives more control. And the source packages include additional files, for example the Tarantool test suite. The section about source is “Downloading and building a source package”.
If the installation has already been done, then you should try it out. So we've provided some instructions that you can use to make a temporary “sandbox”. In a few minutes you can start the server, start the client, and type in some database-manipulation statements. The section about sandbox is “Starting Tarantool and making your first database”.
The repositories for the “stable” release are at tarantool.org/dist. The repositories for the “master” release are at tarantool.org/dist/master. Since this is the manual for the “stable” release, all instructions use tarantool.org/dist.
An automatic build system creates, tests and publishes packages for every push into the stable branch.
Therefore if you looked at tarantool.org/dist you would see many files.
Names of binary packages have the format
.
Here is one example:
tarantool-
<version>
-<OS>
-<machine>
.tar.gz
tarantool-1.5.1-97-g8e8cd06-linux-x86_64.tar.gz 26-Sep-2013 15:55 3664777
which means “Tarantool package, major version = 1, minor version number = 5, patch number 1, git revision id g8e8cd06, is a Linux x86 64-bit compressed tarball, pushed on September 26 2013, which contains 3.6 MB.”
To download and install the package that's appropriate for your environment, start a shell (terminal) and enter one of the following sets of command-line instructions.
# DEBIAN commands for Tarantool stable binary download: # There is always an up-to-date Debian repository at http://tarantool.org/dist/debian # The repository contains builds for Debian unstable "Sid", stable "Wheezy", forthcoming "Jessie", ... # add the tarantool.org repository to your apt sources list: # ($release is an environment variable which will contain the Debian version code e.g. "Wheezy") wget http://tarantool.org/dist/public.key sudo apt-key add./public.key
release=`lsb_release -c -s` # append two lines to a list of source repositories echo "deb http://tarantool.org/dist/debian/ $release main" | sudo tee-a
/etc/apt/sources.list.d/tarantool.list
echo "deb-src http://tarantool.org/dist/debian/ $release main" | sudo tee-a
/etc/apt/sources.list.d/tarantool.list
# install sudo apt-get update sudo apt-get install tarantool tarantool-client
# UBUNTU commands for Tarantool stable binary download: # There is always an up-to-date Ubuntu repository at http://tarantool.org/dist/ubuntu # The repository contains builds for Ubuntu 12.04 "precise", 12.10 "quantal", 13.04 "raring", 13.10 "saucy", ... # add the tarantool.org repository to your apt sources list: # ($release is an environment variable which will contain the Ubuntu version code e.g. "precise") # (if you want the version that comes with Ubuntu, start with the lines that follow the '# install' comment) cd ~ wget http://tarantool.org/dist/public.key sudo apt-key add./public.key
release=`lsb_release -c -s` # append two lines to a list of source repositories echo "deb http://tarantool.org/dist/ubuntu/ $release main" | sudo tee-a
/etc/apt/sources.list.d/tarantool.list
echo "deb-src http://tarantool.org/dist/ubuntu/ $release main" | sudo tee-a
/etc/apt/sources.list.d/tarantool.list
# install sudo apt-get update sudo apt-get install tarantool tarantool-client
# CENTOS commands for Tarantool stable binary download: # These instructions are applicable for CentOS version 5 or 6, and RHEL version 5 or 6 # Pick the CentOS repository which fits your CentOS/RHEL version and your x86 platform: # http://tarantool.org/dist/centos/5/os/i386 for version 5, x86-32 # http://tarantool.org/dist/centos/5/os/x86_64 for version 5, x86-64 # http://tarantool.org/dist/centos/6/os/i386 for version 6, x86-32 # http://tarantool.org/dist/centos/6/os/x86_64 for version 6, x86-64 # Add the following section to your yum repository list (/etc/yum.repos.d/tarantool.repo
): # (in the following instructions, $releasever i.e. CentOS release version must be either 5 or 6) # (in the following instructions, $basearch i.e. base architecture must be either i386 or x86_64) # [tarantool] # name=CentOS-$releasever
- Tarantool # baseurl=http://tarantool.org/dist/centos/$releasever
/os/$basearch
/ # enabled=1 # gpgcheck=0 # For example, if you have CentOS version 6 and x86-64, you can # add the new section thus: echo "[tarantool]" | sudo tee/etc/yum.repos.d/tarantool.repo
echo "name=CentOS-6 - Tarantool"| sudo tee-a
/etc/yum.repos.d/tarantool.repo
echo "baseurl=http://tarantool.org/dist/centos/6/os/x86_64/" | sudo tee-a
/etc/yum.repos.d/tarantool.repo
echo "enabled=1" | sudo tee-a
/etc/yum.repos.d/tarantool.repo
echo "gpgcheck=0" | sudo tee-a
/etc/yum.repos.d/tarantool.repo
# GENTOO commands for Tarantool stable binary download: # Tarantool is available from tarantool portage overlay. Use layman to add the overlay to your system: layman-S
layman-a
tarantool emergedev-db/tarantool
-av
# ANY-LINUX commands for Tarantool stable binary download: # If you have a GNU/Linux distribution which is not one of the above, # or if you want to install on your own subdirectory without affecting /usr /etc etc., # start your browser and go to # http://tarantool.org/download.html download page. # Look for words similar to "Other Linux distributions". You will want the # binary tarball (.tar.gz
) file for your release architecture (32-bit or 64-bit). # Click on either "32-bit" or "64-bit" depending on your release architecture. # This will cause a download of the latest stable tarball. # Suppose it istarantool-1.5.1-133-g11edda1-linux-x86_64.tar.gz
. Say: tarzxvf
tarantool-1.5.1-133-g11edda1-linux-x86_64.tar.gz
# You now have a directory named tarantool-1.5.1-133-g11edda1-linux-x86_64. # Let's move it to ~/tarantool, which is an easier name to remember. mvtarantool-1.5.1-133-g11edda1-linux-x86_64 ~/tarantool
# Within it there is a subdirectory/bin
containing both server and client.
# FREEBSD commands for Tarantool stable binary download: # With your browser go to the FreeBSD ports page # http://www.freebsd.org/ports/index.html # Enter the search term: tarantool # Choose the package you want ... # However, there is a known issue with the binary of Tarantool # version 1.5, see # https://github.com/tarantool/tarantool/issues/19.
# MAC OS X commands for Tarantool stable binary download: # This is actually a “homebrew” recipe # so it's not a true binary download, some source code is involved. # First upgrade Clang (the C compiler) to version 3.2 or later using # Command Line Tools for Xcode disk image version 4.6+ from Apple Developer web-site. brew install --use-clang http://tarantool.org/dist/tarantool.rb # or brew install http://tarantool.org/dist/tarantool.rb
More advice for binary downloads is at http://tarantool.org/download.html.
For downloading Tarantool source and building it, the platforms can differ and the preferences can differ. But the steps are always the same. Here in the manual we'll explain what the steps are, then on the Internet you can look at some example scripts.
1. Get tools and libraries that will be necessary for building and testing. The absolutely necessary ones are:
A program for downloading source repositories. In this case the necessary program is “git”. Although tarantool.org/dist has source tarballs (the files whose names end in “-src.tar.gz”), the latest complete source downloads are on github.com, and from github one gets with git.
A C/C++ compiler. Ordinarily the compiler is GCC version 4.5 or later, on Mac OS X it should be Clang version 3.2 or later. There may be some benefit in rebuilding gcc to suit tarantool requirements.
A program for managing the build process. This is always CMake for GNU/Linux and FreeBSD.
Here are names of tools and libraries which may have to be installed in advance,
using “sudo apt-get
” (for Ubuntu), “sudo yum install
” (for CentOS),
or the equivalent on other platforms. Different platforms may use slightly
different names. Do not worry about the “optional, for build with -DENABLE_DOC”
ones unless you intend to work on the documentation.
binutils-dev or binutils-devel # contains GNU bfd for printing stack traces gcc or clang # see above git # see above uuid-dev # optional, for box_uuid_* functions cmake # see above libreadline-dev # optional, for build with -DENABLE_CLIENT libncurses5-dev or ncurses-devel # optional, for build with -DENABLE_CLIENT xsltproc # optional, for build with -DENABLE_DOC lynx # optional, for build with -DENABLE_DOC jing # optional, for build with -DENABLE_DOC libxml2-utils # optional, for build with -DENABLE_DOC docbook5-xml # optional, for build with -DENABLE_DOC docbook-xsl-ns # optional, for build with -DENABLE_DOC w3c-sgml-lib # optional, for build with -DENABLE_DOC libsaxon-java # optional, for build with -DENABLE_DOC libxml-commons-resolver1.1-java # optional, for build with -DENABLE_DOC libxerces2-java # optional, for build with -DENABLE_DOC libxslthl-java # optional, for build with -DENABLE_DOC autoconf # optional, appears only in Mac OS scripts zlib1g or zlib # optional, appears only in Mac OS scripts
2. Pick a default directory.
This can be anywhere. We'll assume that your default directory is “~”, and therefore
the tarantool download will go inside it, as
.
~/tarantool
3. Use git to download from github.com.
cd ~ git clone-b stable
https://github.com/tarantool/tarantool.gittarantool
The optional argument “-b stable” causes download from the stable branch instead of the master branch,
and the optional last word on the line, “tarantool”, means download is to ~/tarantool
.
4. Use git again so that third-party contributions will be seen as well.
This step is only necessary once, the first time you do a download.
There is an alternative -- say “git clone --recursive
” in step 3 --
but we prefer this method because it works with older versions of git.
cd ~/tarantool git submodule init git submodule update cd ../
5. Use CMake to initiate the build.
cd ~/tarantool make clean # unnecessary, added for good luck rm CMakeCache.txt # unnecessary, added for good luck cmake . # The command to initiate with build type=Debug, no client, no doc
The option for specifying build type is -DCMAKE_BUILD_TYPE=
type
where
type = {None | Debug | Release | RelWithDebInfo | MinSizeRel} and a reasonable
choice for production is -DCMAKE_BUILD_TYPE=RelWithDebInfo
(“Debug”
is used only by project maintainers and “Release” is used only when the
highest performance is required).
The option for asking to build client is
and a reasonable choice is -DENABLE_CLIENT=
{true|false}
.
The option for asking to build documentation is -DENABLE_CLIENT=true
and the assumption is that only a minority will need to rebuild the
documentation (such as what you're reading now), so details about
documentation are in the developer manual, and the reasonable choice
is -DENABLE_DOC=
{true|false}
or just don't use the -DENABLE_DOC=false
clause at all.
-DENABLE_DOC
6. Use make to complete the build.
make
It's possible to say “make install
” too, but that's not generally done.
7. Set up python modules for running the test suite. This step is optional.
Say:
python --version
... You should see that the python version is between 2.6 and 3.
On Ubuntu you can get modules from the repository:
sudo apt-get install python-daemon python-yaml python-argparse python-pexpect
On CentOS too you can get modules from the repository:
sudo yum install python26 python26-PyYAML python26-argparse
But in general it is best to set up the modules by getting
a tarball and doing the setup with python setup.py
, thus:
# python module for parsing YAML (pyYAML): # (If wget fails, check the PyYAML web site to see what the current version is.) cd ~ wget http://pyyaml.org/download/pyyaml/PyYAML-3.10.tar.gz tar-xzf
PyYAML-3.10.tar.gz cd PyYAML-3.10 sudo python setup.py install # python module for spawning child applications (pexpect): # (If wget fails, check the python-pexpect web site to see what the current version is.) cd ~ wget http://pypi.python.org/packages/source/p/pexpect-u/pexpect-u-2.5.1.tar.gz tar-xzvf
pexpect-u-2.5.1.tar. cd pexpect-u-2.5.1 sudo python setup.py install # python module for assisting programs to turn themselves into daemons (daemon): # (if wget fails, check the python-daemon web site to see what the current version is.) cd ~ wget http://pypi.python.org/packages/source/d/daemon/daemon-1.0.tar.gz tar-xzvf
daemon-1.0.tar.gz cd daemon-1.0 sudo python setup.py install
8. Run the test suite. This step is optional.
Tarantool's developers always run the test suite before they publish new versions. You should run the test suite too, if you
make any changes in the code.
Assuming you downloaded to ~/tarantool
,
the principal steps are:
mkdir ~/tarantool/bin # make a subdirectory named bin
ln usr/bin/python ~/tarantool/bin/python # link python to bin
cd ~/tarantool/test #get on the test subdirectory
PATH=~/tarantool/bin:$PATH ./run #run tests using python
The output should contain reassuring reports, for example
======================================================================
TEST RESULT
------------------------------------------------------------
box/admin.test [ pass ]
box/admin_coredump.test [ pass ]
box/args.test [ pass ]
box/cjson.test [ pass ]
box/configuration.test [ pass ]
box/errinj.test [ pass ]
box/fiber.test [ pass ]
... etc.
There are more than 70 tests in the suite.
To prevent later confusion, clean up what's in the bin
subdirectory:
rm ~/tarantool/bin/python rmdir ~/tarantool/bin
9. Make an rpm.
This step is optional. It's only for people who want to redistribute Tarantool.
Ordinarily it should be skipped. It will add a new subdirectory: ~/tarantool/RPM
.
make rpm
This is the end of the list of steps to take for source downloads.
For your added convenience, github.com has README files with example scripts: README.CentOS for CentOS 5.8, README.FreeBSD for FreeBSD 8.3, README.MacOSX for Mac OS X “Lion”, README.md for generic GNU/Linux. These example scripts assume that the intent is to download from the master branch, build the server and the client (but not the documentation), and run tests after build.
To build with SUSE 13.1, the steps are as described above, except that the appropriate YaST2 package names are:
binutils-devel, libuuid-devel, cmake, ncurses-devel, lynx, jing, libxml2-devel, docbook_5, saxon, libxslt-devel.
The python connector can be installed with sudo easy_install pip
and sudo pip install tarantool
.
Here is how to create a simple test database after installing.
1. Create a new directory. It's just for tests, you can delete it when the tests are over.
mkdir ~/tarantool_sandbox cd ~/tarantool_sandbox mkdir work_dir
2. Create a configuration file. The Tarantool server requires a configuration
file with some definitions of ports and database objects.
The server, by default, looks for its configuration file in
the current working directory and in
.
Enter the following commands
which make a minimal configuration file that will be suitable for day one.
etc/
echo "slab_alloc_arena = 0.1" | tee tarantool.cfg echo "pid_file = \"box.pid\"" | tee-a
tarantool.cfg echo "primary_port = 33013" | tee-a
tarantool.cfg echo "secondary_port = 33014" | tee-a
tarantool.cfg echo "admin_port = 33015" | tee-a
tarantool.cfg echo "rows_per_wal = 50000" | tee-a
tarantool.cfg echo "space[0].enabled = 1" | tee-a
tarantool.cfg echo "space[0].index[0].type = \"HASH\"" | tee-a
tarantool.cfg echo "space[0].index[0].unique = 1" | tee-a
tarantool.cfg echo "space[0].index[0].key_field[0].fieldno = 0" | tee-a
tarantool.cfg echo "space[0].index[0].key_field[0].type = \"NUM\"" | tee-a
tarantool.cfg echo "logger = \"tee --append tarantool.log\"" | tee-a
tarantool.cfg echo "work_dir = \"work_dir\"" | tee-a
tarantool.cfg (With some downloads a tarantool.cfg file like this is already available in a test subdirectory.)
Initialize the storage area. You only have to do this once.
/usr/bin/tarantool_box --init-storage #if you downloaded a binary with apt-get or yum ~/tarantool/bin/tarantool_box --init-storage #if you downloaded and untarred a binary tarball to ~/tarantool ~/tarantool/src/box/tarantool_box --init-storage #f you built from a source download
Start the server.
The server name is
.tarantool_box
/usr/bin/tarantool_box #if you downloaded a binary with apt-get or yum ~/tarantool/bin/tarantool_box #if you downloaded and untarred a binary tarball to ~/tarantool ~/tarantool/src/box/tarantool_box #f you built from a source download
If all goes well, you will see the server displaying progress as it initializes, something like this:
2013-10-18 20:20:36.806 [16560] 1/sched C> version 1.5.1-141-ga794d35 2013-10-18 20:20:36.830 [16560] 1/sched I> Loading plugin: /usr/lib/tarantool/plugins/libmysql.so 2013-10-18 20:20:37.016 [16560] 1/sched I> Plugin 'mysql' was loaded, version: 1 2013-10-18 20:20:37.016 [16560] 1/sched I> Loading plugin: /usr/lib/tarantool/plugins/libpg.so 2013-10-18 20:20:37.044 [16560] 1/sched I> Plugin 'postgresql' was loaded, version: 1 2013-10-18 20:20:37.044 [16560] 1/sched I> space 0 successfully configured 2013-10-18 20:20:37.044 [16560] 1/sched I> recovery start 2013-10-18 20:20:37.060 [16560] 1/sched I> recover from `./00000000000000000001.snap' 2013-10-18 20:20:37.060 [16560] 1/sched I> snapshot recovered, confirmed lsn: 1 2013-10-18 20:20:37.070 [16560] 1/sched I> done `./00000000000000000002.xlog' confirmed_lsn: 2 2013-10-18 20:20:37.070 [16560] 1/sched I> WALs recovered, confirmed lsn: 2 2013-10-18 20:20:37.070 [16560] 1/sched I> building secondary indexes 2013-10-18 20:20:37.070 [16560] 1/sched I> bound to primary port 33013 2013-10-18 20:20:37.070 [16560] 1/sched I> I am primary 2013-10-18 20:20:37.070 [16560] 1/sched I> bound to secondary port 33014 2013-10-18 20:20:37.070 [16560] 1/sched I> bound to admin port 33015 2013-10-18 20:20:37.071 [16560] 1/sched C> log level 4 2013-10-18 20:20:37.071 [16560] 1/sched C> entering event loop
Now take the server down, with
Ctrl+C
Now start the server again. This time start it in the background.
/usr/bin/tarantool_box --background #if you downloaded a binary with apt-get or yum ~/tarantool/bin/tarantool_box --background #if you downloaded and untarred a binary tarball to ~/tarantool ~/tarantool/src/box/tarantool_box --background #f you built from a source download
If all went well, there is now an instance of the Tarantool server running in the background. You can confirm that with the command:
ps -a
| grep tarantool_box
or look at the log file:
less work_dir/tarantool.log
Please follow distribution-specific instructions to find out how to manage Tarantool instances on your operating system.
Alternatively, the server can be started right out of the in-source build. Use the Tarantool regression testing framework:
$
./test/run--start-and-exit
It will create necessary files in directory
./test/var/
, and start the server with
minimal configuration.
Now that the server is up, you can start the client. The client name is tarantool.
/usr/bin/tarantool #If you downloaded a binary with apt-get or yum: ~/tarantool/bin/tarantool #If you downloaded and untarred a binary tarball to ~/tarantool: ~/tarantool/client/tarantool/tarantool #If you built from a source download on ~tarantool
If all goes well, a prompt will appear:
localhost>
The client is waiting for the user to type instructions.
To insert three “tuples” (our name for “records”) into the first “space” of the database (which is called t0), try this:
localhost>
INSERT INTO t0 VALUES (1)
localhost>
INSERT INTO t0 VALUES (2,'Music')
localhost>
INSERT INTO t0 VALUES (3,'length',93)
To select a tuple from the first space of the database, using the first defined key (which is called k0), try this:
localhost>
SELECT * FROM t0 WHERE k0 = 3
Your terminal screen should now look like this:
localhost> INSERT INTO t0 VALUES (1) Insert OK, 1 rows affected localhost> INSERT INTO t0 VALUES (2,'Music') Insert OK, 1 rows affected localhost> INSERT INTO t0 VALUES (3,'Length',93) Insert OK, 1 rows affected localhost> SELECT * FROM t0 WHERE k0 = 3 Select OK, 1 rows affected [3, 'Length', 93] localhost>>
You can repeat INSERT and SELECT indefinitely.
When the testing is over:
To stop the client: Ctrl+C.
To stop the server: sudo pkill -f tarantool_box
.
To destroy the test: rm -r ~/tarantool_sandbox
.
This chapter describes how Tarantool stores values and what operations with data it supports.
If you tried out the “Starting Tarantool and making your first database” exercise from the last chapter, then your database looks like this:
+--------------------------------------------+ | | | SPACE 'space[0]' | | +----------------------------------------+ | | | | | | | TUPLE SET 't0' | | | | +-----------------------------------+ | | | | | Tuple: [ 1 ] | | | | | | Tuple: [ 2, 'Music' ] | | | | | | Tuple: [ 3, 'length', 93 ] | | | | | +-----------------------------------+ | | | | | | | | INDEX 'index[0]' | | | | +-----------------------------------+ | | | | | Key: 1 | | | | | | Key: 2 | | | | | | Key: 3 | | | | | +-----------------------------------+ | | | | | | | +----------------------------------------+ | +--------------------------------------------+
A space -- 'space[0]' in the example -- is a container.
There is always at least one space; there can be many spaces, numbered as space[0], space[1], and so on. Spaces always contain one tuple set and one or more indexes.
A tuple set -- 't0' in the example -- is a group of tuples.
There is always one tuple set in a space. For the tarantool client, the identifier of a tuple set is “t” followed by the space's number, for example “t0” refers to the tuple set of space[0]. (The letter “t” stands for “tuple set.”)
A tuple fills the same role as a “row” or a “record”, and the components of a tuple (which we call “fields”) fill the same role as a “row column” or “record field”, except that: the fields of a tuple don't need to have names. That's why there was no need to pre-define the tuple set in the configuration file, and that's why each tuple can have a different number of elements, and that's why we say that Tarantool has a “dynamic” data model.
Any given tuple may have any number of fields and the fields may have any of these three types: NUM (32-bit unsigned integer between 0 and 2,147,483,647), NUM64 (64-bit unsigned integer between 0 and 18,446,744,073,709,551,615), or STR (string, any sequence of octets). The identifier of a field is “k” followed by the field's number, for example “k0” refers to the first field of a tuple.
This manual is following the tarantool client convention by using tuple identifier = “t” followed by the space's number, and using field identifier = “k” followed by the field's number. The server knows nothing about such identifiers, it only cares about the number. Other clients follow different conventions, and may even have sophisticated ways of mapping meaningful names to numbers.
When the tarantool client displays a tuple, it surrounds
strings with single quotes, separates fields with commas,
and encloses the tuple inside square brackets. For example:
[ 3, 'length', 93 ]
.
An index -- 'index[0]' in the example -- is a group of key values and pointers.
There is always at least one index in a space; there can be many. The identifier of an index is 'index' followed by the index's number within the space, so in our example there is one index and its identifier is “index[0]”.
An index may be multi-field, that is, the user can declare that an index key value is taken from two or more fields in the tuple, in any order. An index may be unique, that is, the user can declare that it would be illegal to have the same key value twice. An index may have one of three types: HASH which is fastest and uses the least memory but must be unique, TREE which allows partial-key searching and ordered results, and BITSET which can be good for searches that contain '=' and 'AND' in the WHERE clause. The first index -- index[0] -- is called the “primary key” index and it must be unique; all other indexes -- index[1], index[2], and so on -- are “secondary” indexes.
An index definition always includes at least one identifier of a tuple field and its expected type. Take our example configuration file, which has the lines:
space[0].index[0].key_field[0].fieldno = 0 space[0].index[0].key_field[0].type = "NUM"
The effect is that, for all tuples in t0, field number 0 (k0) must exist and must be a 32-bit unsigned integer.
For the current version of the Tarantool server, space definitions and index definitions must be in the configuration file. Administrators must take care that what's in the configuration file matches what's in the database. If a server is started with the wrong configuration file, it could behave in an unexpected way or crash. However, it is possible to stop the server or disable database accesses, then add new spaces and indexes, then restart the server or re-enable database accesses. The syntax details for defining spaces and indexes are in chapter 7 Configuration reference.
The basic operations are: the four data-change operations (INSERT, UPDATE, DELETE, REPLACE), and the data-retrieval operation (SELECT). There are also minor operations like “ping” which are not available via the tarantool client's SQL-like interface but can only be used with the binary protocol. Also, there are index iterator operations, which can only be used with Lua stored procedures. (Index iterators are for traversing indexes one key at a time, taking advantage of features that are specific to an index type, for example evaluating Boolean expressions when traversing BITSET indexes, or going in descending order when traversing TREE indexes.)
Five examples of basic operations:
/* Add a new tuple to tuple set t0. The first field, k0, will be 999 (type is NUM). The second field, k1, will be 'Taranto' (type is STR). */ INSERT INTO t0 VALUES (999,'Taranto') /* Update the tuple, changing field k1. The clause "WHEREprimary-key-field-identifier
=value
is mandatory because UPDATE statements must always have a WHERE clause that specifies the primary key, which in this case is k0. */ UPDATE t0 SET k1 = 'Tarantino' WHERE k0 = 999 /* Replace the tuple, adding a new field. This is not possible with the UPDATE statement because the SET clause of an UPDATE statement can only refer to fields that already exist. */ REPLACE INTO t0 VALUES (999,'Tarantella',Tarantula') /* Retrieve the tuple. The WHERE clause is still mandatory, although it does not have to mention the primary key. */ SELECT * FROM t0 WHERE k0 = 999 /* Delete the tuple. Once again the clause "WHERE k0 =value
is mandatory. */ DELETE FROM t0 WHERE k0 = 999
How does Tarantool do a basic operation? Let's take this example:
UPDATE t0 SET k1 = 'size', k2=0 WHERE k0 = 3
STEP #1: the client parses the statement and changes it to a binary-protocol instruction which has already been checked, and which the server can understand without needing to parse everything again. The client ships a packet to the server.
STEP #2: the server's “transaction processor” thread uses the primary-key index on field k0 to find the location of the tuple in memory. It determines that the tuple can be updated (not much can go wrong when you're merely changing an unindexed field value to something shorter).
STEP #3: the transaction processor thread sends a message to the write-ahead logging (WAL) thread.
At this point a “yield” takes place. To know the significance of that -- and it's quite significant -- you have to know a few facts and a few new words.
FACT #1: there is only one transaction processor thread. Some people are used to the idea that there can be multiple threads operating on the database, with (say) thread #1 reading row #x while thread#2 writes row#y. With Tarantool no such thing ever happens. Only the transaction processor thread can access the database, and there is only one transaction processor thread for each instance of the server.
FACT #2: the transaction processor thread can handle many fibers. A fiber is a set of computer instructions that may contain “yield” signals. The transaction processor thread will execute all computer instructions until a yield, then switch to execute the instructions of a different fiber. Thus (say) the thread reads row#x for the sake of fiber#1, then writes row#y for the sake of fiber#2.
FACT #3: yields must happen, otherwise the transaction processor thread would stick permanently on the same fiber. There are implicit yields: every data-change operation or network-access causes an implicit yield, and every statement that goes through the tarantool client causes an implicit yield. And there are explicit yields: in a Lua stored procedure one can and should add “yield” statements to prevent hogging. This is called cooperative multitasking.
Since all data-change operations end with an implicit yield and an implicit commit, and since no data-change operation can change more than one tuple, there is no need for any locking. Consider, for example, a stored procedure that does three operations:
SELECT /* this does not yield and does not commit */ UPDATE /* this yields and commits */ SELECT /* this does not yield and does not commit */
The combination “SELECT plus UPDATE” is an atomic transaction: the stored procedure holds a consistent view of the database until the UPDATE ends. For the combination “UPDATE plus SELECT” the view is not consistent, because after the UPDATE the transaction processor thread can switch to another fiber, and delete the tuple that was just updated.
Since locks don't exist, and disk writes only involve the write-ahead log, transactions are usually fast. Also the Tarantool server may not be using up all the threads of a powerful multi-core processor, so advanced users may be able to start a second Tarantool server on the same processor without ill effects.
Additional examples of SQL statements can be found in the Tarantool regression test suite. A complete grammar of supported SQL is provided in the Language reference chapter.
Since not all Tarantool operations can be expressed in SQL, to gain
complete access to data manipulation functionality one must use
a Perl, Python, Ruby or other
programming language connector. The client/server
protocol is open and documented: an annotated BNF can be found
in the source tree, file doc/protocol.txt
.
To maintain data persistence, Tarantool writes each data change
request (INSERT, UPDATE, DELETE) into a write-ahead log (WAL)
file in the wal_dir directory.
A new WAL file is created for every rows_per_wal records. Each data change request
gets assigned a continuously growing 64-bit log sequence number.
The name of the WAL file is based on the log sequence
number of the first record in the file, plus an extension .xlog
.
Apart from a log sequence number and the data change request
(its format is the same as in the binary protocol and is described
in doc/box-protocol.txt
),
each WAL record contains a checksum and a UNIX time stamp.
For example this is what the WAL file looks like after the first INSERT
statement ("INSERT INTO t0 VALUES (1)") for the introductory sandbox exercise
“Starting Tarantool and making your first database”.
On the left are the hexadecimal bytes that one would see with
$
hexdump work_dir/00000000000000000002.xlog
and on the right are comments.
Hex dump of WAL file Comment -------------------- ------- 58 4c 4f 47 0a File header: "XLOG\n" 30 2e 31 31 0a 0a File header: "0.11\n\n" = version ed ab 0b ba Magic row marker always = 0xba0babed for version 0.11 10 2a 1b 5e Record header: crc32 checksum of header fields 02 00 00 00 00 00 00 00 Record header: 2 = (64-bit int) log sequence number 99 c8 f1 d9 32 a8 d4 41 Record header: (double) unix time-since-epoch for transaction 1d 00 00 00 Record header: 29 = length of rest of record, non-inclusive 60 45 94 cd Record header: crc32 checksum of data fields fe ff Tag = XLOG which is #defined as 65534 02 00 99 f9 7f 00 00 01 Session cookie, comes from struct sockaddr_in *addr 0d 00 00 00 Data change request code = insert which is #defined as 13 00 00 Space number = 0 02 00 00 00 Flags = BOX_ADD which is defined as 2 01 00 00 00 Tuple: Count of fields in tuple = 1 04 Tuple: field[0] length = 4 because value is 4 bytes long 01 00 00 00 Tuple: field[0] value = 1
Tarantool processes requests atomically: a change is either accepted and recorded in the WAL, or discarded completely. Let's clarify how this happens, using REPLACE command as an example:
The server attempts to locate the original tuple by primary key. If found, a reference to the tuple is retained for later use.
The new tuple is then validated. If it violates a unique-key constraint, misses an indexed field, or an index-field type does not match the type of the index, the change is aborted.
The new tuple replaces the old tuple in all existing indexes.
A message is sent to WAL writer running in a separate thread, requesting that the change is recorded in the WAL. The server switches to work on the next request until the write is acknowledged.
On success, a confirmation is sent to the client. Upon failure, a rollback procedure is begun. During the rollback procedure, the transaction processor rolls back all changes to the database which occurred after the first failed change, from latest to oldest, up to the first failed change. All rolled back requests are aborted with ER_WAL_IO error. No new change is applied while rollback is in progress. When the rollback procedure is finished, the server restarts the processing pipeline.
One advantage of the described algorithm is that complete request pipelining is achieved, even for requests on the same value of the primary key. As a result, database performance doesn't degrade even if all requests touch upon the same key in the same space.
The transaction processor and the WAL writer threads communicate using asynchronous (yet reliable) messaging; the transaction processor thread, not being blocked on WAL tasks, continues to handle requests quickly even at high volumes of disk I/O. A response to a request is sent as soon as it is ready, even if there were earlier incomplete requests on the same connection. In particular, SELECT performance, even for SELECTs running on a connection packed with UPDATEs and DELETEs, remains unaffected by disk load.
The WAL writer employs a number of durability modes, as defined in configuration variable wal_mode. It is possible to turn the write ahead log completely off, by setting wal_mode to none. Even without the write ahead log it's still possible to take a persistent copy of the entire data set with the SAVE SNAPSHOT statement.
This chapter provides a reference of Tarantool data operations and administrative commands.
Unlike many other key/value servers, Tarantool uses different TCP ports and client/server protocols for data manipulation and administrative statements. During start up, the server can connect to up to five TCP ports:
Read/write data port, to handle INSERTs, UPDATEs, DELETEs, SELECTs and CALLs. This port speaks the native Tarantool protocol, and provides full data access.
The default value of the port is 33013
,
as defined in primary_port
configuration option.
Read only port, which only accepts SELECTs and CALLs,
default port number 33014
, as defined in
secondary_port configuration option.
Administrative port, which defaults to 33015
,
and is defined in admin_port
configuration option.
Replication port (see replication_port), by default set to
33016
, used to send updates to
replicas. Replication is optional, and if this port is not
set in the configuration file, the corresponding server process
is not started.
Memcached port. Optional, read-write data port that speaks Memcached text protocol. This port is off by default.
In absence of authentication, this approach allows system administrators to restrict access to read/write or administrative ports. The client, however, has to be aware of the separation, and tarantool command line client automatically selects the correct port for you with help of a simple regular expression. SELECTs, UPDATEs, INSERTs, DELETEs and CALLs are sent to the primary port. SHOW, RELOAD, SAVE and other statements are sent to the administrative port.
Five basic request types are supported: INSERT, UPDATE, DELETE, SELECT and CALL. All requests, including INSERT, UPDATE and DELETE may return data. A SELECT can be requested to limit the number of returned tuples. This is useful when searching in a non-unique index or when a special “wildcard” (zero-length string) value is supplied as search key or a key part.
UPDATE statement supports operations on fields — assignment, arithmetic operations (the field must be numeric), cutting and pasting fragments of a field, — as well as operations on a tuple: push and pop of a field at the tail of a tuple, deletion and insertion of a field. Multiple operations can be combined into a single update, and in this case they are performed atomically. Each operation expects field number as its first argument. When a sequence of changes is present, field identifier in each operation is assumed to be relative to the most recent state of the tuple, i.e. as if all previous operations in a multi-operation update have already been applied. In other words, it's always safe to merge multiple UPDATE statements into a single one, with no change in semantics.
Tarantool protocol was designed with focus on asynchronous I/O and easy integration with proxies. Each client request starts with a 12-byte binary header, containing three fields: request type, length, and a numeric id.
The mandatory length, present in request header simplifies client or proxy I/O. A response to a request is sent to the client as soon as it is ready. It always carries in its header the same type and id as in the request. The id makes it possible to match a request to a response, even if the latter arrived out of order.
Request type defines the format of the payload. INSERTs, UPDATEs and DELETEs can only be made by the primary key, so an index id and a key (possibly multipart) are always present in these requests. SELECTs can use secondary keys. UPDATE only needs to list the fields that are actually changed. With this one exception, all commands operate on whole tuple(s).
Unless implementing a client driver, one needn't
concern oneself with the complications of the binary
protocol. Language-specific
drivers provide a friendly way to store domain
language data structures in Tarantool, and the command line
client supports a subset of standard SQL.
A complete description of both, the binary protocol and
the supported SQL, is maintained in annotated Backus-Naur
form in the source tree: please see
doc/box-protocol.txt
and
doc/sql.txt
respectively.
If full access to Tarantool functionality is not needed, or there is no readily available connector for the programming language in use, any existing client driver for Memcached will make do as a Tarantool connector. To enable text Memcached protocol, turn on memcached_port in the configuration file. Since Memcached has no notion of spaces or secondary indexes, this port only makes it possible to access one dedicated space (see memcached_space) via its primary key. Unless tuple expiration is enabled with memcached_expire, TTL part of the message is stored but ignored.
Notice, that memcached_space is also accessible using the primary port or Lua. A common use of the Memcached port in Tarantool is when a Memcached default expiration algorithm is insufficient, and a custom Lua expiration procedure is used.
Tarantool does not support the binary protocol of Memcached. If top performance is a must, Tarantool's own binary protocol should be used.
The administrative console uses a simple text protocol. All commands are case-insensitive. You can connect to the administrative port using any telnet client, or a tool like rlwrap, if access to readline features is desired. Additionally, tarantool, the SQL-capable command line client, understands all administrative statements and automatically directs them to the administrative port. The server response to an administrative command, even though it is always in plain text, can be quite complex. It is encoded using YAML markup to simplify automated parsing.
To learn about all supported administrative commands, you can type help in the administrative console. A reference description also follows below:
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. For
example:
localhost> show info --- info: version: "1.4.6" lsn: 843301 ... localhost> save snapshot --- ok ... localhost> save snapshot --- fail: 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 save snapshot started must be kept for recovery, since it still contains log records written after the start of save 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.
Re-read the configuration file. If the file contains changes to dynamic parameters, update the runtime settings. If configuration syntax is incorrect, or a read-only parameter is changed, produce an error and do nothing.
Show the current settings. Displays all settings, including those that have default values and thus are not necessarily present in the configuration file.
localhost>
show info
--- info: version: "1.5.2-8-g54a279d" uptime: 441524 pid: 12315 logger_pid: 12316 snapshot_pid: 0 lsn: 15481913304 recovery_lag: 0.000 recovery_last_update: 1306964594.980 status: primary config: "/usr/local/etc/tarantool.cfg"
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>".
Show the number of keys and the amount of memory used by each each index.
Show the average number of requests per second, and the total number of requests since startup, broken down by request type: INSERT or SELECT or UPDATE or DELETE."
localhost> show stat --- statistics: INSERT: { rps: 139 , total: 48207694 } SELECT_LIMIT: { rps: 0 , total: 0 } SELECT: { rps: 1246 , total: 388322317 } UPDATE_FIELDS: { rps: 1874 , total: 743350520 } DELETE: { rps: 147 , total: 48902544 }
Show the statistics of the slab allocator. The slab allocator is the main allocator used to store tuples. This can be used to monitor the total memory use and memory fragmentation.
items_used contains the % of slab_alloc_arena already used to store tuples.
arena_used contains the % of slab_alloc_arena that is already distributed to the slab allocator.
bytes_waste contains the amount of memory wasted by the slab allocator due to internal fragmentation.
A pool allocator is used for temporary memory, when serving client requests. Every fiber has its own temporary pool. Shows the current state of pools of all fibers.
Fork and dump a core. Since Tarantool stores all tuples in memory, it can take some time. Mainly useful for debugging.
Show all running fibers, with their stack. Mainly useful for debugging.
Execute a chunk of Lua code. This can be used to define, invoke, debug and drop stored procedures, inspect server environment, perform automated administrative tasks.
Lua is a light-weight, multi-paradigm, embeddable language. Stored procedures in Lua can be used to implement data manipulation patterns or data structures. A server-side procedure written in Lua can select and modify data, access configuration and perform administrative tasks. It is possible to dynamically define, invoke, alter and drop Lua procedures. Lua procedures can run in the background and perform administrative tasks, such as data expiration or re-sharding.
Tarantool uses the LuaJIT just-in-time Lua compiler and virtual machine. Apart from increased performance, this provides such features as bitwise operations and 64-bit integer arithmetic.
Procedures can be invoked from the administrative console and using the binary protocol, for example:
localhost> lua function f1() return 'hello' end
---
...
localhost> call f1()
Call OK, 1 rows affected
['hello']
In the language of the administrative console LUA ... evaluates an arbitrary Lua chunk. CALL is an SQL standard statement, so its syntax was adopted by Tarantool command line client to invoke the CALL command of the binary protocol.
In the example above, "lua function f1() return 'hello' end
" defines a Lua procedure
using the text protocol of the administrative port,
and "call f1()
" invokes the procedure using the Tarantool client-side SQL
parser plus the binary protocol on the primary_port.
Since it's possible to execute any Lua chunk in the
administrative console, the newly created function f1()
can be invoked there too:
localhost> lua f1()
---
- hello
...
localhost> lua 1+2
---
- 3
...
localhost> lua "hello".." world"
---
- hello world
...
Lua procedures could also be called at the time of initialization
using a dedicated init.lua
script,
located in script_dir.
An example of such a script is given below:
-- Importing expirationd module dofile("expirationd.lua") function is_expired(args, tuple) if tuple == nil then return true end if #tuple <= args.field_no then return true end field = tuple[args.field_no] if field == nil or #field ~= 4 then return true end local current_time = os.time() local tuple_ts = box.unpack("i", field) return current_time >= tuple_ts + args.ttl end function purge(args, tuple) box.space[0]:delete(tuple[0]) end -- Run task expirationd.run_task("exprd space 0", 0, is_expired, purge, { field_no = 1, ttl = 30 * 60 })
The initialization script can select and modify data. However, if the server is a running replica, data change requests from the start script fail just the same way they would fail if they were sent from a remote client.
Another common task to perform in the initialization script is to start background fibers for data expiration, re-sharding, or communication with networked peers.
Finally, the script can be used to define Lua triggers invoked on various events within the system.
There is a single global instance of the Lua interpreter, which is
shared across all connections. Anything prefixed with
lua
on the administrative console is sent
directly to this interpreter. Any change of the interpreter
state is immediately available to all client connections.
Each connection, however, is using its own Lua coroutine — a mechanism akin to Tarantool fibers. A coroutine has an own execution stack and a Lua closure — set of local variables and definitions.
The interpreter environment is not restricted when init.lua is loaded. But before the server starts accepting requests, the standard Lua APIs, such as for file I/O, process control and module management are unset, to avoid possible trivial security attacks.
In the binary protocol, it's only possible to call existing procedures, but not define or alter them. The CALL request packet contains the command code for CALL (22), the name of a procedure to be called, and a tuple for procedure arguments. Currently, Tarantool tuples are type-agnostic, thus each field of the tuple is passed into the procedure as an argument of type “string”. For example:
kostja@atlas:~$ cat arg.lua
function f1(a)
local s = a
if type(a) == 'string' then
s = ''
for i=1, #a, 1 do
s = s..string.format('0x%x ', string.byte(a, i))
end
end
return type(a), s
end
kostja@atlas:~$ tarantool
localhost> lua dofile('arg.lua')
---
...
localhost> lua f1('1234')
---
- string
- 0x31 0x32 0x33 0x34
...
localhost> call f1('1234')
Call OK, 2 rows affected
['string']
['0x31 0x32 0x33 0x34 ']
localhost> lua f1(1234)
---
- number
- 1234
...
localhost> call f1(1234)
Call OK, 2 rows affected
['string']
['0xd2 0x4 0x0 0x0 ']
In the above example, the way the procedure receives its argument is identical in the two protocols, when the argument is a string. A numeric field, however, when submitted via the binary protocol, is seen by the procedure as a 4-byte blob, not as a Lua “number” type.
In addition to conventional method invocation, Lua provides object-oriented syntax. Access to the latter is available on the administrative console only:
localhost> lua box.space[0]:truncate()
---
...
localhost> call box.space[0]:truncate()
error: 1:15 expected '('
Since it's impossible to invoke object methods from the binary protocol, the object-oriented syntax is often used to restrict certain operations to be used by a system administrator only.
Every value, returned from a stored function by means of a
return
clause, is converted to a Tarantool tuple.
Tuples are returned as such, in binary form; a Lua scalar, such as
a string or an integer, is converted to a tuple with only
one field. When the returned value is a Lua
table, the resulting tuple contains only table
values, but not keys.
When a function in Lua terminates with an error, the error is sent to the client as ER_PROC_LUA return code, with the original error message preserved. Similarly, an error which has occurred inside Tarantool (observed on the client as an error code), when it happens during execution of a Lua procedure, produces a genuine Lua error:
localhost> lua function f1() error("oops") end
---
...
localhost> call f1()
Call ERROR, Lua error: [string "function f1() error("oops") end"]:1: oops (ER_PROC_LUA)
localhost> call box.insert('99', 1, 'test')
Call ERROR, Space 99 is disabled (ER_SPACE_DISABLED)
localhost> lua pcall(box.insert, 99, 1, 'test')
---
- false
- Space 99 is disabled
...
It's possible not only to invoke trivial Lua code, but call
into Tarantool storage functionality, using the
box
Lua library. The contents of the library can be
inspected at runtime:
localhost> lua for k, v in pairs(box) do print(k, ": ", type(v)) end
---
fiber: table
space: table
cfg: table
on_reload_configuration: function
update: function
process: function
delete: function
insert: function
select: function
index: table
unpack: function
replace: function
select_range: function
pack: function
...
As is shown in the listing, the box
package contains:
high-level functions, such as
process(), update(), select(), select_range(), insert(),
replace(), delete()
, to manipulate
tuples and access spaces from Lua.
libraries, such as cfg, space, fiber, index, tuple
,
to access server configuration, create, resume and
interrupt fibers, inspect contents of spaces, indexes
and tuples, send and receive data over the network.
Global Lua names added by Tarantool
Convert a given string or a Lua number to a 64-bit integer. The returned value supports all arithmetic operations, but uses 64-bit integer arithmetic, rather than floating-point arithmetic as in the built-in number type.
localhost>lua tonumber64('123456789'), tonumber64(123456789)
--- - 123456789 - 123456789 ... localhost>lua i=tonumber64(1)
--- ... localhost>lua type(i), type(i*2), type(i/2), i, i*2, i/2
--- - cdata - cdata - cdata - 1 - 2 - 0 ...
Process a request passed in as a binary string. This is an entry point into the server request processor. It can be used to insert, update, select and delete tuples from within a Lua procedure.
The box.process
API is a low-level API, and it expects
all arguments to be packed in accordance
with the binary protocol (excluding the iproto
header). Normally, there is no need
to use box.process()
directly:
box.select(), box.update()
and other convenience wrappers
invoke box.process()
with
correctly packed arguments.
op — number, any
Tarantool command code, except 22 (CALL). See
doc/box-protocol.txt .
|
request — command
arguments packed in binary format. |
This function returns zero or more tuples. In Lua, a
tuple is represented by a
userdata object of type
box.tuple
. If
a Lua procedure is called from the administrative
console, returned tuples are printed out in YAML
format. When called from the binary
protocol, the binary format is used.
Any server error produced by the executed command.
Please note that, since all requests from Lua enter the core through box.process(), all checks and triggers run by the core automatically apply. For example, if the server is in read-only mode, an update or delete fails. Analogously, if a system-wide "instead of" trigger is defined, it is run.
Search for a tuple or tuples in the given space. A
wrapper around box.process()
.
space_no — space id,
|
index_no — index number in the
space, to be used for match |
... — index key,
possibly multipart.
|
Returns zero or more tuples.
Same as in box.process()
. Any error
results in a Lua exception.
localhost>call box.insert(0, 'test', 'my first tuple')
Call OK, 1 rows affected ['test', 'my first tuple'] localhost>call box.select(0, 0, 'test')
Call OK, 1 rows affected ['test', 'my first tuple'] localhost>lua box.insert(5, 'testtest', 'firstname', 'lastname')
--- - 'testtest': {'firstname', 'lastname'} ... localhost>lua box.select(5, 1, 'firstname', 'lastname')
--- - 'testtest': {'firstname', 'lastname'} ...
Insert a tuple into a space. Tuple fields
follow space_no
. If a tuple with
the same primary key already exists,
box.insert()
returns an error.
This function is a
wrapper around box.process()
.
Returns the inserted tuple.
Search for tuples in the given space. This is a full version of the built-in SELECT command, in which one can specify offset and limit for a multi-tuple return. The server may return multiple tuples when the index is non-unique or a partial key is used for search.
Insert a tuple into a space. Tuple fields
follow space_no
. If a tuple with
the same primary key already exists,
box.replace()
replaces the existing
tuple with a new one. This function is a
wrapper around box.process()
.
Returns the inserted tuple.
Update a tuple.
The space_no
and key
arguments
identify the tuple; if a key is multipart then it is
passed as a Lua table.
The format
argument is a sequence of
pairs of characters, where the first character in each pair
is the operation specifier, and the second character in
each pair is the operation argument.
The {field_no, value}
arguments are the
field numbers of affected fields and applicable values.
For some operations the field number can be -1, meaning
the last field in the tuple.
There must be a pair of {field_no, value} arguments
for each character pair in the format argument.
The format and {field_no, value} arguments are passed to
box.pack()
and the result is sent
to box.process()
.
Possible operation specifiers are: “+”
for addition, “-” for subtraction,
“&” for bitwise AND,
“|” for bitwise OR, “^”
for bitwise exclusive OR (XOR), “:”
for string splice, “!” for insertion.
Possible operation arguments are: “p”.
Thus in the instruction lua box.update(0,44,'+p=p',1,55,3,'x')
the space number is 0, the primary-key value is 44,
the format is '+p=p' meaning "add a value to a field
and then assign a value to a field", the first affected field
is field 1 and the value which will be added to it is 55, the second affected field
is field 3 and the value which will be assigned to it is 'x'.
Returns the updated tuple.
#Assume that the initial state of the database is ... # space[0] has one tuple set and one primary key whose type is 32-bit integer. # There is one row, with field[0] = 999 and field[1] = 'A'. #In the following update ... # The first argument is 0, that is, the affected space is space[0] # The second argument is 999, that is, the affected tuple is identified by primary key value = 999 # The third argument is '=p', that is, there is one operation, assignment to a field # The fourth argument is 1, that is, the affected field is field[1] # The fifth argument is 'B', that is, field[1] contents change to 'B' # Therefore, after the following update, field[0] = 999 and field[1] = 'B'. lua box.update(0, 999, '=p', 1, 'B') #In the following update, the arguments are the same, except that ... # the key is passed as a Lua table (inside braces). This is unnecessary # when the primary key has only one field, but would be necessary if the # primary key had more than one field. # Therefore, after the following update, field[0] = 999 and field[1] = 'B' (no change). lua box.update(0, {999}, '=p', 1, 'B') #In the following update, the arguments are the same, except that ... # The fourth argument is 2, that is the affected field is field[2]. # It is okay that, until now, field[2] has not existed. It gets added. # Therefore, after the following update, field[0] = 999, field[1] = 'B', field[2] = 1. lua box.update(0, 999, '=p', 2, 1) #In the following update, the arguments are the same, except that ... # The third argument is '+p', that is, the operation is addition rather than assignment. # Since field[2] previously contained 10, this means we're adding 1 to 1. # Therefore, after the following update, field[0] = 999, field[1] = 'B', field[2] = 2. lua box.update(0, 999, '+p', 2, 1) #In the following update ... # The idea is to modify two fields at once. # The third argument is '|p=p', that is, there are two operations, OR and assignment. # The fourth and fifth arguments mean that field[2] gets ORed with 1. # The fifth and sixth arguments mean that field[1] gets assigned 'C'. # Therefore, after the following update, field[0] = 999, field[1] = 'C', field[2] = 3. lua box.update(0, 999, '|p=p', 2, 1, 1, 'C') #In the following update ... # The idea is to delete field[1], then subtract 3 from field[2], but ... # after the delete, there is a renumbering -- so field[2] becomes field[1] # before we subtract 3 from it, and that's why the sixth argument is 1 not 2. # Therefore, after the following update, field[0] = 999, field[1] = 0. lua box.update(0, 999, '#p-p', 1, 0, 1, 3) #In the following update ... # We're making a long string so that the splice will work in the next example. # Therefore, after the following update, field[0[ = 999, field[1] = 'XYZ'. lua box.update(0, 999, '=p', 1, 'XYZ') #In the following update ... # The third argument is ':p', that is, this is the example of splice. # The fifth argument is actually four arguments packed together ... # a filler, an offset, the number of bytes to cut (1), and the string to add ('!') # Therefore, after the following update, field[0[ = 999, field[1] = 'X!Z'. lua box.update(0, 999, ':p', 1, box.pack('ppp', 1, 1, '!'))
Delete a tuple identified by a primary key.
Returns the deleted tuple.
localhost>call box.delete(0, 'test')
Call OK, 1 rows affected ['test', 'my first tuple'] localhost>call box.delete(0, 'test')
Call OK, 0 rows affected localhost>call box.delete(0, 'tes')
Call ERROR, Illegal parameters, key is not u32 (ER_ILLEGAL_PARAMS)
Select a range of tuples, starting from the offset
specified by key
. The key can be
multipart. Limit selection with at most
limit
tuples. If no key is specified,
start from the first key in the index.
For TREE indexes, this returns tuples in sorted order.
For HASH indexes, the order of tuples is unspecified, and
can change significantly if data is inserted or deleted
between two calls to box.select_range()
.
If key
is nil
or unspecified,
the selection starts from the start of the index.
This is a simple wrapper around box.space[space_no]:select_range(index_no, ...)
.
BITSET index does not support this call.
localhost>show configuration
--- ... space[4].cardinality: "-1" space[4].estimated_rows: "0" space[4].index[0].type: "HASH" space[4].index[0].unique: "true" space[4].index[0].key_field[0].fieldno: "0" space[4].index[0].key_field[0].type: "STR" space[4].index[1].type: "TREE" space[4].index[1].unique: "false" space[4].index[1].key_field[0].fieldno: "1" space[4].index[1].key_field[0].type: "STR" ... localhost>insert into t4 values ('0', '0')
Insert OK, 1 rows affected localhost>insert into t4 values ('1', '1')
Insert OK, 1 rows affected localhost>insert into t4 values ('2', '2')
Insert OK, 1 rows affected localhost>insert into t4 values ('3', '3')
Insert OK, 1 rows affected localhost>lua box.select_range(4, 0, 10)
--- - '3': {'3'} - '0': {'0'} - '1': {'1'} - '2': {'2'} ... localhost>lua box.select_range(4, 1, 10)
--- - '0': {'0'} - '1': {'1'} - '2': {'2'} - '3': {'3'} ... localhost>lua box.select_range(4, 1, 2)
--- - '0': {'0'} - '1': {'1'} ... localhost>lua box.select_range(4, 1, 2, '1')
--- - '1': {'1'} - '2': {'2'} ...
Select a reverse range of tuples, starting from the offset
specified by key
. The key can be
multipart.
Limit selection with at most limit
tuples.
If no key is specified, start from the last key in
the index.
For TREE indexes, this returns tuples in sorted order.
For other index types this call is not supported.
If key
is nil
or unspecified,
the selection starts from the end of the index.
localhost>show configuration
--- ... space[4].cardinality: "-1" space[4].estimated_rows: "0" space[4].index[0].type: "HASH" space[4].index[0].unique: "true" space[4].index[0].key_field[0].fieldno: "0" space[4].index[0].key_field[0].type: "STR" space[4].index[1].type: "TREE" space[4].index[1].unique: "false" space[4].index[1].key_field[0].fieldno: "1" space[4].index[1].key_field[0].type: "STR" ... localhost>insert into t4 values ('0', '0')
Insert OK, 1 rows affected localhost>insert into t4 values ('1', '1')
Insert OK, 1 rows affected localhost>insert into t4 values ('2', '2')
Insert OK, 1 rows affected localhost>insert into t4 values ('3', '3')
Insert OK, 1 rows affected localhost>lua box.select_reverse_range(4, 0, 10)
--- error: 'Illegal parameters, hash iterator is forward only ... localhost>lua box.select_reverse_range(4, 1, 10)
--- - '3': {'3'} - '2': {'2'} - '1': {'1'} - '0': {'0'} ... localhost>lua box.select_reverse_range(4, 1, 2)
--- - '3': {'3'} - '2': {'2'} ... localhost>lua box.select_reverse_range(4, 1, 2, '1')
--- - '1': {'1'} - '0': {'0'} ...
To use Tarantool binary protocol primitives from Lua, it's necessary to convert Lua variables to binary format. This helper function is prototyped after Perl 'pack'. It takes a format and a list of arguments, and returns a binary string with all arguments packed according to the format.
b — converts Lua
variable to a 1-byte
integer, and stores the integer in the resulting
string
|
s — converts Lua
variable to a 2-byte
integer, and stores the integer in the resulting
string, low byte first,
|
i — converts Lua
variable to a 4-byte
integer, and stores the integer in the resulting
string, low byte first,
|
l — converts Lua
variable to a 8-byte
integer, and stores the integer in the resulting
string, low byte first,
|
n — converts Lua
variable to a 2-byte
integer, and stores the integer in the resulting
string, big endian,
|
N — converts Lua
variable to a 4-byte
integer, and stores the integer in the resulting
string, big endian,
|
Q — converts Lua
variable to a 8-byte
integer, and stores the integer in the resulting
string, big endian,
|
f — converts Lua
variable to a 4-byte
float, and stores the float in the resulting
string,
|
d — converts Lua
variable to a 8-byte
double, and stores the double in the resulting
string,
|
w — converts Lua
integer to a BER-encoded integer,
|
p — stores the length
of the argument as a BER-encoded integer
followed by the argument itself (a little-endian 4-byte integer for integers,
and a binary blob for other types),
|
=, +, &, |, ^, : —
stores the corresponding Tarantool UPDATE
operation code: field assignment, addition,
conjunction, disjunction, exclusive disjunction,
splice (from Perl SPLICE function). Expects
field number to update as an argument. These format
specifiers only store the corresponding operation
code and field number to update, but do not
describe operation arguments.
|
Unknown format specifier.
localhost>lua box.insert(0, 0, 'hello world')
--- - 0: {'hello world'} ... localhost>lua box.update(0, 0, "=p", 1, 'bye world')
--- - 0: {'bye world'} ... localhost>lua box.update(0, 0, ":p", 1, box.pack('ppp', 0, 3, 'hello'))
--- - 0: {'hello world'} ... localhost>lua box.update(0, 0, "=p", 1, 4)
--- - 0: {4} ... localhost>lua box.update(0, 0, "+p", 1, 4)
--- - 0: {8} ... localhost>lua box.update(0, 0, "^p", 1, 4)
--- - 0: {12} ...
Counterpart to box.pack()
.
localhost>lua tuple=box.replace(2, 0)
--- ... localhost>lua string.len(tuple[0])
--- - 4 ... localhost>lua box.unpack('i', tuple[0])
--- - 0 ... localhost>lua box.unpack('bsil', box.pack('bsil', 255, 65535, 4294967295, tonumber64('18446744073709551615')))
--- - 255 - 65535 - 4294967295 - 18446744073709551615 ... localhost>lua num, str, num64 = box.unpack('ppp', box.pack('ppp', 666, 'string', tonumber64('666666666666666')))
--- ... localhost>lua print(box.unpack('i', num));
--- 666 ... localhost>lua print(str);
--- string ... localhost>lua print(box.unpack('l', num64))
--- 666666666666666 ... localhost>lua box.unpack('=p', box.pack('=p', 1, '666'))
--- - 1 - 666
Redefines Lua print()
built-in to print either to the log file
(when Lua is used from the binary port) or back to the user (for the
administrative console).
When printing to the log file, INFO log level is used. When printing to the administrative console, all output is sent directly to the socket.
Note: the administrative console output must be YAML-compatible.
Evaluates an arbitrary chunk of Lua code passed in
s
. If there is a compilation error,
it is raised as a Lua error. If there is no compilation
error, all arguments which follow s
are passed to the compiled chunk and the chunk is
invoked.
This function is mainly useful to define and run an arbitrary piece of Lua code, without having to introduce changes to the global Lua environment.
localhost>lua box.dostring('abc')
--- error: '[string "abc"]:1: ''='' expected near ''<eof>''' ... localhost>lua box.dostring('return 1')
--- - 1 ... localhost>lua box.dostring('return ...', 'hello', 'world')
--- - hello - world ... localhost>lua box.dostring('local f = function(key) t=box.select(0, 0, key); if t ~= nil then return t[0] else return nil end end return f(...)', 0)
--- - nil ...
Returns current system time (in seconds since the epoch) as a Lua number. The time is taken from the event loop clock, which makes this call very cheap, but still useful for constructing artificial tuple keys.
localhost> lua box.time(),box.time()
---
- 1385758759.2591
- 1385758759.2591
...
Returns current system time (in seconds) as a 64-bit integer. The time is taken from the event loop clock.
localhost> lua box.time(),box.time64()
---
- 1385758828.9825
- 1385758828982485
...
Returns a 128-bit (16-byte) unique id in binary form.
Requires libuuid library to be installed. The library is loaded at runtime, and if the library is not available, this function returns an error.
localhost> lua box.uuid() == box.uuid() -- Comment: == means "are they equal?"
---
- false
...
Returns a 32-byte hexadecimal conversion of a 128-bit unique id, as a string.
localhost> lua box.uuid_hex()
---
- b8eadcb078b54bed8fa8425d129b10e8
...
Raises a client error. The difference between this function
and the built-in error()
function in Lua
is that when the error reaches the client, its error code
is preserved, whereas every Lua error is presented to the
client as ER_PROC_LUA
. This function
makes it possible to emulate any kind of native exception,
such as unique constraint violation, no such space/index,
etc. A complete list of errors is present in errcode.h
file in the source tree.
Lua constants which correspond to Tarantool errors
are defined in box.error
module. The error
message can be arbitrary.
Throws client error. Lua procedure can emulate any
request errors (for example: unique key exception).
localhost> lua box.raise(box.error.ER_WAL_IO, 'Wal I/O error')
---
error: 'Wal I/O error'
...
Insert values into space designated by space_no, using an auto-increment primary key. The space must have a NUM or NUM64 primary key index of type TREE.
localhost>lua box.auto_increment(0, "I am a duplicate")
--- - 1: {'I am a duplicate'} ... localhost>lua box.auto_increment(0, "I am a duplicate")
--- - 2: {'I am a duplicate'} ...
Increments a counter identified by the key. The key can be multipart, but there must be an index covering all fields of the key. If there is no tuple identified by the given key, creates a new one with initial counter value set to 1. Returns the new counter value.
localhost>lua box.counter.inc(0, 'top.mail.ru')
--- - 1 ... localhost>lua box.counter.inc(0, 'top.mail.ru')
--- - 2 ...
Decrements a counter identified by the given key. If the key is not found, is a no-op. When counter value drops to 0, the tuple is deleted.
localhost>lua box.counter.dec(0, 'top.mail.ru')
--- - 1 ... localhost>lua box.counter.dec(0, 'top.mail.ru')
--- - 0 ...
This 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.
Construct a new tuple from a Lua table or a scalar.
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.
In the following example, x and t will be new tuple objects.
Saying lua t
returns the entire tuple t.
localhost>lua x=box.insert(0,'a',tonumber('1'),tonumber64('2')):totable()
--- ... localhost>lua t=box.tuple.new({'abc','def','ghi','abc'})
--- ... localhost>lua t
--- - 'abc': {'def', 'ghi', 'abc'} ...
The # operand in Lua means "return count of components".
So, if t is a tuple instance, #t
will return the number of
fields.
In the following example, a tuple named t is created
and then the number of fields in t is returned.
localhost>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4'})
--- ... localhost>lua #t
--- - 4 ...
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.
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,
so bsize() returns 3*(1+3).
localhost>lua t=box.tuple.new({'aaa','bbb','ccc'})
--- ... localhost>lua t:bsize()
--- - 12 ...
If t is a tuple instance, t[
will return the
n
]n
th field in the tuple. The first field is t[0].
In the following example, a tuple named t is created
and then the second field in t is returned.
localhost>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4'})
--- ... localhost>lua t[1]
--- - Field#2 ...
If t is a tuple instance, t:find(
will return the number of the first field in t that matches search-value, and
search-value
)t:findall(
will return numbers of all fields in t that match search-value. Optionally
one can put a numeric argument n before the search-value to indicate
“start searching at field number n.”
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 after the second field
are returned.
search-value
)
localhost>lua t=box.tuple.new({'a','b','c','a'})
--- ... localhost>lua t:find('a')
--- - 0 ... localhost>lua t:findall('a')
--- - 0 - 3 ... localhost>lua t:findall(1,'a')
--- - 3 ...
If t is a tuple instance, t:transform(
will return a tuple where, starting from field n1, a number of fields (n2) are removed.
Optionally one can add more arguments after n2 to indicate new values that will replace
what was removed.
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.
n1
,n2
)
localhost> lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'})
---
...
localhost> lua t:transform(1,2,'x')
---
- 'Field#1': {'x', 'Field#4', 'Field#5'}
...
If t is a tuple instance, t:slice(
will return all fields starting with field number n, and
n
)t:slice(
will return a tuple containing fields starting with field number n1, but
stopping before field number n2.
In the following example, a tuple named t is created
and then, starting from the second field, fields before the fourth field are selected,
then the result is returned.
n1
,n2
)
localhost>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'})
--- ... localhost>lua t:slice(1,3)
--- - Field#2 - Field#3 ...
If t is a tuple instance, t:unpack(
will return all fields. In effect, n
)unpack()
is the same as slice(0,-1)
.
In the following example, a tuple named t is created
and then all its fields are selected,
then the result is returned.
localhost>lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'})
--- ... localhost>lua t:unpack()
--- - Field#1 - Field#2 - Field#3 - Field#4 - Field#5 ...
In Lua, pairs() is a method which returns: function, value, nil. It is useful for Lua iterators, because Lua iterators traverse a value's components until an end marker is reached. In the following example, a tuple named t is created and then all its fields are selected using a Lua for-end loop.
localhost> lua t=box.tuple.new({'Field#1','Field#2','Field#3','Field#4','Field#5'})
---
...
localhost> lua for k,v in t:pairs() do print(v) end
---
Field#1
Field#2
Field#3
Field#4
Field#5
...
This package provides JSON manipulation routines. It's based on the Lua-CJSON package by Mark Pulford. For a complete manual on Lua-CJSON please read the official documentation.
Convert a Lua object to a JSON string.
localhost>lua box.cjson.encode(123)
--- - 123 ... localhost>lua box.cjson.encode({123})
--- - [123] ... localhost>lua box.cjson.encode({123, 234, 345})
--- - [123,234,345] ... localhost>lua box.cjson.encode({abc = 234, cde = 345})
--- - {"cde":345,"abc":234} ... localhost>lua box.cjson.encode({hello = { 'world' } })
--- - {"hello":["world"]} ...
Convert a JSON string to a Lua object.
localhost>lua box.cjson.decode('123')
--- - 123 ... localhost>lua box.cjson.decode('[123, "hello"]')[2]
--- - hello ... localhost>lua box.cjson.decode('{"hello": "world"}').hello
--- - world ...
This package is a container for all
configured spaces. A space object provides access to space
attributes, such as id, whether or not a space is
enabled, space cardinality, and estimated number of rows. It also
contains object-oriented versions of box
functions. For example, instead of box.insert(0, ...)
one can write box.space[0]:insert(...)
.
Package source code is available in file src/box/lua/box.lua
A list of all space
members follows.
box.space[i].n == i
A container for all defined indexes. An index is a Lua object
of type box.index
with
methods to search tuples and iterate over them in predefined order.
localhost> lua box.space[0].n, box.space[0].enabled, box.space[0].cardinality, box.space[0].index[0].type
---
- 0
- true
- 0
- HASH
...
lua box.space[space_number
]:select(index_number...
)
is the object-oriented equivalent of
box.select(space_number
,index_number...
)
.
lua box.space[space_number
]:select_range(index_number...
)
is the object-oriented equivalent of
box.select_range(space_number
,index_number...
)
.
lua box.space[space_number
]:select_reverse_range(index_number...
)
is the object-oriented equivalent of
box.select_reverse_range(space_number
,index_number...
)
.
lua box.space[space_number
]:insert(key
)
is the object-oriented equivalent of
box.insert(space_number
,key
)
.
lua box.space[space_number
]:replace(key
)
is the object-oriented equivalent of
box.replace(space_number
,key
)
.
lua box.space[space_number
]:delete(key
)
is the object-oriented equivalent of
box.delete(space_number
,key
)
.
lua box.space[space_number
]:update(key
)
is the object-oriented equivalent of
box.update(space_number
,key
)
.
Returns number of tuples in the space.
localhost> lua box.space[0]:len()
---
- 2
...
Deletes all tuples.
localhost>lua box.space[0]:truncate()
--- ... localhost>lua box.space[0]:len()
--- - 0 ...
A helper function to iterate over all space tuples, Lua style.
localhost> lua for k,v in box.space[0]:pairs() do print(v) end
---
1: {'hello'}
2: {'my '}
3: {'Lua '}
4: {'world'}
...
This package implements methods of type box.index
.
Indexes are contained in box.space[i].index[]
array
within each space object. They provide an API for
ordered iteration over tuples. This API is a direct
binding to corresponding methods of index objects in the
storage engine.
The underlying userdata which does all the magic.
localhost> lua box.space[0].index[0].unique,box.space[0].index[0].type,box.space[0].index[0].key_field[0],box.space[0].index[0].idx
---
- true
- HASH
- table: 0x41d03518
- index 0 in space 0
...
This method provides iteration support within an
index. Parameter type
is used to
identify the semantics of iteration. Different
index types support different iterators. The
remaining arguments of the function are varying
and depend on the iteration type. For example,
a TREE index maintains a strict order of keys and
can return all tuples in ascending or descending
order, starting from the specified key. Other
index types, however, do not support ordering.
To understand consistency of tuples returned by an iterator, it's essential to know the principles of the Tarantool transaction processing subsystem. An iterator in Tarantool does not own a consistent read view. Instead, each procedure is granted exclusive access to all tuples and spaces until it encounters a "context switch": by causing a write to disk, network, or by an explicit call to box.fiber.yield(). When the execution flow returns to the yielded procedure, the data set could have changed significantly. Iteration, resumed after a yield point, does not preserve the read view, but continues with the new content of the database.
type — iteration strategy as defined in tables below. |
This method returns an iterator closure, i.e.
a function
which can be used to
get the next value on each invocation.
Selected iteration type is not supported in the subject index type or supplied parameters do not match iteration type.
Table 4.1. Common iterator types
Type | Arguments | HASH | TREE | BITSET | Description |
---|---|---|---|---|---|
box.index.ALL | none | yes | yes | yes | Iterate over all tuples in an index. When iterating over a TREE index, tuples are returned in ascending order of the key. When iterating over a HASH or BITSET index, tuples are returned in physical order or, in other words, unordered. |
box.index.EQ | key | yes | yes | yes |
Equality iterator: iterate over all tuples matching the key. Parts of a multipart key need to be separated by comma. Semantics of the match depends on the index. A HASH index only supports exact match: all parts of a key participating in the index must be provided. In case of TREE index, only few parts of a key or a key prefix are accepted for search. In this case, all tuples with the same prefix or matching key parts are considered matching the search criteria. When a TREE index is not unique, or only part of a key is given as a search criteria, matching tuples are returned in ascending order. BITSET and HASH indexes are always unique. |
box.index.GT | key | yes (*) | yes | no |
Iterate over tuples strictly greater than the search key.
For TREE indexes, a key prefix or key part can be sufficient.
If the key is nil , iteration starts from
the smallest key in the index. The tuples are returned
in ascending order of the key.
HASH index also supports this iterator type, but returns
tuples in unspecified order. However, if the server
does not receive updates, this iterator can be used
to retrieve all tuples via a HASH index piece by piece,
by supplying the last key from the previous range as the
start key for an iterator over the next range.
BITSET index does not support this iteration type yet.
|
Table 4.2. TREE iterator types
Type | Arguments | Description |
---|---|---|
box.index.REQ | key or key part |
Reverse equality iterator. Is equivalent to
box.index.EQ with only distinction that
the order of returned tuples is descending, not
ascending.
|
box.index.GE | key or key part |
Iterate over all tuples for which the corresponding
fields are greater or equal to the search key. The
tuples are returned in ascending order. Similarly to
box.index.EQ , key prefix or key part can
be used to seed the iterator. If the key is
nil , iteration starts from the smallest
key in the index.
|
box.index.LT | key or key part |
Similar to box.index.GT ,
but returns all tuples which are strictly less
than the search key. The tuples are returned
in the descending order of the key.
nil key can be used to start
from the end of the index range.
|
box.index.LE | key or key part |
Similar to box.index.GE , but
returns all tuples which are less or equal to the
search key or key prefix, and returns tuples
in descending order, from biggest to smallest.
If the key is nil , iteration starts
from the end of the index range.
|
Table 4.3. BITSET iterator types
Type | Arguments | Description |
---|---|---|
box.index.BITS_ALL_SET | bit mask | Matches tuples in which all specified bits are set. |
box.index.BITS_ANY_SET | bit mask | Matches tuples in which any of the specified bits is set. |
box.index.BITS_ALL_NOT_SET | bit mask | Matches tuples in which none of the specified bits is set. |
localhost>show configuration
--- ... space[0].enabled: "true" space[0].index[0].type: "HASH" space[0].index[0].unique: "true" space[0].index[0].key_field[0].fieldno: "0" space[0].index[0].key_field[0].type: "NUM" space[0].index[1].type: "TREE" space[0].index[1].unique: "false" space[0].index[1].key_field[0].fieldno: "1" space[0].index[1].key_field[0].type: "NUM" space[0].index[1].key_field[1].fieldno: "2" space[0].index[1].key_field[1].type: "NUM" ... localhost>INSERT INTO t0 VALUES (1, 1, 0)
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES (2, 1, 1)
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES (3, 1, 2)
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES (4, 2, 0)
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES (5, 2, 1)
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES (6, 2, 2)
Insert OK, 1 rows affected localhost>lua it = box.space[0].index[1]:iterator(box.index.EQ, 1); print(it(), " ", it(), " ", it());
--- 1: {1, 0} 2: {1, 1} 3: {1, 2} ... localhost>lua it = box.space[0].index[1]:iterator(box.index.EQ, 1, 2); print(it(), " ", it(), " ", it());
--- 3: {1, 2} nil nil ... localhost>lua i = box.space[0].index[1]:iterator(box.index.GE, 2, 1); print(it(), " ", it(), " ", it());
--- 5: {2, 1} 6: {2, 2} nil ... localhost>lua for v in box.space[0].index[1]:iterator(box.index.ALL) do print(v) end
--- 1: {1, 0} 2: {1, 1} 3: {1, 2} 4: {2, 0} 5: {2, 1} 6: {2, 2} ... localhost>lua i = box.space[0].index[0]:iterator(box.index.LT, 1);
--- error: 'Iterator type is not supported'
The smallest value in the index. Available only for indexes of type 'TREE'.
localhost> lua box.space[0].index[0]:min()
---
- 'Alpha': {55, 'This is the first tuple!'}
...
The biggest value in the index. Available only for indexes of type 'TREE'.
localhost> lua box.space[0].index[0]:max()
---
- 'Gamma': {55, 'This is the third tuple!'}
...
Return a random value from an index. A random non-negative integer must be supplied as input, and a value is selected accordingly in index-specific fashion. This method is useful when it's important to get insight into data distribution in an index without having to iterate over the entire data set.
localhost> lua box.space[0].index[0]:random(1)
---
- 1635018050: {66, 'This is the second tuple!'}
...
Iterate over an index, counting the number of tuples which equal the provided search criteria. The argument can either point to a tuple, a key, or one or more key parts. Returns the number of matched tuples.
localhost> lua box.space[0].index[0]:count("Alpha")
---
- 1
...
Functions in this package allow for creating, running and managing fibers.
A fiber is a set of instructions which are executed
with cooperative multitasking. Fibers managed by the
box.fiber library are associated with a user-supplied function
called the fiber function.
A fiber has three possible states: running, suspended or dead.
When a fiber is created with box.fiber.create()
, it is suspended.
When a fiber is started with box.fiber.resume()
, it is running.
When a fiber yields control with box.fiber.yield()
, it is suspended.
When a fiber ends (because the fiber function ends), it is dead.
A fiber can also be attached or detached.
An attached fiber is a child of the creator,
and is running only if the creator has called
box.fiber.resume()
. A detached fiber is a child of
the Tarantool internal “sched” fiber, and gets
scheduled only if there is a libev event associated
with it.
To detach, a running fiber must invoke box.fiber.detach()
.
A detached fiber loses connection with its parent forever.
All fibers are part of the fiber registry, box.fiber
.
This registry can be searched (box.fiber.find()
)
either by fiber id (fid), which is numeric, or by fiber name,
which is a string. If there is more than one fiber with the given
name, the first fiber that matches is returned.
A runaway fiber can be stopped with box.fiber.cancel()
.
However, box.fiber.cancel()
is advisory — it works
only if the runaway fiber calls box.fiber.testcancel()
once in a while. Most box.*
hooks, such as box.delete()
or box.update()
, do call box.fiber.testcancel()
.
box.select()
does not.
In practice, a runaway fiber can only become unresponsive
if it does many computations and does not check
whether it's been canceled.
The other potential problem comes from detached
fibers which never get scheduled, because they are not subscribed
to any events, or because no relevant events occur. Such morphing fibers
can be killed with box.fiber.cancel()
at any time,
since box.fiber.cancel()
sends an asynchronous wakeup event to the fiber,
and box.fiber.testcancel()
is checked whenever such an event occurs.
Like all Lua objects, dead fibers are garbage collected. The garbage collector frees pool allocator memory owned by the fiber, resets all fiber data, and returns the fiber (now called a fiber carcass) to the fiber pool. The carcass can be reused when another fiber is created.
box.fiber
userdata
object for the currently scheduled fiber.function
. There will be an error if the function does not exist or if a recursion limit is hit.
box.fiber.yield()
arguments passed to box.fiber.yield are returned after temporarily
yielding control back to the scheduler.
time
seconds.
Only the current fiber can be made to sleep.
fiber
. If no argument is
provided, the current fiber's status is returned. The
status can be one of: “dead”,
“suspended”, “attached”
or “running”.
fiber
.
Running and suspended fibers can be canceled.
Returns an error if the subject fiber does not permit cancel.
Make the function which will be associated with the fiber. When this function gets invoked, it will immediately "detach" so it will be running independently of the caller, and then will enter an infinite loop ("while 0 == 0" is an infinite loop). Each iteration of the loop adds 1 to a global variable named gvar, then goes to sleep for 2 seconds, then yields. The sleep causes an implicit box.fiber.yield().
localhost>
setopt delimiter = '!'
localhost>
lua function function_x ()
->
box.fiber.detach()
->
gvar = 0
->
while 0 == 0 do
->
gvar = gvar + 1
->
box.fiber.sleep(2)
->
end
->
end!
--- ...localhost>
setopt delimiter = ''!
Make the fiber and associate the function with it. Get the id of the fiber (fid), to be used in later displays.
localhost>
lua fiber_of_x = box.fiber.create(function_x)
--- ...localhost>
lua fid = box.fiber.id(fiber_of_x)
--- ...
"Resume" the fiber. This causes invocation of the function.
localhost>
lua box.fiber.resume(fiber_of_x)
--- ...
Pause for a while, while the detached function runs. Then ... Display the fiber id, the fiber status, and gvar (gvar will have gone up a bit depending how long the pause lasted). The status is suspended because the fiber spends almost all its time sleeping or yielding.
localhost>
lua print("fiber=",fid,". ",box.fiber.status(fiber_of_x),". gvar=",gvar)
--- fiber=104. suspended. gvar=9 ...
Pause for a while, while the detached function runs. Then ... Cancel the fiber. Then, once again ... Display the fiber id, the fiber status, and gvar (gvar will have gone up a bit more depending how long pause lasted). This time the status is dead because the cancel worked.
localhost>
lua box.fiber.cancel(fiber_of_x)
--- ...localhost>
lua print("fiber=",fid,". ",box.fiber.status(fiber_of_x),". gvar=",gvar)
--- fiber=104. dead. gvar=22 ...
Query session state, write to a session-specific temporary record, or set up triggers which will fire when a session starts or ends. A session is an object associated with each client connection.
localhost>
lua box.session.peer(box.session.id())
--- - 127.0.0.1:45129 ...localhost>
lua box.session.storage.random_memorandum = "Don't forget to buy eggs."
--- ...localhost>
lua box.session.storage.radius_of_mars = 3396
--- ...localhost>
lua for k,v in pairs(box.session.storage) do print(k,' ',v) end
--- radius_of_mars 3396 random_memorandum Don't forget to buy eggs. ...
See the triggers chapter
for instructions about defining triggers for connect and disconnect events
with box.session.on_connect()
and box.session.on_disconnect()
.
Send and receive messages between different procedures of a session.
Call box.ipc.channel()
to allocate space and get a channel object, which will be
called channel
for examples in this section.
Call the other box.ipc() routines, passing channel
, to send messages, receive messages, or check ipc status.
Message exchange is synchronous.
The channel is garbage collected when no one is using it, as with any
other Lua object.
Object-oriented and functional APIs are equivalent, so channel:put(message)
is the same as box.ipc.channel.put(channel, message)
.
box.ipc.channel.put()
blocks until there is a free slot in the channel.
If timeout
is provided,
and the channel doesn't become empty for the duration
of the timeout,
box.ipc.channel.put()
returns false. Otherwise it returns true.
box.ipc.channel.get()
blocks until there is a message.
If timeout
is provided,
and there are no new messages for the duration
of the timeout,
box.ipc.channel.get()
returns error.
box.ipc.channel.broadcast()
is equivalent to
box.ipc.channel.put()
.
Otherwise, box.ipc.channel.broadcast()
sends the message to all readers of the
channel.
box.ipc.channel.get()
and then
blocked).
Otherwise return false.
box.ipc.channel.put()
and then blocked
due to lack of room). Otherwise return false.
local channel = box.ipc.channel(10) function consumer_fiber() while true do local task = channel:get() ... end end function consumer2_fiber() while true do local task = channel:get(10) -- 10 seconds if task ~= nil then ... else print("timeout!") end end end function producer_fiber() while true do task = box.select(...) ... if channel:is_empty() then # channel is empty end if channel:is_full() then # channel is full end ... if channel:has_readers() then # there are some fibers that wait are waiting for data end ... if channel:has_writers() then # there are some fibers that are waiting for readers end channel:put(task) end end function producer2_fiber() while true do task = box.select(...) if channel:put(task, 10) then -- 10 seconds ... else print("timeout!") end end end
BSD sockets is a mechanism to exchange data with a local or
remote host in connection-oriented (TCP) or datagram-oriented
(UDP) mode.
Semantics of the calls in the box.socket
API closely follow
semantics of the corresponding POSIX calls. Function names
and signatures are mostly compatible with
luasocket.
Similarly to luasocket, box.socket
doesn't throw exceptions
on errors. On success, most calls return a socket object.
On error, a multiple return of nil, status, errno, errstr
is produced.
Status
can be one of "error"
, "timeout"
,
"eof"
or "limit"
. On
success, status is always nil
.
A call which returns data (recv()
, recvfrom()
,
readline()
) on success returns a Lua string of
the requested size and nil
status. On error or timeout,
an empty string is followed by the corresponding status, error number and message.
A call which sends data (send()
, sendto()
) on
success returns the number of bytes sent, and the status
is, again,
nil
. On error or timeout 0
is returned,
followed by status, error number and message.
The last error can be retrieved from the socket using
socket:error()
. Any call except error()
clears
the last error first (but may set a new one).
Calls which require a socket address and in POSIX expect
struct sockaddr_in
, in box.socket
simply accept host name and port as additional arguments.
Name resolution is done automatically. If it fails,
status is set to "error"
, errno is set to -1
and error string is set to "Host name resolution failed"
.
All calls that can take time block the calling fiber and can get it preempted. The implementation, however, uses non-blocking cooperative I/O, so Tarantool continues processing queries while a call is blocked. A timeout can be provided for any socket call which can take a long time.
As with all other box
libraries, the API can be used
in procedural style (e.g. box.socket.close(socket)
) as well
as in object-oriented style (socket:close()
).
A closed socket should not be used any more. Alternatively, the socket will be closed when its userdata is garbage collected by Lua.
Create a new TCP socket.
A new socket or nil
.
Create a new UDP socket.
A new socket or nil
.
Connect a socket to a remote host. Can be used with IPv6 and IPv4 addresses, as well as domain names. If multiple addresses correspond to a domain, tries them all until successfully connected.
Returns a connected socket on success,
nil, status, errno, errstr
on error or timeout.
Send data over a connected socket.
The number of bytes sent. On success, this is exactly
the length of data
. In case of error or timeout,
returns the number of bytes sent before error,
followed by status, errno, errstr
.
Read size
bytes from a connected socket.
An internal read-ahead buffer is used to reduce the cost
of this call.
A string of the requested length on success.
On error or timeout, returns an empty string, followed
by status, errno, errstr
.
If there was some data read before a timeout occurred, it
will be available on the next call.
In case the writing side has closed its end, returns the remainder
read from the socket (possibly an empty string),
followed by "eof"
status.
Read a line from a connected socket.
socket:readline()
with no arguments reads data from a socket
until '\n' or eof.
If a limit is set, the call reads data until a separator is found,
or the limit is reached. By default, there is no limit.
Instead of the default separator, a Lua table can be used
with one or multiple separators. Then the data is read
until the first matching separator is found.
A Lua string with data in case of success or an empty string in case of error. When multiple separators were provided in a separator table, the matched separator is returned as the third argument.
Table 4.4. readline()
returns
data, nil, separator | success |
"", "timeout", ETIMEDOUT, errstr | timeout |
"", "error", errno, errstr | error |
data, "limit" | limit |
data, "eof" | eof |
Bind a socket to the given host/port.
A UDP socket after binding can be used
to receive data (see recvfrom()
). A TCP socket
can be used to accept new connections, after it's
been put in listen mode.
The timeout is used for name resolution only. If host
name is an IP address, the call never yields and
the timeout is unused.
Socket object on success, nil, status, errno, errstr
on error.
Start listening for incoming connections. The listen
backlog, on Linux, is taken from /proc/sys/net/core/somaxconn
,
whereas on BSD it is set to SOMAXCONN
.
Socket on success, nil, "error", errno, errstr
on error.
Wait for a new client connection and create a connected socket.
peer_socket, nil, peer_host, peer_port
on success.
nil, status, errno, errstr
on error.
Send a message on a UDP socket to a specified host.
The number of bytes sent on success, 0, status, errno, errstr
on error or timeout.
Receive a message on a UDP socket.
Message, nil
, client address, client port on success,
"", status, errno, errstr
on error or timeout.
Shutdown a reading, writing or both ends of a socket. Accepts box.socket.SHUT_RD, box.socket.SHUT_WR and box.socket.SHUT_RDWR.
Socket on success, nil, "error", errno, errstr
on error.
Close (destroy) a socket. A closed socket should not be used any more.
Retrieve the last error that occurred on a socket.
errno, errstr
. 0, "Success"
if there is no error.
The box.net
library 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.box
.
This is for connecting to tarantool_box servers via a network.
Call box.net.box.new()
to connect and get a connection object,
which will be called conn
for examples in this section.
Call the other box.net.box()
routines, passing conn
,
to execute requests on the remote box.
Call box.net.box.close(conn)
to disconnect.
Object-oriented and functional APIs are equivalent, so
conn:close()
is the same as box.net.box.close(conn)
.
All box.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.
host
, port
[, reconnect_interval
])
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 argument
reconnect_interval
(in seconds) is
responsible for the amount of time the server
sleeps between failing attempts to reconnect.
The returned conn
object supports methods for making remote
requests, such as select, update or delete.
Example: conn = box.net.box.new('localhost', 33013)
.
For the local tarantool_box server there is a pre-created always-established
connection object named box.net.self
.
Its purpose is to make polymorphic use of the
box.net.box
API easier. Therefore
conn = box.net.box.new('localhost', 33013)
can
be replaced by conn = box.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.
Execute a PING command.
Returns true
on success,
false
on error. Example: self:ping()
.
Close a connection. Example: conn:close()
.
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.
space_no
, index_no
, ...)
conn:select(...)
is the remote-call equivalent of the local call box.select(...)
.
Please note this difference: a local box.select()
does not yield,
but a remote conn:select()
call does yield,
so local data may change while a remote conn:select()
is running.
space_no
, index_no
, offset
, limit
, ...)
conn:select_limit(...)
is the remote-call equivalent of the local call box.select_limit(...)
.
space_no
, index_no
, limit
, key
, ...)
conn:select_range(...)
is the remote-call equivalent of the local call box.select_range(...)
.
space_no
, ...)
conn:insert(...)
is the remote-call equivalent of the local call box.insert(...)
.
space_no
, ...)
conn:replace(...)
is the remote-call equivalent of the local call box.replace(...)
.
space_no
, key
, format
, ...)
conn:update(...)
is the remote-call equivalent of the local call box.update(...)
.
space_no
, key
)
conn:delete(...)
is the remote-call equivalent of the local call box.delete(...)
.
proc_name
[, arguments
])
conn:call('proc','1','2','3')
is the remote-call equivalent of CALL proc('1','2','3')
.
That is, box.net.box.call is a remote stored-procedure call.
Please keep in mind that the call is using
the binary protocol to pack procedure arguments,
and the binary protocol is type agnostic, so it's recommended
to pass all arguments of remote stored procedure calls as
strings. Example: conn:call("box.select_reverse_range", "1", "4", "10", "Smith")
.
timeout
)
timeout(...)
is a wrapper which sets a timeout for the request that follows it.
Example: conn:timeout(0):update('1', 'arg1', 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.
This example will work with the sandbox configuration described in the preface. That is, there is a space[0] with a numeric primary key. Assume that the database is nearly empty. Assume that the tarantool_box server is running on localhost 127.0.0.1:33013.
localhost>
setopt delimiter = '!'
localhost>
lua function example()
->
if self:ping() then
->
print("self:ping() succeeded (not surprising since self is a pre-established connection).")
->
end
->
if box.cfg.primary_port == 33013 then
->
print('The local server primary port number is 33013 (the default)')
->
else
->
print('The local server primary port number is not 33013, so connect will fail')
->
end
->
conn = box.net.box.new('127.0.0.1', 33013)
->
conn:delete(0,800)
->
print('conn:delete done on space[0].')
->
conn:insert(0,800,'data')
->
print('conn:insert done on space[0], index 0. primary key value = 800.')
->
wtuple = conn:select(0,0,800)
->
print('conn:select done on space[0], index 0. number of fields = ', #wtuple)
->
conn:delete(0,800)
->
print('conn:delete done on space[0].')
->
conn:replace(0,800,'New data','Extra data')
->
print('conn:replace done on space[0].')
->
conn:timeout(0):update(0,800,'=p=p=p',1, 'Field#1', 2,'Field#2', 3,'Field#3')
->
print('conn:update done on space[0], timed out after waiting 0 seconds for a return')
->
conn:close()
->
print('conn:close done')
->
end!
--- ...localhost>
setopt delimiter = ''!
localhost>
lua example()
--- self:ping() succeeded (not surprising since self is a pre-established connection). The local server primary port number is 33013 (the default) conn:delete done on space[0]. conn:insert done on space[0], index 0. primary key value = 800. conn:select done on space[0], index 0. number of fields = 2 conn:delete done on space[0]. conn:replace done on space[0]. conn:update done on space[0], timed out after waiting 0 seconds for a return conn:close done ...localhost>
select * from t0 where k0 = 800 # Prove that the update succeeded.
Select OK, 1 rows affected [800, 'Field#1', 'Field#2', 'Field#3']
This package provides read-only access to all server configuration parameters.
Package box.info
This package provides access to information about server variables: pid, uptime, version and such. Its contents are identical to the output from SHOW 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.
localhost> lua for k,v in pairs(box.info()) do print(k, ": ", v) end
---
version: 1.4.7-92-g4ba95ca
status: primary
pid: 1747
lsn: 1712
recovery_last_update: 1306964594.980
recovery_lag: 0.000
uptime: 39
build: table: 0x419cb880
logger_pid: 1748
config: /home/unera/work/tarantool/test/box/tarantool_good.cfg
...
localhost>lua box.info.pid
--- - 1747 ... localhost>lua box.info.logger_pid
--- - 1748 ... localhost>lua box.info.version
--- - 1.4.7-92-g4ba95ca ... localhost>lua box.info.config
--- - /home/unera/work/tarantool/test/box/tarantool_good.cfg ... localhost>lua box.info.uptime
--- - 3672 ... localhost>lua box.info.lsn
--- - 1712 ... localhost>lua box.info.status
--- - primary ... localhost>lua box.info.recovery_lag
--- - 0.000 ... localhost>lua box.info.recovery_last_update
--- - 1306964594.980 ... localhost>lua box.info.snapshot_pid
--- - 0 ... localhost>lua for k, v in pairs(box.info.build) do print(k .. ': ', v) end
--- flags: -D_GNU_SOURCE -D_FILE_OFFSET_BITS=64 -DCORO_ASM -fno-omit-frame-pointer -fno-stack-protector -fexceptions -funwind-tables -fgnu89-inline -pthread -Wno-sign-compare -Wno-strict-aliasing -std=gnu99 -Wall -Wextra -Werror target: Linux-x86_64-Debug compiler: /usr/bin/gcc options: cmake . -DCMAKE_INSTALL_PREFIX=/usr/local -DENABLE_STATIC=OFF -DENABLE_GCOV=OFF -DENABLE_TRACE=ON -DENABLE_BACKTRACE=ON -DENABLE_CLIENT=OFF ...
Package box.slab
This package provides access to slab allocator statistics.
localhost>lua box.slab.arena_used
--- - 4194304 ... localhost>lua box.slab.arena_size
--- - 104857600 ... localhost>lua for k, v in pairs(box.slab.slabs) do print(k) end
--- 64 128 ... localhost>lua for k, v in pairs(box.slab.slabs[64]) do print(k, ':', v) end
--- items:1 bytes_used:160 item_size:64 slabs:1 bytes_free:4194144 ...
This package provides access to request statistics.
localhost>lua box.stat -- a virtual table
--- - table: 0x41a07a08 ... localhost>lua box.stat() -- a full table (the same)
--- - table: 0x41a0ebb0 ... localhost>lua for k, v in pairs(box.stat()) do print(k) end
--- DELETE SELECT REPLACE CALL UPDATE DELETE_1_3 ... localhost>lua for k, v in pairs(box.stat().DELETE) do print(k, ': ', v) end
--- total: 23210 rps: 22 ... localhost>lua for k, v in pairs(box.stat.DELETE) do print(k, ': ', v) end -- the same
--- total: 23210 rps: 22 ... localhost>lua for k, v in pairs(box.stat.SELECT) do print(k, ': ', v) end
--- total: 34553330 rps: 23 ... localhost>
Additional examples can be found in the open source Lua stored procedures repository and in the server test suite.
There are two limitations in stored procedures support one should be aware of: execution atomicity and lack of typing.
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 a 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 stored procedure, 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 stored procedure
author. Most of the box
calls, such as
box.insert()
, box.update()
,
box.delete()
are yield points; box.select()
and box.select_range()
, however, are not.
It should also be noted that, in absence of transactions, any yield in a stored procedure 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. procedures which select and then modify a record. Multiple data change requests always run through a built-in yield point.
When invoking a stored procedure from the binary protocol, it's not possible to convey types of arguments. Tuples are type-agnostic. The conventional workaround is to use strings to pass all (textual and numeric) data.
Triggers are Lua scripts invoked by the system upon a certain event. Tarantool currently only supports system-wide triggers, run when a new connection is established or dropped. Since trigger body is a Lua script, it is external to the server, and a trigger must be set up on each server start. This is most commonly done in the initialization script. Once a trigger for an event exists, it is automatically invoked whenever an event occurs. The performance overhead of triggers, as long as they are not defined, is minimal: merely a pointer dereference and check. If a trigger is defined, its overhead is equivalent to the overhead of calling a stored procedure.
Set a callback (trigger) invoked on each connected session. The callback doesn't get any arguments, but is the first thing invoked in the scope of the newly created session. If the trigger fails by raising an error, the error is sent to the client and the connection is shut down. Returns the old value of the trigger.
If a trigger always results in an error, it may become impossible to connect to the server to reset it.
To set up replication, it's necessary to prepare the master, configure a replica, and establish procedures for recovery from a degraded state.
A replica gets all updates from the master by continuously fetching and applying its write ahead log (WAL). Each record in the WAL represents a single Tarantool command such as INSERT or UPDATE or DELETE, and is assigned a monotonically growing log sequence number (LSN). In essence, Tarantool replication is row-based: all data change commands are fully deterministic and operate on a single record.
A stored program invocation does not enter the Write Ahead Log. Instead, log events for actual UPDATEs and DELETEs, performed by the Lua code, are written to the log. This ensures that possible non-determinism of Lua does not cause replication to go out of sync.
For replication to work correctly, the latest LSN on the replica must match or fall behind the latest LSN on the master. If the replica had its own updates, this would lead to it getting out of sync, since updates from the master having identical LSNs would not be applied. In fact, if replication is ON, Tarantool does not accept updates, even on its primary_port.
To prepare the master for connections from the replica, it's only
necessary to enable replication_port in
the configuration file. An example configuration file can be
found in test/replication/cfg/master.cfg
. A master with enabled replication_port can accept connections
from as many replicas as necessary on that port. Each replica
has its own replication state.
A server, whether master or replica, always requires a valid snapshot file to boot from. For a master, a snapshot file is usually prepared with with the --init-storage option. For a replica, it's usually copied from the master.
To start replication, configure replication_source. Other parameters can also be changed, but existing spaces and their primary keys on the replica must be identical to the ones on the master.
Once connected to the master, the replica requests all changes
that happened after the latest local LSN. It is therefore
necessary to keep WAL files on the master host as long as
there are replicas that haven't applied them yet. An example
configuration can be found in test/replication/cfg/replica.cfg
.
If required WAL files are absent, a replica can be "re-seeded" at any time with a newer snapshot file, manually copied from the master.
Replication parameters are "dynamic", which allows the replica to become a master and vice versa with the help of the RELOAD CONFIGURATION statement.
"Degraded state" is a situation when the master becomes unavailable -- due to hardware or network failure, or due to a programming bug. There is no reliable way for a replica to detect that the master is gone for good, since sources of failure and replication environments vary significantly.
A separate monitoring script (or scripts, if a decision-making quorum is desirable) is necessary to detect a master failure. Such a script would typically try to update a tuple in an auxiliary space on the master, and raise an alarm if a network or disk error persists for longer than is acceptable.
When a master failure is detected, the following needs to be done:
First and foremost, make sure that the master does not accept updates. This is necessary to prevent the situation when, should the master failure end up being transient, some updates still go to the master, while others already end up on the replica.
If the master is available, the easiest way to turn on read-only mode is to turn Tarantool into a replica of itself. This can be done by setting the master's replication_source to point to self.
If the master is not available, best bet is to log into the machine and kill the server, or change the machine's network configuration (DNS, IP address).
If the machine is not available, it's perhaps prudent to power it off.
Record the replica's LSN, by issuing SHOW INFO. This LSN may prove useful if there are updates on the master that never reached the replica.
Propagate the replica to become a master. This is done by setting replication_source on replica to an empty string.
Change the application configuration to point to the new master. This can be done either by changing the application's internal routing table, or by setting up the old master's IP address on the new master's machine, or using some other approach.
Recover the old master. If there are updates that didn't make it to the new master, they have to be applied manually. You can use the Tarantool command line client to read the server log files.
Typical server administration tasks include starting and stopping the server, reloading configuration, taking snapshots, log rotation.
The server is configured to gracefully shutdown on SIGTERM and SIGINT (keyboard interrupt) or SIGHUP. SIGUSR1 can be used to save a snapshot. All other signals are blocked or ignored. The signals are processed in the main event loop. Thus, if the control flow never reaches the event loop (thanks to a runaway stored procedure), the server stops responding to any signal, and can be only killed with SIGKILL (this signal can not be ignored).
This section shows all legal syntax for the tarantool command-line client, with short notes and examples. Other client programs may have similar options and statement syntaxes.
Tokens are character sequences which are treated as syntactic units within statements.
Square brackets [
and ]
enclose optional syntax.
Three dots in a row ...
mean the preceding tokens may be repeated.
A vertical bar |
means the preceding and following tokens are mutually exclusive alternatives.
General form: tarantool [
.
Statement will be described in a later part of this section.
Option is one of the following (in alphabetical order by the long form of the option):
option
...] [statement
]
Syntax: short form: -a
long form: port-number
--a[dmin-port] [=]
.
Effect: Client will look for the server on the port designated by port-number.
Notes: This is the “administrative” port. The default value is 33015.
If --port is specified then there is no need to specify --admin-port, the client will discover it.
port-number
Syntax: short form: -B
long form: --b[in]
.
Effect: When displaying with the Lua printer, treat values with
type NUM as if they are type STR, unless they are arguments
in updates used for arithmetic.
Example: --bin
Syntax: short form: -C
long form: file-name
--c[at]
.
Effect: Client will print the contents of the write-ahead log or snapshot designated by file-name.
Example: file-name
--cat /tarantool_user/work_dir/00000000000000000018.xlog
Notes: The client stops after displaying the contents. There is also a way to use --cat for Lua statements.
Syntax: short form: -D
long form: delimiter
--d[elim]
.
Effect: If --cat is used, then put delimiter at end of each line
of a Lua file. If --cat is not used, then require that
all statements end with delimiter.
Example: delimiter
--delim = '!'
Notes: See also the SETOPT DELIMITER statement.
Syntax: short form: -M tarantool|raw
long form: --fo[rmat] tarantool|raw
.
Effect: set format for output from --cat
Example: --format tarantool
Notes: The default format is tarantool.
Syntax: short form: -F
long form: log-sequence-number
--fr[om]
.
Effect: Play only what has a a log sequence number greater than or equal to log-sequence-number.
Example: log-sequence-number
--from 55
Notes: See also --play and --to.
Syntax: short form: -H
long form: --hea[der]
.
Effect: Add a header if --format=raw.
Example: --header
Notes: The default is 'no header'.
Syntax: short form: -?
long form: --hel[p]
.
Effect: Client displays a help message including a list of options.
Example: --help
Notes: The client stops after displaying the help.
Syntax: short form: -h
long form: host-name
--ho[st] [=]
.
Effect: Client will look for the server on the computer designated by host-name.
Example: host-name
--host = 127.0.0.1
Notes: The default value is localhost.
Syntax: short form: -P
long form: file-name
--pl[ay] f
.
Effect: Client will tell server to replay the write-ahead log designated by file-name.
Example: file-name
--play /tarantool_user/work_dir/00000000000000000018.xlog
Syntax: short form: -p
long form: port-number
--po[rt] [=]
.
Effect: Client will look for the server on the port designated by port-number.
Example: port-number
--port = 33013
Notes: This is the “primary port” also known as the “read/write data” port. The default value is 33013.
If --admin-port is specified then there is no need to specify --port, the client will discover it.
Syntax: short form: -R
long form: server-name
--rpl
.
Effect: Act as a replica for the server specified by server-name.
Example: server-name
--rpl = wombat
Syntax: short form: -S
Long form: space-number
--s[pace]
.
Effect: Play only what is applicable to the space designated by space-number.
Example: space-number
--space 0
Syntax: short form: -T
long form: log-sequence-number
--t[o]
.
Effect: Play only what has a log sequence number less than or equal to log-sequence-number.
Example: log-sequence-number
--to 66
Notes: See also --play and --from.
Syntax: short form: -V
long form: --v[ersion]
.
Effect: Client displays version information.
Example: --version
Notes: The client stops after displaying the version.
Keywords are: Character sequences containing only letters of the English alphabet. Examples: SELECT, INTO, FIBER. Notes: Keywords are case insensitive so SELECT and Select are the same thing.
Tuple set identifiers are: Lower case letter 't' followed by one or more digits. Examples: t0, t55.
Field identifiers are: Lower case letter 'k' followed by one or more digits. Examples: k0, k55.
Procedure identifiers are: Any sequence of letters, digits, or underscores which is legal according to the rules for Lua identifiers.
String literals are: Any sequence of zero or more characters enclosed in single quotes. Examples: 'Hello, world', 'A'.
Numeric literals are: Character sequences containing only digits, optionally preceded by + or -. Examples: 55, -. Notes: Tarantool NUM data type is unsigned, so -1 is understood as a large unsigned number.
Single-byte tokens are: * or , or ( or ). Examples: * , ( ).
Tokens must be separated from each other by one or more spaces, except that spaces are not necessary around single-byte tokens or string literals.
Although an initial statement may be entered on the tarantool command line,
generally they are entered following the prompt in interactive mode while
tarantool is running. (A prompt will be the name of the host and a greater-than
sign, for example localhost>
). The end-of-statement marker is
a newline (line feed).
Syntax: CALL
.
Effect: The client tells the server to execute the procedure identified by procedure-identifier.
Example: procedure-identifier
()CALL proc50()
.
Notes: The client sends to the server's read/write data port.
Syntax: DELETE FROM
.
Effect: Client tells server to delete the tuple identified by the WHERE clause.
Example: tuple-set-name
WHERE field-name
= literal
DELETE FROM t0 WHERE k0='a'
.
Notes: field-name must identify the primary key. The client sends to the server's read/write data port after converting from SQL to binary protocol.
Syntax: E[XIT]
.
Effect: The tarantool program stops.
Example: EXIT
.
Notes: The QUIT statement does the same thing. The client sends nothing to the server.
Syntax: H[ELP]
.
Effect: Client displays a message including a list of possible statements.
Example: HELP
.
Notes: The client sends nothing to the server.
Syntax: INSERT [INTO]
.
Effect: The client tells the server to add the tuple consisting of the literal values.
Example: tuple-set-identifier
VALUES (literal
[,literal
...])INSERT INTO t0 VALUES ('a',0)
.
Notes: The client sends to the server's read/write data port after converting from SQL to binary protocol.
Syntax: LOADFILE
.
Effect: The client loads instructions from the file identified by string-literal.
Example: string-literal
LOADFILE '/home/tarantool_user/file5.txt'
.
Syntax: LUA
.
Effect: The client tells the server to execute the tokens as Lua statements.
Example: token
[token
...]LUA "hello".." world"
.
Notes: The client sends to the server's administrative port.
Syntax: PING
.
Effect: The client sends a ping to the server.
Example: PING
.
Notes: The client sends to the server's read/write data port.
Syntax: Q[UIT]
.
Effect: The client stops. This statement is handled entirely by the client.
Example: QUIT
.
Notes: The EXIT statement does the same thing. The client sends nothing to the server.
Syntax: RELOAD CONFIGURATION
.
Effect: The client tells the server to re-read the configuration file.
Example: RELOAD CONFIGURATION
.
Notes: The client sends to the server's administrative port.
Syntax; REPLACE [INTO]
.
Effect: The client tells the server to add the tuple consisting of the literal values.
Example: tuple-set-identifier
VALUES (literal
[,literal
...])REPLACE INTO t0 VALUES ('a',0)
.
Notes: REPLACE and INSERT are the same, except that INSERT will return an error if a tuple already exists with the same primary key.
The client sends to the server's read/write data port after converting from SQL to binary protocol.
Syntax: SAVE COREDUMP | SNAPSHOT
.
Effect: The client tells the server to save the designated object.
Example: SAVE SNAPSHOT
.
Notes: The client sends to the server's administrative port.
Syntax: SELECT * FROM
].
Effect: Client tells server to find the tuple or tuples identified in the WHERE clause.
Example: tuple-set-identifier
WHERE field-identifier
= literal
[AND|OR field-identifier
= literal
...] [LIMIT numeric-literal
[,numeric-literal
]SELECT * FROM t0 WHERE k0 = 5 AND k1 = 7 LIMIT 1
.
Notes: The client sends to the server's read/write data port.
Syntax: SET INJECTION
.
Effect: In normal mode: error.
Notes: This statement is only available in debug mode.
name-token
state-token
Syntax: SETOPT DELIMITER =
.
The string must be a value in single quotes.
Effect: string becomes end-of-statement delimiter, so newline alone is not treated as end of statement.
Example: string-literal
SETOPT DELIMITER = '!'
.
Notes: The client sends nothing to the server.
Syntax: SETOPT PAGER =
.
The string must be a value in single quotes.
Effect: string becomes the pager that will be invoked for subsequent commands;
usually the values are '/usr/bin/less' or '/bin/more' for the common
Linux pagers.
Example: string-literal
SETOPT PAGER = '/usr/bin/less'
.
Notes: The client sends nothing to the server.
Syntax: SHOW CONFIGURATION | FIBER | INFO | INJECTIONS | PALLOC | PLUGINS | SLAB | STAT
.
Effect: The client asks the server for information about environment or statistics.
Example: SHOW INFO
.
Notes: The client sends to the server's administrative port.
SHOW INJECTIONS is only available in debug mode.
Syntax: UPDATE
.
Effect: Client tells server to change the tuple identified in the WHERE clause.
Example: tuple-set-identifier
SET field-identifier
= literal
[,field-identifier
= literal
...] WHERE field-identifier
= literal
UPDATE t1 SET k1= 'K', k2 = 7 WHERE k0 = 0
.
Notes: The client sends to the server's read/write data port after converting from SQL to binary protocol.
For a condensed Backus-Naur Form [BNF] description of some of the statements, see
doc/box-protocol.txt
and
doc/sql.txt
.
Depending how one combines the tarantool client's options, there are in effect three modes of operation: “interactive”, “print and play”, or “replication” mode.
In interactive mode, one types statements and gets results.
One can specify a statement file when starting
(tarantool < file_name
)
or one can specify a statement file with the LOADFILE statement:
(LOADFILE file_name
), but typically the statements
are typed in by the user following prompts.
Here is an example of an interactive-mode tarantool client session:
$
tarantool localhost>INSERT INTO t0 VALUES ('X-1',100)
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES ('X-2',200,'On Order')
Insert OK, 1 rows affected localhost>INSERT INTO t0 VALUES ('X-3',300,'')
Insert OK, 1 rows affected localhost>UPDATE t0 SET k1 = 300 WHERE k0 = 'X-1'
Update OK, 1 rows affected localhost>DELETE FROM t0 WHERE k0 = 'X-2'
Delete OK, 1 rows affected localhost>SELECT * FROM t0 WHERE k0 = 'X-1'
Select OK, 1 rows affected ['X-1', 300] localhost>EXIT
$
In print and play mode, one uses --cat and --play and --from and --to and --space options to print write-ahead-log contents, or to send write-ahead-log contents to the server. Here is an example of a print-and-play-mode tarantool client session:
$
tarantool --cat /home/user1/tarantool_test/work_dir/00000000000000000005.xlog --from 22 --to 26 Insert, lsn: 22, time: 1385327353.345869, len: 33, space: 0, cookie: 127.0.0.1:44787 ['X-1', 100] Insert, lsn: 23, time: 1385327353.346745, len: 42, space: 0, cookie: 127.0.0.1:44787 ['X-2', 200, 8243105135088135759] Insert, lsn: 24, time: 1385327353.347352, len: 34, space: 0, cookie: 127.0.0.1:44787 ['X-3', 300, ''] Update, lsn: 25, time: 1385327353.348209, len: 42, space: 0, cookie: 127.0.0.1:44787 ['X-1'] Delete, lsn: 26, time: 1385327353.348879, len: 28, space: 0, cookie: 127.0.0.1:44787 ['X-2']$
In replication mode, one connects as a replica, and then writes a binary log to a file.
The tarantar utility program will create new snapshots by reading existing snapshots and write-ahead-log (xlog) files. Thus it differs from SAVE SNAPSHOT, which creates new snapshots from the database. Since tarantar uses less memory than SAVE SNAPSHOT, it is especially appropriate for taking periodic snapshots as a background task.
To prepare: ensure that the configuration file is available.
To run:
tarantar [options] configuration-file
where possible options are:
-c or --create — create snapshot file. example: --create |
-i or --interval — repeat every seconds-count seconds. example: -i 3600 |
-n or --lsn — start from lsn = lsn-number. if not specified, lsn = latest. example: -n 5 |
-l or --limit — do not use more than bytes-count bytes of memory. example: -l 5000000 |
-? or --help — display a help message and exit. example: --help |
-V or --version — display version and exit. example: -V |
Example:
$
~/tarantool/client/tarantar/tarantar -c -i 30 ./tarantool.cfg
snap_dir: /home/user/tarantool_test/work_dir
wal_dir: /home/user/tarantool_test/work_dir
spaces: 1
interval: 30
memory_limit: 0M
START SNAPSHOTTING Fri Oct 25 09:35:25 2013
last snapshot lsn: 7
(snapshot) 00000000000000000007.snap 0.000M processed
( >> ) 00000000000000000006.snap 0.000M processed
START SNAPSHOTTING Fri Oct 25 09:35:55 2013
last snapshot lsn: 7
(snapshot) 00000000000000000007.snap 0.000M processed
( >> ) 00000000000000000006.snap 0.000M processed
snapshot exists, skip.
...
To recapitulate the idea of a snapshot and a write-ahead log: Tarantool's database is entirely in memory; however, it has a complete and up-to-date on-disk backup. This consists of snapshot files (extension .snap) which are copies of the database as of the time the snapshot was taken, and write-ahead-log files (extension .xlog) which contain records of insert/update/delete operations that are written when the operations occur. If the tarantool_box server goes down and later restarts, it will recover the database by reading the snapshot and then re-applying the write-ahead-log records.
The approach is reliable. But if the snapshot gets old and the number of write-ahead-log records get huge, then recovery would take too long and .xlog files would take too much space. So periodically one should make new snapshots -- if a .snap file is up to date, then the .xlog files are unnecessary and can be archived.
To take a snapshot with the tarantool client, one can say SAVE SNAPSHOT. SAVE SNAPSHOT will copy every tuple from the in-memory database to the .snap file. However, this is not always the ideal method.
Taking snapshots with tarantar, instead of with SAVE SNAPSHOT, can be better because:
tarantar can work even if the tarantool_box server is down because it works from the existing .xnap and .xlog files, rather than from an in-memory database.
tarantar saves memory when constructing its own in-memory index to the rows by making SHA-1 hashes for primary keys that contain strings or that are multi-column and longer than 20 bytes
tarantar can be made to limit its memory usage so that it does not interfere with resource use by other processes including other Tarantool-related processes.
tarantar can be made to run periodically as a daemon
For more explanation of tarantar's design see the Tarantool wiki.
The tarancheck utility program will generate and verify “signature files”. A signature file contains, along with basic information that identifies the database, checksums calculated for each index in each space of the database, based on the latest snapshot and all subsequent entries in the write-ahead log. Signature files are useful for ensuring that databases have been saved without error, and for quick comparisons to see whether a database's components have been modified.
The main reason that tarancheck was created was so that users would be able to compare the consistency of two running servers, the master and the replica. By creating a signature file on the master using the master directory, and then copying the signature file to the replica, one will be able to confirm that the replica is not corrupt.
There is one necessary warning. Since either the master or the replica is likely to be active when tarancheck runs, the check can only be applicable for the database as of the last transaction that was run on both the master and the replica. That is why tarancheck displays last_xlog_lsn, which is the log sequence number of the write-ahead log, when it finishes.
To prepare: ensure that the configuration file contains wal_dir and snap_dir clauses. Tarancheck does not assume that wal_dir and snap_dir have default values.
To run:
tarancheck [options] configuration-file
where possible options are:
-G or --generate — generate signature file. example: -G x.crc |
-W or --verify — verify signature file. example: --verify x.crc |
-? or --help — display a help message and exit. example: --help |
-V or --version — display version and exit. example: -V |
Example:
$
~/tarantool/client/tarantar/tarancheck --generate=x.crc tarantool.cfg
>>> Signature file generation
configured spaces: 1
snap_dir: ./work_dir
wal_dir: ./work_dir
last snapshot lsn: 1
last xlog lsn: 0
(snapshot) 00000000000000000001.snap
(signature) saving x.crc
With tarantool_deploy one can set up so that, during system boot,
one or more instances of the tarantool_box server will start.
This utility is for use on Red Hat or CentOS where Tarantool
was installed using rpm --install
.
Technically, tarantool_deploy will place instructions in /etc/init.d
which will initiate tarantool_box with appropriate options and
with settings that maximize resource usage.
The root password is necessary. These options are available,
as shown by tarantool_deploy --help
:
Tarantool deployment script: add more Tarantool instances. usage: tarantool_deploy.sh [options] <instance> --prefix <path> installation path (/usr) --prefix_etc <path> installation etc path (/etc) --prefix_var <path> installation var path (/var) --status display deployment status --dry don't create anything, show commands --debug show commands --yes don't prompt --help display this usage
The default prefixes (/usr
and /etc
and /var
) are appropriate
if a Tarantool installation was done with default settings,
for example tarantool_box should be in /usr/bin
.
The only necessary argument is the "instance", which is an
arbitrary numeric identification formatted as digit.digit.
The following is a sample run:
$
tarantool_deploy.sh 0.1
tarantool_deploy.sh: About to deploy Tarantool instance 0.1.
tarantool_deploy.sh: Continue? [n/y]
y
tarantool_deploy.sh: >>> deploy instance 0.1
tarantool_deploy.sh: >>> updating deployment config
tarantool_deploy.sh: done
This chapter provides a cheatsheet for most common server management routines on every supported operating system.
Setting up an instance: ln -s /etc/tarantool/instances.available/instance-name.cfg /etc/tarantool/instances.enabled/
Starting all instances: service tarantool start
Stopping all instances: service tarantool stop
Starting/stopping one instance: service tarantool-instance-name start/stop
This chapter provides a reference of options which can be set in the command line or
tarantool.cfg
configuration file.
Tarantool splits its configuration parameters between command line options and a configuration file. Command line options are provided only for the most basic properties; the rest must be set in the configuration file. At runtime, this allows to disambiguate the source of a configuration setting: it unequivocally comes either from the command line, or from the configuration file, but never from both.
Tarantool follows the GNU
standard for its command line interface: long
options start with a double dash (--option
),
their short counterparts use a single one (-o
).
For phrases, both dashes and
underscores can be used as word separators
(--cfg-get
and --cfg_get
both work).
If an option requires an argument, you can either separate it
with a space or equals sign (--cfg-get=pid_file
and
--cfg-get pid_file
both work).
Print an annotated list of all available options and exit.
Print product name and version, for example:
$
./tarantool_box --version
Tarantool 1.4.0-69-g45551dd
In this example:
“Tarantool” is the name of the reusable asynchronous networking programming framework. |
“Box” is the name of the storage back-end. |
The 3-number version follows the standard
<major>-<minor>-<patch>
scheme, in which <major> number
is changed only rarely, <minor>
is incremented for each new milestone and
indicates possible incompatible changes,
and <patch> stands for the number of
bug fix releases made after the start of the
milestone. The optional commit number and
commit SHA1 are output for non-released versions
only, and indicate how much this particular build has diverged
from the last release.
|
Tarantool uses git describe to produce its version id, and this id can be used at any time to check out the corresponding source from our git repository.
--config=
, /path/to/config.file
-c
Tarantool does not start without a configuration file. By
default, the server looks for file named
tarantool.cfg
in the current working
directory. An alternative location can be provided using
this option.
--check-config
Check the configuration file for errors. This option is
normally used on the command line
before “reload configuration”
is issued on the administrative port, to ensure that the new
configuration is valid. When configuration is
indeed correct, the program produces no output and returns 0
.
Otherwise, information about discovered error is printed out
and the program terminates with a non-zero value.
--cfg-get=
option_name
Given option name, print option value. If the option does not exist, or the configuration file is incorrect, an error is returned. If the option is not explicitly specified, its default value is used instead. Example:
$
./tarantool_box --cfg-get=admin_port
33015
Initialize the directory, specified in vardir
configuration option by creating an empty snapshot file in
it. If vardir
doesn't contain at
least one snapshot, the server does not start. There is no
“magic” with automatic initialization of
vardir
on boot to make
potential system errors more noticeable. For example, if the
operating system reboots and fails to mount the partition on
which vardir
is expected to reside, the
rc.d
or service script
responsible for server restart will also fail, thanks to this
option.
The only two options which could affect a running server are:
--verbose
, -v
Increase verbosity level in log messages. This option currently has no effect.
--background
, -b
Detach from the controlling terminal and run in the background.
Tarantool uses
stdout
and
stderr
for
debug and error log output. When starting the server with
option --background
, make sure to
either redirect its standard out and standard error
streams, or provide logger option
in the configuration file, since otherwise all logging
information will be lost.
All advanced configuration parameters must be specified in a
configuration file, which is required for server start. If no path to
the configuration file is specified on the command line (see
--config
),
the server looks for a file named tarantool.cfg
in the current working directory.
To facilitate centralized and automated configuration management, runtime configuration modifications are supported solely through the RELOAD CONFIGURATION administrative statement. Thus, the procedure to change Tarantool configuration at runtime is to edit the configuration file. This ensures that, should the server get killed or restart, no unexpected changes to configuration can occur.
Not all configuration file settings are changeable at runtime: such settings will be highlighted in this reference. If the same setting is given more than once, the latest occurrence takes effect. You can always invoke SHOW CONFIGURATION from the administrative console to show the current configuration.
Tarantool maintains a set of all allowed configuration
parameters in two template files, which are easy to maintain
and extend:
cfg/core_cfg.cfg_tmpl
,
src/box/box_cfg.cfg_tmpl
.
These files can always be used as a reference for any
parameter in this manual.
In addition, two working examples can be found in the source tree:
test/box/tarantool.cfg
,
test/big/tarantool.cfg
.
Table 7.1. Basic parameters
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
username | string | "" | no | no | UNIX user name to switch to after start. |
work_dir | string | "" | no | no | A directory where database working files will be stored. The server switches to work_dir with chdir(2) after start. Can be relative to the current directory. If not specified, defaults to the current directory. |
script_dir | string | "" | no | no | If this path is set, it is added to the Lua package
search path, so that instance-specific Lua scripts can
be loaded and executed. If the directory specified in
the path contains init.lua file, it
is loaded and executed at server start.
|
wal_dir | string | "" | no | no | A directory where write ahead log (.xlog) files are stored. Can be relative to work_dir. Most commonly used so that snapshot files and write ahead log files can be stored on separate disks. If not specified, defaults to work_dir. |
snap_dir | string | "" | no | no | A directory where snapshot (.snap) files will be stored. Can be relative to work_dir. If not specified, defaults to work_dir. See also wal_dir. |
bind_ipaddr | string | "INADDR_ANY" | no | no | The network interface to bind to. By default, the server binds to all available addresses. Applies to all ports opened by the server. |
primary_port | integer | none | yes | no | The read/write data port. Has no default value, so must be specified in the configuration file. Normally set to 33013. Note: a replica also binds to this port, and accepts connections, but these connections can only serve reads until the replica becomes a master. |
secondary_port | integer | none | no | no | Additional, read-only port. Normally set to 33014. Not used unless is set. |
admin_port | integer | none | no | no | The TCP port to listen on for administrative connections. Has no default value. Not used unless assigned a value. Normally set to 33015. |
pid_file | string | tarantool.pid | no | no | Store the process id in this file. Can be relative to work_dir. |
custom_proc_title | string | "" | no | no |
Inject the given string into server process title (what's shown in the COMMAND column for ps and top commands). For example, ordinarily ps shows the Tarantool server process thus: kostja@shmita:~$ ps -a -o command | grep box tarantool_box: primary pri: 33013 sec: 33014 adm: 33015 But if the configuration file contains custom_proc_title=sessions then the output looks like: kostja@shmita:~$ ps -a -o command | grep box tarantool_box: primary@sessions pri: 33013 sec: 33014 adm: 33015 |
Table 7.2. Configuring the storage
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
slab_alloc_arena | float | 1.0 | no | no | How much memory Tarantool allocates to actually store tuples, in gigabytes. When the limit is reached, INSERT or UPDATE requests begin failing with error ER_MEMORY_ISSUE. While the server does not go beyond the defined limit to allocate tuples, there is additional memory used to store indexes and connection information. Depending on actual configuration and workload, Tarantool can consume up to 20% more than the limit set here. |
slab_alloc_minimal | integer | 64 | no | no | Size of the smallest allocation unit. It can be tuned down if most of the tuples are very small. |
slab_alloc_factor | float | 2.0 | no | no | Use slab_alloc_factor as the multiplier for computing the sizes of memory chunks that tuples are stored in. A lower value may result in less wasted memory depending on the total amount of memory available and the distribution of item sizes. |
space | array of objects | none | yes | no | This is the main Tarantool parameter, describing the data structure that users get access to via the client/server protocol. It holds an array of entries, and each entry describes a tuple set and its indexes. Every entry is a composite object, best seen as a C programming language "struct" [a]. |
[a] Space settings explainedSpace is a composite parameter, that is, it has properties. /* * Each tuple consists of fields. Three field types are * supported. */ enum { STR, NUM, NUM64 } field_type; /* * Tarantool is interested in field types only inasmuch as * it needs to build indexes on fields. An index * can cover one or more fields. */ struct index_field_t { unsigned int fieldno; enum field_type type; }; /* * HASH and TREE and BITSET index types are supported. */ enum { HASH, TREE, BITSET } index_type; struct index_t { index_field_t key_field[]; enum index_type type; /* Secondary index may be non-unique */ bool unique; }; struct space_t { /* A space can be quickly disabled and re-enabled at run time. */ bool enabled; /* * If cardinality is given, each tuple in the space must have exactly * this many fields. */ unsigned int cardinality; /* estimated_rows is only used for HASH indexes, to preallocate memory. */ unsigned int estimated_rows; struct index_t index[]; }; The way a space is defined in a configuration file is similar to how one would initialize a C structure in a program. For example, a minimal storage configuration looks like the following: space[0].enabled = 1 space[0].index[0].type = HASH space[0].index[0].unique = 1 space[0].index[0].key_field[0].fieldno = 0 space[0].index[0].key_field[0].type = NUM64 The parameters listed above are mandatory. Other space properties are set in the same way. An alternative syntax, mainly useful when defining large spaces, exists: space[0] = { enabled = 1, index = [ { type = HASH, key_field = [ { fieldno = 0, type = NUM64 } ] } ] } When defining a space, please be aware of these restrictions:
|
Table 7.3. Binary logging and snapshots
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
panic_on_snap_error | boolean | true | no | no | If there is an error reading the snapshot file (at server start), abort. |
panic_on_wal_error | boolean | false | no | no | If there is an error reading a write ahead log file (at server start), abort. |
rows_per_wal | integer | 500000 | no | no | How many log records to store in a single write
ahead log file. When this limit is reached, Tarantool
creates another WAL file named
<first-lsn-in-wal>.xlog
This can be useful for simple rsync-based backups.
|
snap_io_rate_limit | float | 0.0 | no | yes | Reduce the throttling effect of SAVE SNAPSHOT on INSERT/UPDATE/DELETE performance by setting a limit on how many megabytes per second it can write to disk. The same can be achieved by splitting wal_dir and snap_dir locations and moving snapshots to a separate disk. |
wal_fsync_delay | float | 0 | no | yes | Do not flush the write ahead log to disk more often than once in wal_fsync_delay seconds. By default the delay is zero, which means there is no flushing after writes (the meaning of wait_fsync_delay=0 meay change in later versions). Setting the delay may be necessary to increase write throughput, but may lead to several last updates being lost in case of a power failure. Such failure, however, does not lead to data corruption: all WAL records have a checksum, and only complete records are processed during recovery. |
wal_mode | string | "fsync_delay" | no | yes | Specify fiber-WAL-disk synchronization mode as: none: write ahead log is not maintained; write: fibers wait for their data to be written to the write ahead log (no fsync(2)); fsync: fibers wait for their data, fsync(2) follows each write(2); fsync_delay: fibers wait for their data, fsync(2) is called every N=wal_fsync_delay seconds (N=0.0 means no fsync(2) - equivalent to wal_mode = "write"); |
Table 7.4. Replication
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
replication_port | integer | 0 | no | no | If replication_port is greater than zero, the server is considered to be a Tarantool master. The master server listens on the specified port for incoming connections from replicas. See also replication_source, which complements this setting on the replica side. |
replication_source | string | NULL | no | yes | If replication_source is not an empty string, the server is considered to be a Tarantool replica. The replica server will try to connect to the master which replication_source specifies with format ip:port. For example, if replication_source = "1.2.3.4:55555" then the replica server tries to connect to 1.2.3.4 port 55555. A replica server does not accept updates on primary_port. This parameter is dynamic, that is, to enter master mode, simply set replication_source to an empty string and issue RELOAD CONFIGURATION. |
Table 7.5. Networking
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
io_collect_interval | float | 0.0 | no | yes | The server will sleep for io_collect_interval seconds between iterations of the event loop. Can be used to reduce CPU load in deployments in which the number of client connections is large, but requests are not so frequent (for example, each connection issues just a handful of requests per second). |
readahead | integer | 16384 | no | no | The size of the read-ahead buffer associated with a client connection. The larger the buffer, the more memory an active connection consumes and the more requests can be read from the operating system buffer in a single system call. The rule of thumb is to make sure the buffer can contain at least a few dozen requests. Therefore, if a typical tuple in a request is large, e.g. a few kilobytes or even megabytes, the read-ahead buffer size should be increased. If batched request processing is not used, it's prudent to leave this setting at its default. |
backlog | integer | 1024 | no | no | The size of the listen backlog. |
Table 7.6. Logging
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
log_level | integer | 4 | no | yes | How verbose the logging is. There are 5 log verbosity classes: 1 -- ERROR, 2 -- CRITICAL, 3 -- WARNING, 4 -- INFO, 5 -- DEBUG. By setting log_level, one can enable logging of all classes below or equal to the given level. Tarantool prints its logs to the standard error stream by default, but this can be changed with the "logger" configuration parameter. |
logger | string | "" | no | no | By default, the log is sent to the standard
error stream (stderr ). If logger
is given a value, Tarantool creates a child process,
executes the command indicated by the value, and pipes its standard
output to the standard input of the created process.
Example setting: tee --append
tarantool.log (this will duplicate log output
to stdout and a log file).
|
logger_nonblock | integer | 0 | no | no | If logger_nonblock equals 1, Tarantool does not block on the log file descriptor when it's not ready for write, and drops the message instead. If log_level is high, and a lot of messages go to the log file, setting logger_nonblock to 1 may improve logging performance at the cost of some log messages getting lost. |
too_long_threshold | float | 0.5 | no | yes | If processing a request takes longer than the given value (in seconds), warn about it in the log. Has effect only if log_level is less than or equal to 3 (WARNING). |
Table 7.7. Memcached protocol support
Name | Type | Default | Required? | Dynamic? | Description |
---|---|---|---|---|---|
memcached_port | integer | none | no | no | Turn on Memcached protocol support on the given port. All requests on this port are directed to a dedicated space, set in memcached_space. Memcached-style flags are supported and stored along with the value. The expiration time can also be set and is persistent, but is ignored, unless memcached_expire is turned on. Unlike Memcached, all data still goes to the binary log and to the replica, if the latter is set up, which means that power outage does not lead to loss of all data. Thanks to data persistence, cache warm up time is also very short. |
memcached_space | integer | 23 | no | no | Space id to store memcached data in. The format of tuple is [key, metadata, value], with a HASH index based on the key. Since the space format is defined by the Memcached data model, it must not be previously configured. |
memcached_expire | boolean | false | no | no | Turn on tuple time-to-live support in memcached_space. This effectively turns Tarantool into a persistent, replicated and scriptable implementation of Memcached. |
memcached_expire_per_loop | integer | 1024 | no | yes | How many records to consider per iteration of the expiration loop. Tuple expiration is performed in a separate “green” thread within our cooperative multitasking framework and this setting effectively limits how long the expiration loop stays uninterrupted on the CPU. |
memcached_expire_full_sweep | float | 3600 | no | yes | Try to make sure that every tuple is considered for expiration within this time frame (in seconds). Together with memcached_expire_per_loop this defines how often the expiration “green” thread is scheduled on the CPU. |
Table 7.8. Hot Standby
This chapter documents APIs for various programming languages.
Apart from the native Tarantool client driver, you can always use a Memcached driver of your choice, after enabling Memcached protocol in the configuration file.
The Tarantool API exists so that a client program can send a request packet to the server, and receive a response. Here is an example of a what the client would send for INSERT INTO t0 VALUES ('A','BB'). The BNF description of the components is in file doc/box-protocol.txt. A third-party contribution written in Lua for unpacking Tarantool messages is in file Tnt-dissector.
Component | Byte#0 | Byte#1 | Byte#2 | Byte#3 | |
---|---|---|---|---|---|
type | 13 | 0 | 0 | 0 | |
body_length | 17 | 0 | 0 | 0 | |
request_id | 1 | 0 | 0 | 0 | |
space_no | 0 | 0 | 0 | 0 | |
flags | 2 | 0 | 0 | 0 | |
cardinality | 2 | 0 | 0 | 0 | |
field[0] size | 1 | ||||
field[0] data | 65 | ||||
field[1] size | 2 | ||||
field[1] data | 66 | 66 |
Now, one could send that packet to the tarantool_box server,
and interpret the response (box-protocol.txt has a description
of the packet format for responses as well as requests).
But it would be easier, and less error-prone, if one could
invoke a routine that formats the packet according to typed
parameters. Something like response=tarantool_routine("insert",0,"A","B");
.
And that is why APIs exist for drivers for C, Perl, Python, PHP, Ruby, and so on.
Here is a complete C program that inserts [99999,'BB'] into space[0] via the C API for the
binary protocol. To compile, paste the code into a file named example.c and say
gcc -o example example.c -I/
where tarantool-directory = the directory that contains
the necessary file tarantool-directory
/connector/c/includetp.h
, and the default library path contains
the directory where Tarantool library files were placed at installation time.
Before trying to run, check that the server
(tarantool_box) is running on localhost (127.0.0.1) and its primary port is the default (33013) and
space[0]'s primary key type is numeric (space[0].index[0].key_field[0].type = "NUM" in configuration file).
To run, say ./example
.
The program will format a buffer for sending an INSERT request, then open a socket connection
with the tarantool_box server at localhost:33013, then send the request, then check if the
server returned an error, then — if all is well — print "Insert succeeded". If the
row already exists, the program will print “Duplicate key exists in unique index 0”.
#include <arpa/inet.h> #include <stdio.h> #include <unistd.h> #include <tp.h> /* the usual Tarantool include */ int main() { struct tp request; /* area for sending to server */ struct tp reply; /* area for getting server reply */ int fd; /* file descriptor for socket */ int pk_field_value = 99999; struct sockaddr_in tt; /* the usual socket address info */ tp_init(&request, NULL, 0, tp_realloc, NULL); /* initialize request buffer */ tp_insert(&request, 0, 2); /* append INSERT header */ tp_tuple(&request); /* begin appending body */ tp_field(&request, (char*) &pk_field_value, 4); /* append field[0] */ tp_sz(&request, "BB"); /* append field[1] */ if ((fd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) <= 0) /* open the socket, abort if failure */ exit(1); memset(&tt, 0, sizeof(tt)); /* connect to localhost:33013 */ tt.sin_family = AF_INET; tt.sin_addr.s_addr = inet_addr("127.0.0.1"); tt.sin_port = htons(33013); if (connect(fd, (struct sockaddr *) &tt, sizeof(tt)) < 0) /* connect, abort if failure */ exit(1); int rc = write(fd, tp_buf(&request), tp_used(&request)); /* send the INSERT request */ if (rc != tp_used(&request)) /* abort if send failed */ exit(1); tp_init(&reply, NULL, 0, tp_realloc, NULL); /* initialize reply buffer */ while (1) { ssize_t to_read = tp_req(&reply); if (to_read <= 0) break; ssize_t new_size = tp_ensure(&reply, to_read); if (new_size == -1) /* abort if error e.g. no memory */ exit(1); ssize_t res = read(fd, reply.p, to_read); /* get reply */ if (res <= 0) /* abort if error e.g. no reply */ exit(1); tp_use(&reply, res); } ssize_t server_code = tp_reply(&reply); /* display+abort if error e.g. duplicate key */ if (server_code != 0) { printf("error: %-.*s\n", tp_replyerrorlen(&reply), tp_replyerror(&reply)); tp_free(&reply); exit(1); } tp_free(&request); /* clean up */ tp_free(&reply); close(fd); printf("Insert succeeded\n"); /* congratulate self */ exit(0); }
The example program only shows one command and does not show all that's necessary for
good practice. For that, please see connector/c
in the source tree.
Please see https://github.com/tarantool/tarantool-erlang
.
Please see http://dgreenru.github.io/tarantool-java/
.
Please see http://github.com/devgru/node-tarantool
.
The most commonly used Perl driver is DR::Tarantool. It is not supplied as part of the Tarantool repository; it must be installed separately. The most common way to install it is with CPAN, the Comprehensive Perl Archive Network. DR::Tarantool requires other modules which should be installed first. For example, on Ubuntu, the installation could look like this:
sudo cpan install AnyEvent sudo cpan install Devel::GlobalDestruction sudo cpan install Coro sudo cpan install Test::Pod sudo cpan install Test::Spelling sudo cpan install PAR::Dist sudo cpan install DR::Tarantool
Here is a complete Perl program that inserts [99999,'BB'] into space[0] via the Perl API.
Before trying to run, check that the server
(tarantool_box) is running on localhost (127.0.0.1) and its primary port is the default (33013) and
space[0]'s primary key type is numeric (space[0].index[0].key_field[0].type = "NUM" in configuration file).
To run, paste the code into a file named example.pl and say perl example.pl
.
The program will connect using an application-specific definition of the space.
The program will open a socket connection
with the tarantool_box server at localhost:33013, then send an INSERT request,
then — if all is well — end without displaying any messages.
If tarantool_box is not running on localhost with primary port = 33013, the program will print
“Connection refused”.
#!/usr/bin/perl use DR::Tarantool ':constant', 'tarantool'; use DR::Tarantool ':all'; my $tnt = tarantool host => '127.0.0.1', # look for tarantool_box on localhost port => 33013, # assume tarantool_box primary port = default spaces => { 0 => { # definition of space[0] ... name => 't0', # space[0] name = 't0' default_type => 'STR', # space[0] field type is 'STR' if undefined fields => [ { # definition of space[0].fields ... name => 'k0', type => 'NUM' } ], # space[0].field[0] name='k0',type='NUM' indexes => { # definition of space[0] indexes ... 0 => { name => 'k0', fields => 'k0' } } } }; $tnt->insert('t0' => [ 99999, 'BB' ]); # INSERT INTO t0 VALUES (99999,'BB')
The example program only shows one command and does not show all that's necessary for good practice. For that, please see DR::Tarantool CPAN repository.
The PHP driver is tarantool-php. It is not supplied as part of the Tarantool repository; it must be installed separately. It can be installed with git. It requires other modules which should be installed first. For example, on Ubuntu, the installation could look like this:
sudo apt-get install php5-cli sudo apt-get install php5-dev sudo apt-get install php-pear cd ~ git clone https://github.com/tarantool/tarantool-php.git cd tarantool-php phpize ./configure make make install
At this point there is a file named ~/tarantool-php/modules/tarantool.so
.
PHP will only find it if the PHP initialization file php.ini
contains a line like
extension=./tarantool.so
.
So copy tarantool.so
to the working directory and tell PHP where
to find the php.ini
file that contains that line ...
cd ~ cp ./tarantool-php/modules/tarantool.so . export PHP_INI_SCAN_DIR=~/tarantool-php/test/share
Here is a complete PHP program that inserts [99999,'BB'] into space[0] via the PHP API.
Before trying to run, check that the server
(tarantool_box) is running on localhost (127.0.0.1) and its primary port is the default (33013) and
space[0]'s primary key type is numeric (space[0].index[0].key_field[0].type = "NUM" in configuration file).
To run, paste the code into a file named example.php and say php example.php
.
The program will open a socket connection
with the tarantool_box server at localhost:33013, then send an INSERT request,
then — if all is well — print "Insert succeeded".
If the tuple already exists, the program will print “Duplicate key exists in unique index 0”.
<?php $tarantool = new Tarantool("localhost", 33013, 33015); try { $tarantool->insert(0, array(99999, "BB"), TARANTOOL_FLAGS_ADD); print "Insert succeeded\n"; } catch (Exception $e) { echo "Exception: ", $e->getMessage(), "\n"; } ?>
The example program only shows one command and does not show all that's necessary for
good practice. For that, please see
tarantool-php
project at GitHub.
Here is a complete Python program that inserts [99999,'BB'] into space[0] via the high-level Python API.
To prepare, paste the code into a file named example.py and install tarantool-python with either
to install in pip install tarantool
/usr
(requires root privilege)
or
to install in pip install tarantool --user
~
i.e. user's default directory.
Before trying to run, check that the server (tarantool_box) is running on localhost (127.0.0.1) and its primary port
is the default (33013) and space[0]'s primary key type is string (space[0].index[0].key_field[0].type = "STR" in configuration file).
To run, say python example.py
.
The program will connect to the server, will send the request, and will not throw an exception if all went well.
If the row already exists, the program will throw DatabaseException(“Duplicate key exists in unique index 0”).
#!/usr/bin/python from tarantool import Connection c = Connection("127.0.0.1", 33013) result = c.insert(0,(99999,'BB')) print result
The example program only shows one command and does not show all that's necessary for
good practice. For that, please see
http://tarantool-python.readthedocs.org/en/latest/
.
For an example of a Python API for Queue managers on Tarantool, see
https://github.com/tarantool/tarantool-queue-python
.
You need Ruby 1.9 or later
to use this connector. Connector sources are located in http://github.com/mailru/tarantool-ruby
.
Linux and FreeBSD operating systems allow a running process to modify its title, which otherwise contains the program name. Tarantool uses this feature to help meet the needs of system administration, such as figuring out what services are running on a host, what TCP/IP ports are in use, and so on.
Tarantool process title follows the following naming scheme:
program_name
: role
[@custom_proc_title] [ports in use]
program_name is typically tarantool_box. The role can be one of the following:
primary -- the master node,
replica/IP
:port
-- a replication node,
replication_server -- runs only if replication_port is set, accepts connections on this port and creates a
replication_relay -- a process that servers a single replication connection.
Possible port names are: “pri” for primary_port, “sec” for secondary_port, “adm” for admin_port and “memcached” for memcached_port.
For example:
tarantool_box: primary pri: 50000 sec: 50001 adm: 50002
tarantool_box: primary@infobox pri: 15013 sec: 15523 adm: 10012
In the current version of the binary protocol, error message,
which is normally more descriptive than error code,
is not present in server response. The actual message may contain
a file name, a detailed reason or an operating system error code.
All such messages, however, are logged in the error log. When
using Memcached protocol, the error message is sent to the
client along with the code. Below follow only general descriptions
of some popular codes. A complete list of errors can be found in
file errcode.h
in the source tree.
List of error codes
Attempt to execute an update on a running replica.
Illegal parameters. Malformed protocol message.
Out of memory: slab_alloc_arena limit is reached.
Failed to record the change in the write ahead log. Some sort of disk error.
Key part count is greater than index part count
Attempt to access a space that is not configured (doesn't exist).
No index with the given id exists.
An error inside Lua procedure.
Recursion limit reached when creating a new fiber. This is usually an indicator of a bug in a stored procedure, recursively invoking itself ad infinitum.
A error occured during update of a field.
For BITSET indexes, the maximum is 1. For TREE indexes, the theoretical maximum is about 4 billion (BOX_FIELD_MAX) but the practical maximum is the number of fields in a tuple.
10 (BOX_INDEX_MAX).
There is no theoretical maximum. The practical maximum is whatever is specified by space.cardinality in the configuration file, or the maximum tuple length.
255.
The practical limit is the number of file descriptors that one can set with the operating system.
The total maximum size for all spaces is in effect set by the slab_alloc_arena_size parameter in the configuration file, which in turn is limited by the total available memory.
The maximum number of operations that can be in a single update is 4000 (BOX_UPDATE_OP_CNT_MAX).
This is an exercise assignment: “Insert one million tuples. Each tuple should have a constantly-increasing numeric primary-key field and a random alphabetic 10-character string field.”
The purpose of the exercise is to show what Lua functions look like inside Tarantool. It will be necessary to employ the Lua math library, the Lua string library, the Tarantool box library, the Tarantool box.tuple library, loops, and concatenations. It should be easy to follow even for a person who has not used either Lua or Tarantool before. The only requirement is a knowledge of how other programming languages work and a memory of the first two chapters of this manual. But for better understanding, follow the comments and the links, which point to the Lua manual or to elsewhere in this Tarantool manual. To further enhance learning, type the statements in with the tarantool client while reading along.
We are going to use the "tarantool_sandbox" that was created in section Starting Tarantool and making your first database. So there is a single space, and a numeric primary key, a runnning tarantool_box server, and a running tarantool client.
We'll be making functions which go over one line. We don't want the client to send to the server after every line. So we declare a delimiter. This means “Do not send to the server until you see an exclamation mark.”
localhost> SETOPT DELIMITER = '!'
From now on it will be possible to use multiple-line statements, but it will be necessary to end all statements with exclamation marks.
We will start by making a function that returns a fixed string, “Hello world”.
lua function string_function() return "hello world" end!
The word LUA
is a Tarantool keyword that means “we're about to go into Lua.”
The function name is string_function.
The function has one executable statement, return "hello world"
.
The string "hello world" is enclosed in double quotes here,
although Lua doesn't care -- one could use single quotes instead.
The word "end" means “this is the end of the Lua function declaration.”
The word "end" is followed by "!" because "!" happens to be the delimiter that we chose in the previous step.
To confirm that the function works, we can say
call string_function()!
The word CALL is a Tarantool keyword that means “invoke the Lua function.” The effect is that the string which the function returns will end up on the screen.
For more about Lua strings see Lua manual chapter 2.4 "Strings" http://www.lua.org/pil/2.4.html. For more about functions see Lua manual chapter 5 "Functions" http://www.lua.org/pil/5.html.
The screen now looks like this:
localhost>lua function string_function()
->return "hello world"
->end!
--- ... localhost>call string_function()!
Call OK, 1 rows affected ['hello world'] localhost>
Now that string_function exists, we can invoke it from another function.
lua function main_function() local string_value string_value = string_function() return string_value end!
We begin by declaring a variable "string_value". The word "local" means that string_value appears only in main_function. If we didn't use "local" then string_value would be visible everywhere -- even by other users using other clients connected to this server! Sometimes that's a very desirable feature for inter-client communication, but not this time.
Then we assign a value to string_value, namely, the result of string_function(). We don't need the word CALL for this (CALL is only meaningful to the tarantool client, not to the Lua compiler). But we will CALL main_function() to check that it got the value.
For more about Lua variables see Lua manual chapter 4.2 "Local Variables and Blocks" http://www.lua.org/pil/4.2.html.
The screen now looks like this:
localhost>lua function main_function()
->local string_value
->string_value = string_function()
->return string_value
->end!
--- ... localhost>call main_function()!
Call OK, 1 rows affected ['hello world'] localhost>
Now that it's a bit clearer how to make a variable, we can change string_function() so that, instead of returning a fixed literal 'Hello world", it returns a random letter between 'A' and 'Z'.
lua function string_function() local random_number local random_string random_number = math.random(65,90) random_string = string.char(random_number) return random_string end!
It is not necessary to destroy the old string_function() contents, they're simply overwritten. The first assignment invokes a random-number function in Lua's math library; the parameters mean “the number must be an integer between 65 and 90.” The second assignment invokes an integer-to-character function in Lua's string library; the parameter is the code point of the character. Luckily the ASCII value of 'A' is 65 and the ASCII value of 'Z' is 90 so the result will always be a letter between A and Z.
For more about Lua math-library functions see Lua users "Math Library Tutorial" http://lua-users.org/wiki/MathLibraryTutorial. For more about Lua string-library functions see Lua users "String Library Tutorial" http://lua-users.org/wiki/StringLibraryTutorial.
Once again the string_function() can be invoked from main_function() which can be invoked with
call main_function()
The screen now looks like this:
localhost>lua function string_function()
->local random_number
->local random_string
->random_number = math.random(65,90)
->random_string = string.char(random_number)
->return random_string
->end!
--- ... localhost>call main_function()!
Call OK, 1 rows affected ['C'] localhost>
... Well, actually it won't always look like this because math.random() produces random numbers. But for the illustration purposes it won't matter what the random string values are.
Now that it's clear how to produce one-letter random strings, we can reach our goal of producing a ten-letter string by concatenating ten one-letter strings, in a loop.
lua function string_function() local random_number local random_string random_string = "" for x = 1,10,1 do random_number = math.random(65,90) random_string = random_string .. string.char(random_number) end return random_string end!
The words "for x = 1,10,1" mean “start with x equals 1, loop until x equals 10, increment x by 1 for each iteration.” The symbol ".." means "concatenate", that is, add the string on the right of the ".." sign to the string on the left of the ".." sign. Since we start by saying that random_string is "" (a blank string), the end result is that random_string has 10 random letters. Once again the string_function() can be invoked from main_function() which can be invoked with
call main_function()
For more about Lua loops see Lua manual chapter 4.3.4 "Numeric for" http://www.lua.org/pil/4.3.4.html.
The screen now looks like this:
localhost>lua function string_function()
->local random_number
->local random_string
->random_string = ""
->for x = 1,10,1 do
->random_number = math.random(65,90)
->random_string = random_string .. string.char(random_number)
->end
->return random_string
->end!
--- ... localhost>call main_function()!
Call OK, 1 rows affected ['ZUDJBHKEFM'] localhost>
Now that it's clear how to make a 10-letter random string, it's possible to make a tuple that contains a number and a 10-letter random string, by invoking a function in Tarantool's library of Lua functions.
lua function main_function() local string_value string_value = string_function() t = box.tuple.new({1,string_value}) return t end!
Once this is done, t will be the value of a new tuple which has two fields. The first field is numeric: 1. The second field is a random string. Once again the string_function() can be invoked from main_function() which can be invoked with
call main_function()
For more about Tarantool tuples see Tarantool manual section Package box.tuple.
The screen now looks like this:
localhost>lua function main_function()
->local string_value
->string_value = string_function()
->t = box.tuple.new({1,string_value})
->return t
->end!
--- ... localhost>call main_function()!
Call OK, 1 rows affected [1, 'PNPZPCOOKA'] localhost>
Now that it's clear how to make a tuple that contains a number and a 10-letter random string, the only trick remaining is putting that tuple into space[0]. Remember that space[0] is the first space that was defined in the configuration file, so it's like a database table.
lua function main_function() local string_value string_value = string_function() t = box.tuple.new({1,string_value}) box.replace(0,t) end!
The new line here is box.replace(0,t). The first parameter is 0, because the insertion is going to be to space[0]. The second parmeter is the tuple value. To be perfectly correct we could have said box.insert(0,t) here, rather than box.replace(0,t), but "replace" means “insert even if there is already a tuple whose primary-key value is a duplicate”, and that makes it easier to re-run the exercise even if the sandbox database isn't empty. Once this is done, space[0] will contain a tuple with two fields. The first field will be 1. The second field will be a random 10-letter string. Once again the string_function() can be invoked from main_function() which can be invoked with call main_function(). But main_function() won't tell the whole story, because it does not return t, it only puts t into the database. To confirm that something got inserted, we'll use a SELECT statement.
call main_function()! SELECT * FROM t0 WHERE k0 = 1!
For more about Tarantool insert and replace calls, see Tarantool manual section Package box.
The screen now looks like this:
localhost>lua function main_function()
->local string_value
->string_value = string_function()
->t = box.tuple.new({1,string_value})
->box.replace(0,t)
->end!
--- ... localhost>call main_function()!
Call OK, 0 rows affected localhost>SELECT * FROM t0 WHERE k0 = 1!
Select OK, 1 rows affected [1, 'EUJYVEECIL'] localhost>
Now that it's clear how to insert one tuple into the database, it's no big deal to figure out how to scale up: instead of inserting with a literal value = 1 for the primary key, insert with a variable value = between 1 and 1 million, in a loop. Since we already saw how to loop, that's a simple thing. The only extra wrinkle that we add here is a timing function.
lua function main_function() local string_value start_time = os.clock() for i = 1,1000000,1 do string_value = string_function() t = box.tuple.new({i,string_value}) box.replace(0,t) end end_time = os.clock() end! call main_function()! lua print('inserted 1 million tuples in ' .. end_time - start_time .. ' seconds')!
The Lua os.clock() function will return the number of seconds since the start. Therefore, by getting start_time = number of seconds just before the inserting, and then getting end_time = number of seconds just after the inserting, we can calculate (end_time - start_time) = elapsed time in seconds. We will display that value with Lua's answer to the C printf() function, which is print().
For more on Lua os.clock() see Lua manual chapter 22.1 "Date and Time" http://www.lua.org/pil/22.1.html. For more on Lua print() see Lua manual chapter 5 "Functions" http://www.lua.org/pil/5.html.
Since this is the grand finale, we will redo the final versions of all the necessary statements: the SETOPT DELIMITER statement, the statement that created string_function(), the statement that created main_function(), and the statement that invokes main_function().
#NB: If you have already said "setopt delimiter = '!'", do not say it again -- skip this statement. setopt delimiter = '!' lua function string_function() local random_number local random_string random_string = "" for x = 1,10,1 do random_number = math.random(65,90) random_string = random_string .. string.char(random_number) end return random_string end! lua function main_function() local string_value start_time = os.clock() for i = 1,1000000,1 do string_value = string_function() t = box.tuple.new({i,string_value}) box.replace(0,t) end end_time = os.clock() end! call main_function()! lua print('inserted 1 million tuples in ' .. end_time - start_time .. ' seconds')!
The screen now looks like this:
localhost>setopt delimiter = '!'!
localhost>lua function string_function()
->local random_number
->local random_string
->random_string = ""
->for x = 1,10,1 do
->random_number = math.random(65,90)
->random_string = random_string .. string.char(random_number)
->end
->return random_string
->end!
--- ... localhost>lua function main_function()
->local string_value
->start_time = os.clock()
->for i = 1,1000000,1 do
->string_value = string_function()
->t = box.tuple.new({i,string_value})
->box.replace(0,t)
->end
->end_time = os.clock()
->end!
--- ... localhost>call main_function()!
Call OK, 0 rows affected localhost>lua print('inserted 1 million tuples in ' .. end_time - start_time .. ' seconds')!
--- inserted 1 million tuples in 41.66 seconds ... localhost>
What has been shown is that Lua functions are quite expressive (in fact one can do more with Tarantool's Lua stored procedures than one can do with stored procedures in some SQL DBMSs), and that it's straightforward to combine Lua-library functions and Tarantool-library functions.
What has also been shown is that inserting a million tuples took 42 seconds. The host computer was a Toshiba laptop with a 2.2-GHz Intel Core Duo CPU.
A plugin is an optional library which enhances Tarantool functionality.
The details of creating one's own plugin are described on the Tarantool Plugin API wiki page.
The discussion here in the user guide is about incorporating and using two plugins that have already been created: the "SQL DBMS plugins" for MySQL and PostgreSQL.
To call another DBMS from Tarantool, the essential requirements are: another DBMS, and Tarantool.
It will be necessary to build Tarantool from source, as described in “Downloading and building a source package”.
The Tarantool plugins allow for connecting to an SQL server and executing SQL statements the same way that a MySQL or PostgreSQL client does. The SQL statements are visible as Lua methods. Thus Tarantool can serve as a "MySQL Lua Connector" or "PostgreSQL Lua Connector", which would be useful even if that was all Tarantool could do. But of course Tarantool is also a DBMS, so the plugin also is useful for any operations, such as database copying and accelerating, which work best when the application can work on both SQL and Tarantool inside the same Lua routine.
The connection method is
box.net.sql.connect('mysql'|'postgresql',
.
The methods for select/insert/etc. are the same as the ones in the box.net library.
host
, port
, user
, password
, database
)
This example assumes that MySQL 5.5 or MySQL 5.6 has been installed (recent MariaDB versions should also work).
The example was run on a Linux machine where the base directory had a copy of the Tarantool source on ~/tarantool-stable, and a copy of MySQL on ~/mysql-5.5. The mysqld server is already running on the local host 127.0.0.1.
# Check that the include subdirectory exists by looking for ~/include/mysql.h.$
[ -f ~/mysql-5.5/include/mysql.h ] && echo "OK" || echo "Error"
OK # Check that the library subdirectory exists and has the necessary .so file.$
[ -f ~/mysql-5.5/lib/libmysqlclient.so ] && echo "OK" || echo "Error"
OK # Check that the mysql client can connect using some factory defaults: # port = 3306, user = 'root', user password = '', database = 'test'. # These can be changed, provided one changes them in all places.$
~/mysql-5.5/bin/mysql --port=3306 --host=127.0.0.1 --user=root --database=test
Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 25 Server version: 5.5.35 MySQL Community Server (GPL) ... Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.mysql>
# Insert a row in database test, and quit.mysql>
CREATE TABLE IF NOT EXISTS test (s1 INT, s2 VARCHAR(50));
Query OK, 0 rows affected (0.13 sec)mysql>
INSERT INTO test.test VALUES (1,'MySQL row');
Query OK, 1 row affected (0.02 sec)mysql>
QUIT
Bye # Build the Tarantool server. Make certain that "cmake" gets the right # paths for the MySQL include directory and the MySQL libmysqlclient # library which were checked earlier.$
cd ~/tarantool-stable
$
make clean
$
rm CMakeCache.txt
$
cmake . -DWITH_MYSQL=on -DMYSQL_INCLUDE_DIR=~/mysql-5.5/include -DMYSQL_LIBRARIES=~/mysql-5.5/lib/libmysqlclient.so -DENABLE_CLIENT=true
... -- Found MySQL includes: ~/mysql-5.5/include/mysql.h -- Found MySQL library: ~/mysql-5.5/lib/libmysqlclient.so -- box.net.sql(mysql) INC=~/mysql-5.5/include -- box.net.sql(mysql) LIBS=~/mysql-5.5/lib/libmysqlclient.so ... -- Configuring done -- Generating done -- Build files have been written to: ~/tarantool-stable$
make
... [ 49%] Built target core [ 50%] Building CXX object src/plugin/mysql/CMakeFiles/mysql.dir/mysql.cc.o Linking CXX shared library libmysql.so [ 50%] Built target mysql ... [100%] Built target xlog [100%] Built target man$
# Before starting Tarantool server, tell it where the MySQL plugin is.$
export TARANTOOL_PLUGIN_DIR=~/tarantool-stable/src/plugin/mysql
$
# Start the Tarantool server. # Run it in the background but let the initial display be in the foreground. # So it's possible to see the message that the plugin was loaded.$
~/tarantool-stable/src/box/tarantool_box&
2013-12-03 17:46:16.239 [12957] 1/sched C> version 1.5.1-271-g610930e 2013-12-03 17:46:16.241 [12957] 1/sched I> Loading plugin: ~/tarantool-stable/src/plugin/mysql/libmysql.so 2013-12-03 17:46:16.242 [12957] 1/sched I> Plugin 'mysql' was loaded, version: 1 ... 2013-12-03 17:46:16.244 [12957] 1/sched C> entering event loop # Type 'Enter' and then start the Tarantool client.$
~/tarantool-stable/client/tarantool/tarantool
localhost>
# Say “show plugins”. Since all has gone well, this is certain to work.localhost>
show plugins
--- plugins: - { name: "mysql", version: 1 } ... # Create a Lua function that will connect to the MySQL server, # retrieve one row, and display the row. # For explanations of the statement types used here, read the # Lua tutorial in the Tarantool user manual.localhost>
SETOPT delimiter = '!'
localhost>
lua function mysql_select ()
->
local dbh = box.net.sql.connect('mysql', '127.0.0.1', 3306, 'root', '', 'test')
->
local test = dbh:select('SELECT * FROM test WHERE s1 = 1')
->
for i, card in pairs(test) do
->
print(card.s2)
->
end
->
end!
--- ...localhost>
SETOPT delimiter = ''!
localhost>
# Execute the Lua function.localhost>
CALL mysql_select()
2013-12-03 17:57:24.688 [12957] 102/iproto I> MySQL row Call OK, 0 rows affected # Observe the result. It contains "MySQL row". # So this is the row that was inserted into the MySQL database. # And now it's been selected with the Tarantool client.
This example assumes that a recent version of PostgreSQL has been installed. The PostgreSQL library library and include files are also necessary. On Ubuntu they can be installed with
$
sudo apt-get install libpq-dev
If that works, then cmake will find the necessary files without requiring any special user input. However, because not all platforms are alike, for this example the assumption is that the user must check that the appropriate PostgreSQL files are present and must explicitly state where they are when building Tarantool from source.
The example was run on a Linux machine where the base directory had a copy of the Tarantool source on ~/tarantool-stable, and a copy of PostgreSQL on /usr. The postgres server is already running on the local host 127.0.0.1.
# Check that the include subdirectory exists by looking for /usr/include/postgresql/libpq-fe-h.$
[ -f /usr/include/postgresql/libpq-fe.h ] && echo "OK" || echo "Error"
OK # Check that the library subdirectory exists and has the necessary .so file.$
[ -f /usr/lib/libpq.so ] && echo "OK" || echo "Error"
OK # Check that the psql client can connect using some factory defaults: # port = 5432, user = 'postgres', user password = 'postgres', database = 'postgres'. # These can be changed, provided one changes them in all places. # Insert a row in database postgres, and quit.$
~psql -h 127.0.0.1 -p 5432 -U postgres -d postgres
Password for user postgres: psql (9.3.0, server 9.3.2) SSL connection (cipher: DHE-RSA-AES256-SHA, bits: 256) Type "help" for help.postgres=#
CREATE TABLE test (s1 INT, s2 VARCHAR(50));
CREATE TABLEpostgres=#
INSERT INTO test VALUES (1,'PostgreSQL row');
INSERT 0 1postgres=#
\q
$
# Build the Tarantool server. Make certain that "cmake" gets the right # paths for the PostgreSQL include directory and the PostgreSQL libpq # library which were checked earlier.$
cd ~/tarantool-stable
$
make clean
$
rm CMakeCache.txt
$
cmake . -DWITH_POSTGRESQL=on -DPostgreSQL_LIBRARY=/usr/lib/libpq.so -DPostgreSQL_INCLUDE_DIR=/usr/include/postgresql -DENABLE_CLIENT=true
... -- Found PostgreSQL: /usr/lib/libpq.so -- box.net.sql(pg): INC=/usr/include/postgresql;/usr/include/postgresql -- box.net.sql(pg): LIBS=pq ... -- Configuring done -- Generating done -- Build files have been written to: ~/tarantool-stable$
make
... [ 49%] Built target core [ 50%] Building CXX object src/plugin/pg/CMakeFiles/pg.dir/pg.cc.o Linking CXX shared library libpg.so [ 50%] Built target pg ... [100%] Built target xlog [100%] Built target man$
# Before starting Tarantool server, tell it where the PostgreSQL plugin is.$
export TARANTOOL_PLUGIN_DIR=~/tarantool-stable/src/plugin/pg
$
# Start the Tarantool server. # Run it in the background but let the initial display be in the foreground. # So it's possible to see the message that the plugin was loaded.$
~/tarantool-stable/src/box/tarantool_box&
2013-12-06 13:01:51.518 [23978] 1/sched C> version 1.5.1-290-g45b93e7 2013-12-06 13:01:51.520 [23978] 1/sched I> Loading plugin: ~/tarantool-stable/src/plugin/pg/libpg.so 2013-12-06 13:01:51.527 [23978] 1/sched I> Plugin 'postgresql' was loaded, version: 1 ... 2013-12-06 13:01:51.531 [23978] 1/sched C> entering event loop # Type 'Enter' and then start the Tarantool client.$
~/tarantool-stable/client/tarantool/tarantool
localhost>
# Say “show plugins”. Since all has gone well, this is certain to work.localhost>
show plugins
--- plugins: - { name: "postgresql", version: 1 } ... # Create a Lua function that will connect to the PostgreSQL server, # retrieve one row, and display the row. # For explanations of the statement types used here, read the # Lua tutorial in the Tarantool user manual.localhost>
SETOPT delimiter = '!'
localhost>
lua function postgresql_select ()
->
local dbh = box.net.sql.connect('pg', '127.0.0.1', 5432, 'postgres', 'postgres', 'postgres')
->
local test = dbh:select('SELECT * FROM test WHERE s1 = 1')
->
for i, card in pairs(test) do
->
print(card.s2)
->
end
->
end!
--- ...localhost>
SETOPT delimiter = ''!
localhost>
# Execute the Lua function.localhost>
CALL postgresql_select()
2013-12-06 13:07:45.893 [23978] 102/iproto I> PostgreSQL row Call OK, 0 rows affected # Observe the result. It contains "PostgreSQL row". # So this is the row that was inserted into the PostgreSQL database. # And now it's been selected with the Tarantool client.