Quick start

Connecting to the server

Create a connection to the server:

>>> import tarantool
>>> conn = tarantool.Connection('localhost', 3301, user='user', password='pass')

Data manipulation

Select

select() a tuple with id 'AAAA' from the space demo using primary index:

>>> resp = conn.select('demo', 'AAAA')
>>> len(resp)
1
>>> resp[0]
['AAAA', 'Alpha']

select() a tuple with secondary index key 'Alpha' from the space demo with secondary index sec:

>>> resp = conn.select('demo', 'Alpha', index='sec')
>>> resp
- ['AAAA', 'Alpha']

Insert

insert() the tuple ('BBBB', 'Bravo') into the space demo:

>>> conn.insert('demo', ('BBBB', 'Bravo'))
- ['BBBB', 'Bravo']

Throws an error if there is already a tuple with the same primary key.

>>> try:
...     conn.insert('demo', ('BBBB', 'Bravo'))
... except Exception as exc:
...     print(exc)
...
(3, 'Duplicate key exists in unique index "pk" in space "demo" with old tuple - ["BBBB", "Bravo"] and new tuple - ["BBBB", "Bravo"]')

Replace

replace() inserts the tuple ('CCCC', 'Charlie') into the space demo, if there is no tuple with primary key 'CCCC':

>>> conn.replace('demo', ('CCCC', 'Charlie'))
- ['CCCC', 'Charlie']

If there is already a tuple with the same primary key, replaces it:

>>> conn.replace('demo', ('CCCC', 'Charlie-2'))
- ['CCCC', 'Charlie-2']

Update

update() the tuple with id 'BBBB' placing the value 'Bravo-2' into the field 1:

>>> conn.update('demo', 'BBBB', [('=', 1, 'Bravo-2')])
- ['BBBB', 'Bravo-2']

Field numeration starts from zero, so the field 0 is the first element in the tuple. Tarantool 2.3.1 and newer supports field name identifiers.

Upsert

upsert() inserts the tuple, if tuple with id 'DDDD' not exists. Otherwise, updates tuple fields.

>>> conn.upsert('demo', ('DDDD', 'Delta'), [('=', 1, 'Delta-2')])

>>> conn.select('demo', 'DDDD')
- ['DDDD', 'Delta']
>>> conn.upsert('demo', ('DDDD', 'Delta'), [('=', 1, 'Delta-2')])

>>> conn.select('demo', 'DDDD')
- ['DDDD', 'Delta-2']

Delete

delete() a tuple identified by id 'AAAA':

>>> conn.delete('demo', 'AAAA')
- [('AAAA', 'Alpha')]

Creating a space instance

An instance of Space is a named object to access the key space.

Create a demo object that will be used to access the space with id 'demo':

>>> demo = conn.space('demo')

You can use the space instance to do data manipulations without specifying space id.

>>> demo.select('AAAA')
- ['AAAA', 'Alpha']
>>> demo.insert(('BBBB', 'Bravo'))
- ['BBBB', 'Bravo']

Call server-side functions

call() a stored Lua procedure:

>>> conn.call("my_add", (1, 2))
- 3

Evaluate Lua code

eval() arbitrary Lua code on a server:

>>> lua_code = r"""
...     local a, b = ...
...     return a + b
... """
>>> conn.eval(lua_code, (1, 2))
- 3

Execute SQL query

execute() SQL query on a Tarantool server:

>>> conn.execute('insert into "demo" values (:id, :name)', {'id': 'BBBB', 'name': 'Bravo'})

Connecting to a cluster of servers

Create a connection to several servers:

>>> import tarantool
>>> conn = tarantool.ConnectionPool(
...        [{'host':'localhost', 'port':3301},
...        {'host':'localhost', 'port':3302}],
...        user='user', password='pass')

ConnectionPool is best suited to work with a single replicaset. Its API is the same as a single server Connection, but requests support mode parameter (a tarantool.Mode value) to choose between read-write and read-only pool instances:

>>> resp = conn.select('demo', 'AAAA', mode=tarantool.Mode.PREFER_RO)
>>> resp
- ['AAAA', 'Alpha']

Receiving out-of-band messages

Receiving out-of-band messages from a server that uses box.session.push call is supported for methods: call(), eval(), select(), insert(), replace(), update(), upsert(), delete().

To work with out-of-band messages, 2 optional arguments are used in the methods listed above:

  • on_push - callback, launched with the received data for each out-of-band message. Two arguments for this callback are expected:

    • the first is the received from an out-of-band message data.

    • the second is on_push_ctx, variable for working with callback context (for example, recording the result or pass data to callback).

  • on_push_ctx - result of the on_push work can be written to this variable, or through this variable you can pass data to on_push callback.

Below is an example of the proposed API with method call() and insert(). In the described example, before the end of the call() and insert(), out-of-band messages are processed via specified callback.

In the example below, two shells are used, in the first we will configure the server:

fiber = require('fiber')
box.cfg({listen = 3301})
box.schema.user.grant(
    'guest',
    'read,write,execute',
    'universe'
)
function server_function()
    x = {0,0}
    while x[1] < 3 do
        x[1] = x[1] + 1
        fiber.sleep(1)
        box.session.push(x)
    end
    fiber.sleep(1)
    return x
end

In the second shell, we will execute a call() with receiving out-of-band messages from the server:

import tarantool

def callback(data, on_push_ctx=[]):
    print('run callback with data: ', data)
    data[0][1] = data[0][1] + 1
    on_push_ctx.append(data)

callback_res = []

conn = tarantool.Connection(port=3301)
res = conn.call(
    'server_function',
    on_push=callback,
    on_push_ctx=callback_res
)

# receiving out-of-band messages,
# the conn.call is not finished yet.

>>> run callback with data:  [[1, 0]]
>>> run callback with data:  [[2, 0]]
>>> run callback with data:  [[3, 0]]

# the conn.call is finished now.

print(res)
>>> [3, 0]

print(callback_res)
>>> [[[1, 1]], [[2, 1]], [[3, 1]]]

Let’s go back to the first shell with the server and create a space and a trigger for it:

box.schema.create_space(
 'tester', {
  format = {
    {name = 'id', type = 'unsigned'},
    {name = 'name', type = 'string'},
  }
})
box.space.tester:create_index(
 'primary_index', {
  parts = {
    {field = 1, type = 'unsigned'},
  }
})
function on_replace_callback()
    x = {0,0}
    while x[1] < 300 do
        x[1] = x[1] + 100
        box.session.push(x)
    end
    return x
end
box.space.tester:on_replace(
    on_replace_callback
)

Now, in the second shell, we will execute an insert() with out-of-band message processing:

callback_res = []

conn_pool = tarantool.ConnectionPool(
        [{'host':'localhost', 'port':3301}],
        user='guest')

res = conn_pool.insert(
    'tester',
    (1, 'Mike'),
    on_push=callback,
    on_push_ctx=callback_res,
)

# receiving out-of-band messages,
# the conn_pool.insert is not finished yet.

>>> run callback with data:  [[100, 0]]
>>> run callback with data:  [[200, 0]]
>>> run callback with data:  [[300, 0]]

# the conn_pool.insert is finished now.

print(res)
>>> [1, 'Mike']

print(callback_res)
>>> [[[100, 1]], [[200, 1]], [[300, 1]]]

Interaction with the crud module

Through the Connection object, you can access crud module methods:

>>> import tarantool
>>> from tarantool.error import CrudModuleError, CrudModuleManyError, DatabaseError
>>> conn = tarantool.Connection(host='localhost',port=3301,fetch_schema=False)

>>> conn.crud_
conn.crud_count(                conn.crud_insert(               conn.crud_insert_object_many(
conn.crud_min(                  conn.crud_replace_object(       conn.crud_stats(
conn.crud_unflatten_rows(       conn.crud_upsert_many(          conn.crud_delete(
conn.crud_insert_many(          conn.crud_len(                  conn.crud_replace(
conn.crud_replace_object_many(  conn.crud_storage_info(         conn.crud_update(
conn.crud_upsert_object(        conn.crud_get(                  conn.crud_insert_object(
conn.crud_max(                  conn.crud_replace_many(         conn.crud_select(
conn.crud_truncate(             conn.crud_upsert(               conn.crud_upsert_object_many(

As an example, consider crud_insert() and crud_insert_object_many(). It is recommended to enclose calls in the try-except construction as follows:

# Insert without exception:
>>> res = conn.crud_insert('tester', (3500,300,'Rob'))
>>> res
<tarantool.crud.CrudResult object at 0x11a56e320>
>>> res.
res.metadata  res.rows
>>> res.rows
[[3500, 300, 'Rob']]
>>> res.metadata
[{'name': 'id', 'type': 'unsigned'}, {'name': 'bucket_id', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'}]

# Insert with exception (duplicate key exists):
>>> try:
...     res = conn.crud_insert('tester', (3500,300,'Rob'))
... except CrudModuleError as e:
...     exc_crud = e
...
>>> exc_crud
CrudModuleError(0, 'Failed to insert: Duplicate key exists in unique index "primary_index" in space "tester" with old tuple - [3500, 300, "Rob"] and new tuple - [3500, 300, "Rob"]')
>>> exc_crud.extra_info_error
<tarantool.crud.CrudError object at 0x10a276950>
>>> exc_crud.extra_info_error.
exc_crud.extra_info_error.class_name  exc_crud.extra_info_error.err         exc_crud.extra_info_error.file        exc_crud.extra_info_error.line        exc_crud.extra_info_error.str
>>> exc_crud.extra_info_error.class_name
'InsertError'
>>> exc_crud.extra_info_error.str
'InsertError: Failed to insert: Duplicate key exists in unique index "primary_index" in space "tester" with old tuple - [3500, 300, "Rob"] and new tuple - [3500, 300, "Rob"]'

# In case of batch operation (*_many), CrudModuleManyError exception contains both result and errors (if there is a problem with at least one row).
>>> try:
...     res = conn.crud_insert_object_many('tester', ({'id':3,'bucket_id':100,'name':'Ann'}, {'id':4,'bucket_id':100,'name':'Sam'}), {'timeout':100, 'rollback_on_error':False})
... except CrudModuleManyError as e:
...     exc_crud = e
...
>>> exc_crud
CrudModuleManyError(0, 'Got multiple errors, see errors_list')
>>> exc_crud.success_list # some of the rows were inserted.
<tarantool.crud.CrudResult object at 0x11a56f310>
>>> exc_crud.success_list.rows
[[1, 100, 'Bob'], [2, 100, 'Rob']]
>>> exc_crud.errors_list # some of the rows were not inserted.
[<tarantool.crud.CrudError object at 0x11a56e9e0>, <tarantool.crud.CrudError object at 0x11a56f490>]
>>> exc_crud.errors_list[0].str
'CallError: Failed for 037adb3a-b9e3-4f78-a6d1-9f0cdb6cbefc: Function returned an error: Duplicate key exists in unique index "primary_index" in space "tester" with old tuple - [3500, 300, "Rob"] and new tuple - [3500, 100, "Mike"]'
>>> exc_crud.errors_list[1].str
'InsertManyError: Failed to flatten object: FlattenError: Object is specified in bad format: FlattenError: Unknown field "second_name" is specified'

# If there are no problems with any rows, the entire response will be contained in the res variable.
>>> res = conn.crud_insert_object_many('tester', ({'id':3,'bucket_id':100,'name':'Ann'}, {'id':4,'bucket_id':100,'name':'Sam'}), {'timeout':100, 'rollback_on_error':False})
>>> res.rows
[[3, 100, 'Ann'], [4, 100, 'Sam']]

If module crud not found on the router or user has not sufficient grants:

>>> try:
...     res = conn.crud_insert('tester', (22221,300,'Rob'))
... except DatabaseError as e:
...     exc_db = e
...
>>> exc_db
DatabaseError(33, "Procedure 'crud.insert' is not defined. Ensure that you're calling crud.router and user has sufficient grants")
>>> exc_db.extra_info
BoxError(type='ClientError', file='/tmp/tarantool-20221003-6335-edruh3/tarantool-2.10.3/src/box/lua/call.c', line=112, message="Procedure 'crud.insert' is not defined", errno=0, errcode=33, fields=None, prev=None)

Using crud_select() and crud_unflatten_rows():

>>> res = conn.crud_select('tester', [], {'first':2})
>>> res
<tarantool.crud.CrudResult object at 0x10a276d10>
>>> res.rows
[[1, 100, 'Mike'], [2, 100, 'Bill']]
>>> res.metadata
[{'name': 'id', 'type': 'unsigned'}, {'name': 'bucket_id', 'type': 'unsigned'}, {'name': 'name', 'type': 'string'}]
>>> r = conn.crud_unflatten_rows(res.rows, res.metadata)
>>> r
[{'id': 1, 'bucket_id': 100, 'name': 'Mike'}, {'id': 2, 'bucket_id': 100, 'name': 'Bill'}]

Using crud_truncate() and crud_len():

>>> res = conn.crud_len('tester')
>>> res
26
>>> res = conn.crud_truncate('tester')
>>> res
True