Tarantool master -> slave replication.

1,097 views
Skip to first unread message

Sairam M P

unread,
Feb 18, 2016, 11:23:12 AM2/18/16
to Tarantool discussion group (English)
Hi,

We are evaluating tarantool for our in-memory requirements. Installed tarantool and started both master & slave in interactive mode. Configured the replication_source in the slave. But unable to retrieve the data in the slave that were inserted in master. Anything am i missing ?

Any help would be appreciated. 

Thanks in advance.

-Sairam.

Konstantin Osipov

unread,
Feb 18, 2016, 11:31:38 AM2/18/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/18 19:23]:
What does box.info.replication show on master and slave?

status: should be connected on slave.

--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.org - www.twitter.com/kostja_osipov

Sairam M P

unread,
Feb 18, 2016, 12:01:38 PM2/18/16
to Tarantool discussion group (English)
The below is after is restarted both master & slave and reconfigure the replication. Seems the lag is 0 now and i am able to select the tuples. But when i tried the first time there a was like -4.0....  

Master :

tarantool> box.info.replication
---
- status: off
...

Slave :

tarantool> box.info.replication
---
- status: follow
  idle: 48.279074668884
  lag: 0
...

Konstantin some basic question :

Is there a way i can start tarantool other than interactive mode ? like in background.. Only & can be used or is it by someother means ?

-Sairam.

Konstantin Osipov

unread,
Feb 18, 2016, 12:54:30 PM2/18/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/18 20:04]:
> The below is after is restarted both master & slave and reconfigure the
> replication. Seems the lag is 0 now and i am able to select the tuples. But
> when i tried the first time there a was like -4.0....

It could be the case, yes, if the server can't establish a
replication link it doesn't abort - only writes an error in the
log file.

> Konstantin some basic question :
>
> Is there a way i can start tarantool other than interactive mode ? like in
> background.. Only & can be used or is it by someother means ?

Put the commands you write in the interactive mode into a .lua
file, add #!/usr/bin/env tarantool as the first line of the file,
change permissions to be able to execute the script and run the
script.

If you need the server to daemonize after start, add
background=true to the list of box.cfg{} options.

Better yet, put this script to
/etc/tarantool/instances.enabled and use tarantoolctl start
<instancename>

Sairam M P

unread,
Feb 18, 2016, 1:18:35 PM2/18/16
to Tarantool discussion group (English)
Thanks a lot Osipov. Will try out the suggestions.

-Sairam. 

Sairam M P

unread,
Feb 22, 2016, 3:59:48 AM2/22/16
to Tarantool discussion group (English)
Hi Osipov,

I am finding it little difficult to understand the concept of lsn. When i run a master/slave setup, for whatever the writes i perform in master the lsn is getting incremented but the lsn in slave remains zero(0). How will it work if i break the replication in slave and reconfigure it again. Does the slave maintains the master lsn and tries to reconfigure the replication from that point ? Is the lsn between master slave a global id for replication configuration ? (Like GID in case of MySQL-5.6)

Master info :

tarantool> box.info
---
- server:
    lsn: 38
    ro: false
    uuid: d2e17051-069e-43b7-869e-eb597f2569e6
    id: 1
  version: 1.6.7-622-g12e3527
  pid: 22233
  status: running
  vclock:
  - 38
  - 0
  - 0
  - 0
  replication:
    status: off
  uptime: 1848
...

Slave info :

tarantool> box.info
---
- server:
    lsn: 0
    ro: false
    uuid: 29ffefac-7ec9-4c9d-ab9b-8003884e5c64
    id: 4
  version: 1.6.7-622-g12e3527
  pid: 21870
  status: running
  vclock:
  - 38
  - 0
  - 0
  - 0
  replication:
    status: follow
    idle: 17.216608762741
    lag: -4.5048859119415
  uptime: 238
...

-Sairam.

Sairam M P

unread,
Feb 22, 2016, 5:12:26 AM2/22/16
to Tarantool discussion group (English)
Also can i parse the wal files in master / slave to see their contents ?

-Sairam.

Konstantin Osipov

unread,
Feb 22, 2016, 8:00:46 AM2/22/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/22 12:58]:
> I am finding it little difficult to understand the concept of lsn. When i
> run a master/slave setup, for whatever the writes i perform in master the
> lsn is getting incremented but the lsn in slave remains zero(0). How will
> it work if i break the replication in slave and reconfigure it again. Does
> the slave maintains the master lsn and tries to reconfigure the replication
> from that point ? Is the lsn between master slave a global id for
> replication configuration ? (Like GID in case of MySQL-5.6)

Every server maintains its own log sequence number in its own
server id slot.

Each server maintains a vector of LSN values for itself and all
other servers.



>
> *Master info :*
>
> tarantool> box.info
> ---
> - server:
> *lsn: 38*
> ro: false
> uuid: d2e17051-069e-43b7-869e-eb597f2569e6
> id: 1
> version: 1.6.7-622-g12e3527
> pid: 22233
> status: running
> vclock:

> - 38
> - 0
> - 0
> - 0

You actually see this vector in box.info.vclock.

Here, master server id is 1 (see id: 1 above) and it is the origin
of 38 log records.

You have 3 other replicas in the cluster (I assume you have one,
you've just been deleting replica directories), with local LSN 0.

If you print the same information on a replica, you're going to
get back the same state.

The mapping between short identifiers (id) and long identifiers
(uuid) is stored in box.space._cluster on master.

> tarantool> box.info
> ---
> - server:
> *lsn: 0*
> ro: false
> uuid: 29ffefac-7ec9-4c9d-ab9b-8003884e5c64
> id: 4

^^^^^^^ Your slave server id is 4.

> version: 1.6.7-622-g12e3527
> pid: 21870
> status: running
> vclock:
> - 38
> - 0
> - 0
> - 0


It has the same state as the master.

> replication:
> status: follow
> idle: 17.216608762741
> lag: -4.5048859119415
> uptime: 238
> ...
>
> -Sairam.


We're working on synchronous master-master replication in 1.7,
eventually we're going to have more than one kind.

Sairam M P

unread,
Feb 22, 2016, 10:01:03 AM2/22/16
to Tarantool discussion group (English)
Thanks Osipav.


From the above doc : Once connected to the master, the replica requests all changes that happened after the latest local LSN.

Does this mean master / slave will have the same lsn w.r.t replication else will there be an mapping for both lsns ? 

-Sairam.

Sairam M P

unread,
Feb 22, 2016, 1:56:58 PM2/22/16
to Tarantool discussion group (English)
Hi,

I have the below setup like Master -> Slave -> Sec.Slave. 

tarantool> box.space._cluster:select{}
---
- - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
  - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
  - [3, '7be545b8-b4fb-41e4-b1da-4c64fb9891df']
...

I am trying to break the replication between Slave -> Sec.Slave and reconfigure it as Master -> Sec.Slave.

After breaking the replication between Slave -> Sec.Slave , STILL........... :

tarantool> box.space._cluster:select{}
---
- - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
  - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
  - [3, '7be545b8-b4fb-41e4-b1da-4c64fb9891df']
...

but the box.info
tarantool> box.info
---
- server:
    lsn: 0
    ro: false
    uuid: 7be545b8-b4fb-41e4-b1da-4c64fb9891df
    id: 3
  version: 1.6.7-630-geab01da
  pid: 3932
  status: running
  vclock:
  - 11
  - 1
  - 0
  replication:
    status: off
  uptime: 1387
...

I thought issuing *_cluster:select{} in sec. slave would not produce the above cluster details, as this sec. slave is not in the replication chain now, but still it produces the old cluster chain.

When i try to configure replication between Master -> Sec.Slave, i get the below error :

2016-02-23 00:08:24.670 [3932] main/107/applier/172.x.x.x:3301 I> can't read row
2016-02-23 00:08:24.671 [3932] main/107/applier/172.x.x.x:3301 xrow.cc:259 E> ER_UNKNOWN_SERVER: Server 7be545b8-b4fb-41e4-b1da-4c64fb9891df is not registered with the cluster

Kindly through some lights on tarantool replication, also it would be helpful if you can share some doc explaining the practical working of tarantool replication. 

-Sairam.

Konstantin Osipov

unread,
Feb 23, 2016, 2:38:50 AM2/23/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/23 10:37]:
> Also can i parse the wal files in master / slave to see their contents ?

There is a Lua rock for reading xlog files, it's not pushed yet,
it's on branch gh-1131-xlog-reader-v2.

We plan to release it as a ready to use rock in a month or two.

Konstantin Osipov

unread,
Feb 23, 2016, 2:40:43 AM2/23/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/23 10:37]:
>
> http://tarantool.org/doc/book/replication/index.html#setting-up-a-replica
>
> From the above doc : *Once connected to the master, the replica requests
> all changes that happened after the latest local LSN.*
>
> Does this mean master / slave will have the same lsn w.r.t replication else
> will there be an mapping for both lsns ?

The documentation is not updated from 1.5, where we had only one
entry for server id and only one component in lsn vector. "Local"
lsn means "the value of remote server lsn as seen locally".

I will file a ticket to update the docs.

Konstantin Osipov

unread,
Feb 23, 2016, 3:05:08 AM2/23/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/23 10:37]:
> tarantool> box.space._cluster:select{}
> ---
> - - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
> - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
> - [3, '7be545b8-b4fb-41e4-b1da-4c64fb9891df']
> ...
>
> I am trying to break the replication between *Slave -> Sec.Slave* and
> reconfigure it as *Master -> Sec.Slave.*

The _cluster table does not store configuration, which is dynamic,
it stored the persistent content. Perhaps it sounds confusing,
but please try to think about this way: once a node has joined
a cluster, we assume it never goes away forever, without a DBA
explicit action (delete the node from _cluster), but may flicker
on and off depending on the actual cluster configuration.


> After breaking the replication between* Slave -> Sec.Slave ,
> STILL........... :*
>
> tarantool> box.space._cluster:select{}
> ---
> - - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
> - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
> - [3, '7be545b8-b4fb-41e4-b1da-4c64fb9891df']
> ...


This is hence as expected.

>
> *but the box.info : *
> tarantool> box.info
> ---
> - server:
> lsn: 0
> ro: false
> uuid: 7be545b8-b4fb-41e4-b1da-4c64fb9891df
> id: 3
> version: 1.6.7-630-geab01da
> pid: 3932
> status: running
> vclock:
> - 11
> - 1
> - 0
> * replication:*
> * status: off*
> uptime: 1387
> ...

We're improving replication status display in 1.7. The current
display is imperfect. It only shows you whether this node is
connected as a replica to some master node, not whether there
are any connected replicas.

> I thought issuing *_cluster:select{} in sec. slave would not produce the
> above cluster details, as this sec. slave is not in the replication chain
> now, but still it produces the old cluster chain.
>
> When i try to configure replication between Master -> Sec.Slave, i get the
> below error :
>
> 2016-02-23 00:08:24.670 [3932] main/107/applier/172.x.x.x:3301 I> can't
> read row
> *2016-02-23 00:08:24.671 [3932] main/107/applier/172.x.x.x:3301 xrow.cc:259
> E> ER_UNKNOWN_SERVER: Server 7be545b8-b4fb-41e4-b1da-4c64fb9891df is not
> registered with the cluster*

This message indicates that the second slave doesn't contain the
above uuid in its _cluster table. Given that this server id is
registered on the master, how did it happen that these changes did
not propagate to the replica?

Sairam M P

unread,
Feb 23, 2016, 4:43:51 AM2/23/16
to Tarantool discussion group (English)
>The documentation is not updated from 1.5, where we had only one 
>entry for server id and only one component in lsn vector. "Local" 
>lsn means "the value of remote server lsn as seen locally". 

So for the same event the lsn will be different in master & slave right ? How do tarantool find this association ?

Say if i want to configure replication from particular point, is there an option for the same ?

-Sairam.
Message has been deleted

Sairam M P

unread,
Feb 23, 2016, 5:00:10 AM2/23/16
to Tarantool discussion group (English)


On Tuesday, February 23, 2016 at 1:35:08 PM UTC+5:30, Konstantin Osipov wrote:
* Sairam M P <smart...@gmail.com> [16/02/23 10:37]:
> tarantool> box.space._cluster:select{}
> ---
> - - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
>   - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
>   - [3, '7be545b8-b4fb-41e4-b1da-4c64fb9891df']
> ...
>
> I am trying to break the replication between *Slave -> Sec.Slave* and
> reconfigure it as *Master -> Sec.Slave.*

>The _cluster table does not store configuration, which is dynamic,
it stored the persistent content. Perhaps it sounds confusing,
but please try to think about this way: once a node has joined
a cluster, we assume it never goes away forever, without a DBA
explicit action (delete the node from _cluster), but may flicker
on and off depending on the actual cluster configuration.

   "delete the node from _cluster"  How can i do this ? 
The uuid that the second slave reports as not registered with the cluster is its own uuid. The box.space._cluster:select{} output from second slave :

tarantool> box.space._cluster:select{}
---
- - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
  - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
  - [3, '7be545b8-b4fb-41e4-b1da-4c64fb9891df']
...
 
The box.space._cluster:select{} output from the master :

tarantool> box.space._cluster:select{}
---
- - [1, 'd6338d72-7808-47f6-8961-5fbd27a43950']
  - [2, 'dba55807-cb01-4a6a-88a9-b09c4d6537e9']
...
 
Actually the master output doesn't contains the second slave's uuid. Is this the cause of error ? What can be the reason for the master not having the second slave's uuid.
 
-Sairam.

Konstantin Osipov

unread,
Feb 23, 2016, 2:20:23 PM2/23/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/23 20:17]:
> >The documentation is not updated from 1.5, where we had only one
> >entry for server id and only one component in lsn vector. "Local"
> >lsn means* "the value of remote server lsn as seen locally". *

> So for the same event the lsn will be different in master & slave right ?
> How do tarantool find this association ?

No, the event gets assigned a pair server id, lsn, and it has the
same value on master and slave.

Sairam M P

unread,
Feb 23, 2016, 3:53:32 PM2/23/16
to Tarantool discussion group (English)
>> No, the event gets assigned a pair server id, lsn, and it has the same value on master and slave.

Consider I have a setup like Master -> Slave -> Sec. Slave, when a write happens at master I hope the wal files at master, slave, sec. slave all will contain the same server I'd and Lsn that originated from master. Is my understanding right !?

If so there should not be an issue in reconfiguring replication from master -> sec. slave in the above setup right !?

-Sairam.

Sairam M P

unread,
Feb 23, 2016, 3:57:10 PM2/23/16
to Tarantool discussion group (English)
If not can i reconfigure replication as above and how !?

Please throw some lights on the internals of tarantool replication and its failover.

-Sairam.

Konstantin Osipov

unread,
Feb 23, 2016, 11:15:59 PM2/23/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/24 07:11]:
> >> No, the event gets assigned a pair server id, lsn, and it has the same value on master and slave.
>
> Consider I have a setup like Master -> Slave -> Sec. Slave, when a write happens at master I hope the wal files at master, slave, sec. slave all will contain the same server I'd and Lsn that originated from master. Is my understanding right !?

Yes.


> If so there should not be an issue in reconfiguring replication
> from master -> sec. slave in the above setup right !?

Yes.

So I wonder how it happened in your case that the entry about one
of the server was not present in one of the replica's _cluster
table.

Konstantin Osipov

unread,
Feb 23, 2016, 11:24:14 PM2/23/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/24 07:11]:
> Please throw some lights on the internals of tarantool
> replication and its failover.

Sairam, I can talk hours on internals, in this case it's more
important to understand what happened. Here's my guess:

You configured 3 replicas into a chain. The chain wasn't a closed
into a circle, so the flow of events was only in one direction.

Registering a replica is an event, it happens as a write in
_cluster table. When third replica got created, this event
was registered on replica #2, but not on replica #1, since there
was no flow of events from replica #2 to replica #1.

To fix this, I think you should have the same value for
replication_source on all replicas, in the same order, e.g.
replication_source={"uri1:port1", "uri2:port2", "uri3:port3"}.

It's OK to list "self" in replication_source, the link is removed
automatically.

If you do this, you don't need to handle the failover event in
any special way - all replicas are connected already.

Sairam M P

unread,
Feb 24, 2016, 9:42:47 AM2/24/16
to Tarantool discussion group (English)
Konstantin,

tarantool> box.cfg{replication_source="uri1:3301","uri2:3301","uri3:3301"}
---
- error: '[string "-- load_cfg.lua - internal file..."]:189: Error: cfg parameter
    ''1'' is unexpected'

When i try to configure like that, it throws the above error.

-Sairam.

Konstantin Osipov

unread,
Feb 24, 2016, 11:23:08 AM2/24/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/24 18:00]:
> tarantool> box.cfg{replication_source="uri1:3301","uri2:3301","uri3:3301"}
> ---
> - error: '[string "-- load_cfg.lua - internal file..."]:189: Error: cfg
> parameter
> ''1'' is unexpected'
>
> When i try to configure like that, it throws the above error.

Please use 1.6.8, http://tarantool.org/dist/1.6

Which OS are you using?

Sairam M P

unread,
Feb 24, 2016, 11:46:49 AM2/24/16
to Tarantool discussion group (English)

>> Which OS are you using? 

CentOS release 6.4 (Final) 

Linux x.x.x.x  2.6.32-431.29.2.el6.x86_64 #1 SMP Tue Sep 9 21:36:05 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

-Sairam.

Konstantin Osipov

unread,
Feb 24, 2016, 11:52:47 AM2/24/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/24 19:49]:
>
> >> Which OS are you using?
> CentOS release 6.4 (Final)

OK, you should follow the download instructions for CentOS but
replace dist/master with dist/1.6 in them:

# Enable EPEL repository
sudo yum -y install http://dl.fedoraproject.org/pub/epel/epel-release-latest-6.noarch.rpm
# Add Tarantool repository
sudo tee /etc/yum.repos.d/tarantool.repo <<- EOF
[tarantool]
name=CentOS-6 - Tarantool
baseurl=http://tarantool.org/dist/1.6/centos/6/os/\$basearch/
enabled=1
gpgcheck=0
EOF
# Install Tarantool
sudo yum clean all
sudo yum -y install tarantool

Sairam M P

unread,
Feb 25, 2016, 8:24:31 AM2/25/16
to Tarantool discussion group (English)
Hi Osipov,

When execute the below commands it tries to install tarantool-1.6.7-630, but you have suggested to install 1.6.8 in the above thread. How can i install the latest tarantool which supports the configuration of multiple uri's in the replication_source.

-Sairam.

Konstantin Osipov

unread,
Feb 25, 2016, 8:39:20 AM2/25/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/25 16:28]:
> When execute the below commands it tries to install *tarantool-1.6.7-630*,
> but you have suggested to install *1.6.8* in the above thread. How can i
> install the latest tarantool which supports the configuration of multiple
> uri's in the replication_source.

It's hard to diagnose the problem without seeing the output from
commands you issued.

In any case, we have released 1.6.8 today, so if you simply follow
the current installation instructions on our web site, you should
be able to install it.

http://tarantool.org/download.html

Sairam M P

unread,
Feb 25, 2016, 2:11:16 PM2/25/16
to Tarantool discussion group (English)
Hi Osipov,

Installed 1.6.8 as suggested in http://tarantool.org/download.html . The exact installed version : /usr/bin/tarantool: version 1.6.8-525-ga571ac0

Tried configuring replication like  box.cfg{replication_source="uri1:3301","uri2:3301","uri3:3301"}

But getting the below error :

 tarantool> box.cfg{replication_source="x.x.x.x:3301","y.y.y.y:3301","z.z.z.z:3301"}
---
- error: 'Incorrect value for option ''1'': unexpected option'
...

All the above 3 servers are listening in 3301. Did using box.cfg{listen=3301} in all the above 3 servers. Also i am trying things in interactive mode.

Please let us know on how configure replication using more than one server.

Also getting the below error on configuring replication between just 2 server :

2016-02-26 00:37:23.519 [3403] main/104/applier/172.20.11.96:3301 I> can't read row
2016-02-26 00:37:23.519 [3403] main/104/applier/x.x.x.x:3301 xrow.cc:262 E> ER_CLUSTER_ID_MISMATCH: Cluster id of the replica 7dd92617-d2b6-4048-852c-6f99dc45a0fe doesn't match cluster id of the master 131f432c-131a-4526-a311-974cfcd56357
---

server 1 : box.info 

tarantool> box.info
---
- server:
    lsn: 1
    ro: false
    uuid: e4227fd0-3b0f-4a66-b6c0-2c5085c675cf
    id: 1
  cluster:
    uuid: 131f432c-131a-4526-a311-974cfcd56357
  version: 1.6.8-525-ga571ac0
  pid: 31481
  status: running
  vclock:
  - 1
  replication:
    status: off
  uptime: 8
...

server 2 : box.info

tarantool> box.info
---
- server:
    lsn: 0
    ro: false
    uuid: d467a674-4e0a-4212-8fda-05d46da0639b
    id: 1
  cluster:
    uuid: 7dd92617-d2b6-4048-852c-6f99dc45a0fe
  version: 1.6.8-525-ga571ac0
  pid: 3403
  status: running
  vclock:
  - 0
  replication:
    status: off
  uptime: 8
...

The cluster uuid in both of these servers differs. Is this the expected behavior or am i missing something.

Kindly help us on configuring and evaluating replication & failover in tarantool.

-Sairam.

Konstantin Osipov

unread,
Feb 25, 2016, 2:28:43 PM2/25/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/25 22:13]:
> Hi Osipov,
>
> Installed* 1.6.8 *as suggested in http://tarantool.org/download.html . The
> exact installed version : */usr/bin/tarantool: version 1.6.8-525-ga571ac0*
>
> Tried configuring replication like
> *box.cfg{replication_source="uri1:3301","uri2:3301","uri3:3301"}*
>
> *But getting the below error :*
>
> tarantool>
> box.cfg{replication_source="x.x.x.x:3301","y.y.y.y:3301","z.z.z.z:3301"}

The syntax above is not a correct Lua program.

Please use:
box.cfg{replication_source={"x.x.x.x:3301","y.y.y.y:3301","z.z.z.z:3301"}}


Konstantin Osipov

unread,
Feb 25, 2016, 2:33:09 PM2/25/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/25 22:13]:

> Also getting the below error on configuring replication between just 2
> server :
>
> 2016-02-26 00:37:23.519 [3403] main/104/applier/172.20.11.96:3301 I> can't
> read row
> 2016-02-26 00:37:23.519 [3403] main/104/applier/x.x.x.x:3301 xrow.cc:262 E> *ER_CLUSTER_ID_MISMATCH:
> Cluster id of the replica 7dd92617-d2b6-4048-852c-6f99dc45a0fe doesn't
> match cluster id of the master 131f432c-131a-4526-a311-974cfcd56357*
> ---

Please follow these steps:

1. Make sure data directories of all instances are empty.

2. Create the first instance in the cluster, thus bootstrapping
the cluster:

For that, start the first instance (the one that will be listening
on the first uri in the list) with the following configuration:

box.cfg{}

Then stop the instance.

3. Bootstrap replicas.

For that, start all instances with

box.cfg{replication_source={"uri1", "uri2", "uri3"}}


Replicas at uri2 and uri3 will register with the cluster
and download their initial snapshots from it.

4. From now on you can start and stop the cluster in any order.
The order of the first start must be as I described above.

Thanks.

Sairam M P

unread,
Feb 26, 2016, 5:46:27 AM2/26/16
to Tarantool discussion group (English)
Hi Konstantin,

Thanks a lot for the detailed steps and i am able to configure the cluster by following the aforementioned steps.

Few clarifications :

Replication :

We assume the cluster configured through the steps mentioned by you is in MULTI-MASTER fashion..! Is that right ?

And how does the replication works in the cluster configured through these steps !? 
Since we have configured all the 3 server ips in all the 3 servers, from where did the sec. slave receive the writes that are performed in master. Is that directly from master to sec. slave or from master to slave to sec. slave.

If all the stuff that we are discussion till now are related to MULTI-MASTER then is there an way in tarantool to have a cluster setup like Master -> Slave -> Sec. Slave fashion ? Which means, slave pulls data only from master, sec. slave pulls data only from slave and only in this direction.

In both of the above approach, how can i find(command to find) from which all servers this particular server replicates ?

Failover :

Whether forming a cluster setup in tarantool always requires re-start of the instance ? 
For eg., When i have a cluster setup with 3 servers(a,b,c), what if i need to replace the server c with d or if i need to add one more server e in the cluster setup. Whether the restart of all (a,b,c) instances is required for performing the failover ?

If thats the case, we can't restart the tarantool instance(s) in production when the service is in live.


-Sairam.

Konstantin Osipov

unread,
Feb 26, 2016, 7:10:54 AM2/26/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/26 13:51]:

> We assume the cluster configured through the steps mentioned by you is in*
> MULTI-MASTER *fashion..! Is that right ?

Yes.

> And how does the replication works in the cluster configured through these
> steps !?

It's asynchronous replication, we only ensure that each event is
applied once on every node.

> Since we have configured all the 3 server ips in all the 3 servers, from
> where did the sec. slave receive the writes that are performed in master.
> Is that directly from* master to sec. slave* or from *master to slave to
> sec. slave*.

The replication is done using binlog shipping. It will receive
these events from the master and from the second slave, but will
apply them once only. We will improve this so that the replication
subscription request can contain specific server ids only, so that
identical events are not shipped twice via a redundant route/link.

> If all the stuff that we are discussion till now are related to
> MULTI-MASTER then* is there an way* in tarantool to have a cluster setup
> like* Master -> Slave -> Sec. Slave fashion* ? Which means, slave pulls
> data only from master, sec. slave pulls data only from slave and only in
> this direction.

Yes, the topology can be arbitrary, and you can change it
dynamically. But you must ensure that _cluster system space is
identical across all members, otherwise your failover won't work
(as you experienced in the earlier emails).

Whenever your replica connects for the first time and downloads
the initial snapshot, the master it downloads this snapshot
from inserts the replica UUID into its _cluster system space and
assigns this replica a short numeric identifier (server id), which
serves as a slot in the vector clock (box.info.vclock).

So I think the easiest way to get the topology master -> slave -> second slave,
is to first fire up a multi-master setting, then reconfigure the
cluster with the new topology.

> In both of the above approach, how can i find*(command to find)* from which
> all servers this particular server replicates ?

I hope I explained this so far, the replication is done through
binlog shipping. There are two filters on the shipped events:
before send, and after receive. When subscribing, a replica sends
its vclock to the master, thus the master only ships events which
are not part of replica vclock. On apply, the replica checks event
vclock, and skips it instead of applying if this event was already
applied (because it was received from a different source).

I understand this looks unfinished, mainly we did it to simplify
failover and as ground work for the case when every replica only
receives the events it doesn't have.
>
> *Failover :*
>
> Whether forming a cluster setup in tarantool always requires re-start of
> the instance ?
> *For eg., *When i have a cluster setup with 3 servers(a,b,c), what if i
> need to replace the server c with d or if i need to add one more server e
> in the cluster setup. Whether the restart of all (a,b,c) instances is
> required for performing the failover ?

The restart is an easy way to explain what goes under the hood.
You don't need to restart to fail over, replication topology can
be reconfigured at any time at any node without restart.
In fact, if your topology is full mesh, you don't need to do
anything to fail over. This is not MySQL :), there is no need to run
slaves of slaves, we even try to avoid this old school term.
Let me refer back to my explanation of the _cluster system space
and how it's maintained. Please feel free to call my cell if you
feel this explanation is unclear.

Sairam M P

unread,
Feb 26, 2016, 11:45:53 AM2/26/16
to Tarantool discussion group (English)
Thanks a bunch Osipov.

The explanation is clear and precise. Checking on other aspects, will let you know for any further clarifications.

Thanks again.

-Sairam.

Sairam M P

unread,
Feb 29, 2016, 12:22:18 PM2/29/16
to Tarantool discussion group (English)
Hi,

Have some doubts regarding configuring tarantool instance with customized configurations :

Is initialization file(script.lua) is the only option to customize the configurations ?

Or can i specify the options in the command line during starting the server in the interactive mode ? 

I have installed tarantool by following the instructions from here : http://tarantool.org/download.html

Where does this installation fetch the configuration info from ?

-Sairam.


On Thursday, February 18, 2016 at 11:24:30 PM UTC+5:30, Konstantin Osipov wrote:
* Sairam M P <smart...@gmail.com> [16/02/18 20:04]:
> The below is after is restarted both master & slave and reconfigure the
> replication. Seems the lag is 0 now and i am able to select the tuples. But
> when i tried the first time there a was like -4.0....  

It could be the case, yes, if the server can't establish a
replication link it doesn't abort - only writes an error in the
log file.

> Konstantin some basic question :
>
> Is there a way i can start tarantool other than interactive mode ? like in
> background.. Only & can be used or is it by someother means ?

Put the commands you write in the interactive mode into a .lua
file, add #!/usr/bin/env tarantool as the first line of the file,
change permissions to be able to execute the script and run the
script.

If you need the server to daemonize after start, add
background=true to the list of box.cfg{} options.

Better yet, put this script to
/etc/tarantool/instances.enabled and use tarantoolctl start
<instancename>

Sairam M P

unread,
Feb 29, 2016, 12:25:28 PM2/29/16
to Tarantool discussion group (English)

My OS is CentOS, so i followed the instructions from RHEL 6 and CentOS 6 part.

-Sairam.

Konstantin Osipov

unread,
Feb 29, 2016, 12:34:20 PM2/29/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/02/29 20:24]:
> Hi,
>
> Have some doubts regarding configuring tarantool instance with customized
> configurations :
>
> Is initialization file(script.lua) is the only option to customize the
> configurations ?
>
> Or can i specify the options in the command line during starting the server
> in the interactive mode ?
>
> I have installed tarantool by following the instructions from here
> : http://tarantool.org/download.html
>
> Where does this installation fetch the configuration info from ?

Hi,

There are no options on the command line, we aim at being
option-compatible with plain Lua.

The installation creates an example instance in
/etc/tarantool/instances.available

Please check out tarantoolctl manual:
http://tarantool.org/doc/book/administration.html#configuring-for-tarantoolctl


If you want to put your options into a separate file, simply use
Lua features, such as require() or dofile().

Sairam M P

unread,
Mar 10, 2016, 1:06:13 PM3/10/16
to Tarantool discussion group (English)
Hi Konstantin,

We were trying to dump our data to tarantool and get the same from tarantool. Our data is in byte array format, we were able to insert the byte array data to tarantool and the data is appended as binary in the tarantool db. However when we try to fetch this data through select method we are only able to get this data as string and not as byte array. Kindly let me know on how to select the binary data from tarantool as byte[].

-Sairam.

Konstantin Osipov

unread,
Mar 10, 2016, 1:38:49 PM3/10/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/03/10 21:07]:
> We were trying to dump our data to tarantool and get the same from
> tarantool. Our data is in byte array format, we were able to insert the
> byte array data to tarantool and the data is appended as binary in the
> tarantool db. However when we try to fetch this data through *select*
> method we are only able to get this data as string and not as byte array.
> Kindly let me know on how to select the binary data from tarantool as
> byte[].

Are you using the Java driver?

Do you have an example program in Java I could comment on?

Sairam M P

unread,
Mar 10, 2016, 2:17:11 PM3/10/16
to tara...@googlegroups.com
Yes we are using Java driver.

Here is the snippet : 

try {

TarantoolConnection16 con;

byte[] buf = "156457".getBytes();

con = new TarantoolConnection16Impl("x.x.x.x", 3301);

con.insert(513, Arrays.asList("dummykey",buf));

out.println(((ArrayList)(con.select(513, 0, Arrays.asList("dummykey"), 0,100,0).get(0))).get(1).getClass());//expecting byte[] getting String

}catch(Exception e) {

out.println(e.getMessage());

}


-Sairam.

--
You received this message because you are subscribed to a topic in the Google Groups "Tarantool  discussion group (English)" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/tarantool/ZtL6fI7TZ0M/unsubscribe.
To unsubscribe from this group and all its topics, send an email to tarantool+...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Dmitry Grytsovets

unread,
Mar 11, 2016, 5:42:35 AM3/11/16
to Tarantool discussion group (English)

This happens due to MsgPackLite.OPTION_UNPACK_RAW_AS_STRING. 
I will give ability to set this options in the next driver release. 

At this moment I could advice workaround:

Override ConnectionState upack method to

public void unpack() {
...
 toKeyMap(MsgPackLite.unpack(buffer.asInputStream(), 0), body);
...
}

then change TarantoolConnection16Impl state to overriden one immediatly after construction.

четверг, 10 марта 2016 г., 22:17:11 UTC+3 пользователь Sairam M P написал:
Yes we are using Java driver.

Here is the snippet : 

try {

TarantoolConnection16 con;

byte[] buf = "156457".getBytes();

con = new TarantoolConnection16Impl("x.x.x.x", 3301);

con.insert(513, Arrays.asList("dummykey",buf));

out.println(((ArrayList)(con.select(513, 0, Arrays.asList("dummykey"), 0,100,0).get(0))).get(1).getClass());//expecting byte[] getting String

}catch(Exception e) {

out.println(e.getMessage());

}


-Sairam.


On Friday, March 11, 2016, Konstantin Osipov <> wrote:
* Sairam M P <> [16/03/10 21:07]:

> We were trying to dump our data to tarantool and get the same from
> tarantool. Our data is in byte array format, we were able to insert the
> byte array data to tarantool and the data is appended as binary in the
> tarantool db. However when we try to fetch this data through *select*
> method we are only able to get this data as string and not as byte array.
> Kindly let me know on how to select the binary data from tarantool as
> byte[].

Are you using the Java driver?

Do you have an example program in Java I could comment on?


--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.org - www.twitter.com/kostja_osipov

--
You received this message because you are subscribed to a topic in the Google Groups "Tarantool  discussion group (English)" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/tarantool/ZtL6fI7TZ0M/unsubscribe.
To unsubscribe from this group and all its topics, send an email to tarantool+unsubscribe@googlegroups.com.

Sairam M P

unread,
Mar 11, 2016, 9:35:14 AM3/11/16
to Tarantool discussion group (English)
Hi Dmitry,

Thanks a lot for the response.

We tried the following changes, but we are encountering null pointer exception in schema initializations. 

java.lang.NullPointerException at org.tarantool.TarantoolConnection16Base.schema(TarantoolConnection16Base.java:182)
 
Our code snippet for the below changes :

The main class :

                       TarantoolConnection16 con = new TarantoolConnection16Impl("x.x.x.x", 3301);
                        Field f = TarantoolConnection16Base.class.getDeclaredField("state");
                        f.setAccessible(true);
                        f.set(con, new ConState());
                        final KeyValueSchema schema = new KeyValueSchema();
                        con.schema(schema);  // -----------------Getting null pointer here.---------------------------

The inner class :

        private static class ConState extends ConnectionState {
                @Override
                public void unpack() {
                        ByteBuffer buf = buffer.getBuf();
                        buf.limit(buf.position());
                        buf.rewind();
                        body.clear();
                        header.clear();
                        try {
                                toKeyMap(MsgPackLite.unpack(buffer.asInputStream(), MsgPackLite.OPTION_UNPACK_RAW_AS_STRING), header); // Do we need to change in header also ?  But even changing here to BYTE_BUFFER doesn't works. Getting same null pointer exception.
                                if (buf.remaining() > 0) {
                                        toKeyMap(MsgPackLite.unpack(buffer.asInputStream(), MsgPackLite.OPTION_UNPACK_RAW_AS_BYTE_BUFFER), body); //Changes done.
                                }
                        } catch (IOException e) {
                                // this shouldn't happens
                                throw new IllegalStateException(e);
                        }
                }

                private void toKeyMap(Object unpack, EnumMap<Key, Object> result) {
                Map<Integer, Object> map = (Map<Integer, Object>) unpack;
                for (Map.Entry<Integer, Object> entry : map.entrySet()) {
                    Key key = Key.getById(entry.getKey());
                    if (key != null) {
                        result.put(key, entry.getValue());
                    }
                }
            }

        }

Kindly help us in resolving the above one.

-Sairam.

Sairam M P

unread,
Mar 13, 2016, 3:43:48 PM3/13/16
to Tarantool discussion group (English)
Hi Dmitry/Osipav,

Any idea on the below cause !!?? Am i missing something here ?

-Sairam.

Dmitry Grytsovets

unread,
Mar 13, 2016, 5:21:32 PM3/13/16
to Tarantool discussion group (English)
Hi, you should create field like msgPackOptions in your ConState and set required value for each query you made.
Schema method requires OPTION_UNPACK_RAW_AS_STRING. So you should set OPTION_UNPACK_RAW_AS_BYTE_BUFFER only for queries which requires ByteBuffer or you should set options to zero for queries requires byte[].

пятница, 11 марта 2016 г., 17:35:14 UTC+3 пользователь Sairam M P написал:

Sairam M P

unread,
Mar 24, 2016, 7:53:07 PM3/24/16
to Tarantool discussion group (English)
Hi Dmitry,

Thank you for the response.

Setting to zero for queries that require byte[] works fine.

Happened to see this post related to tarantool java connection pool. An inbuilt connection pool is the best to have for a db driver and you have mentioned it will be provided as part of next release, any ETA for the same !!?

It will really make our tasks easier.

-Sairam.

Sairam M P

unread,
Apr 1, 2016, 7:59:12 AM4/1/16
to Tarantool discussion group (English)
Hi Osipav,

The steps mentioned in the downloads installs the package system wide. But i need to start two or different tarantool instances in different ports in the same server. Like to install the package in the custom path for two different instances. Hope this can be achieved only if we install using the binaries. Please point me to get the binaries equivalent to the latest source / the path to latest source and the steps to compile and generate the binaries.

-Sairam.

Sairam M P

unread,
Apr 1, 2016, 8:03:59 AM4/1/16
to Tarantool discussion group (English)

Forget to mention, we are using CentOS 6 and the server is an 64 bit x86_64 server. Kindly point me to the compatible and latest binaries to use in my production systems.

-Sairam.

Konstantin Osipov

unread,
Apr 1, 2016, 9:03:51 AM4/1/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/04/01 15:05]:
> Forget to mention, we are using CentOS 6 and the server is an 64 bit x86_64
> server. Kindly point me to the compatible and latest binaries to use in my
> production systems.

Did you check tarantoolctl manual?

tarantoolctl works with ~/.tarantool as well as /etc/tarantool.

Sairam M P

unread,
Apr 2, 2016, 1:21:57 PM4/2/16
to Tarantool discussion group (English)
Thank you Osipav.

Currently we are testing tarantool in our production, if it succeeds we might need to bundle it in more than one server in the production. So instead of installing tarantool using yum in each server, its better to maintain a common tarantool binary and use it in all the servers in the production. It will be helpful if you can point us the latest tarantool binary(from http://tarantool.org/dist/1.6/) for CentOS 6 in 64 bit x86_64 server.

-Sairam.

Konstantin Osipov

unread,
Apr 3, 2016, 8:46:40 AM4/3/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/04/03 10:27]:
> Thank you Osipav.
>
> Currently we are testing tarantool in our production, if it succeeds we
> might need to bundle it in more than one server in the production. So
> instead of installing tarantool using yum in each server, its better to
> maintain a common tarantool binary and use it in all the servers in the
> production.

It's exactly what yum is doing for you - installing a binary. How do you
plan to use a binary without installing it? Docker? .tar.gz? NFS
mount?

> It will be helpful if you can point us the latest tarantool
> binary(from http://tarantool.org/dist/1.6/) for CentOS 6 in 64 bit x86_64
> server.

Sairam M P

unread,
Apr 15, 2016, 9:26:32 AM4/15/16
to Tarantool discussion group (English)
Hi Osipav,

From the below thread, i can understand tarantool is single-threaded,


But, the below has been mentioned in the overview part of tarantool home page :

"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."

Does the above means tarantool db is multi-threaded !? Can tarantool db use multiple cpu cores in parallel !?

-Sairam.

Konstantin Osipov

unread,
Apr 15, 2016, 10:01:16 AM4/15/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/04/15 16:48]:
> From the below thread, i can understand tarantool is single-threaded,
>
> https://groups.google.com/forum/#!msg/tarantool/j7zbpC1shto/k7yn7oU0DwAJ
>
> But, the below has been mentioned in the overview part of tarantool home
> page :
>
> "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."
>
> Does the above means tarantool db is multi-threaded !? Can tarantool db use
> multiple cpu cores in parallel !?

a) There are 2 threads running at all times: the network thread and
the transaction processor thread.

b) There is an extra thread for write ahead logging, if write ahead
logging is on.

c) Each time you call box.snapshot(), to save a snapshot, or when
it's called automatically by the snapshot daemon, a dedicated
thread is created for this task. The thread exists when the
snapshot is saved.

d) Each time a replica connects to a master, a dedicated "relay"
thread is created on the master for this replica. There are as
many relays as there are replicas (there can be up to 30 replicas).

e) There is a thread pool to work on tasks of the application server in a
non-blocking fashion. So, each time you use a method from fio
library, a task is passed to the thread pool to handle the call.

The size of the thread pool is dynamic, depending on the amount of
concurrent tasks. The size never grows above the number of CPU
cores.

f) There are threads created temporarily at start up and when a
secondary key is created. These are OpenMP threads for sorting. To
build a secondary index, we use parallel sort to utilize all CPU
cores.

g) Sophia uses an own thread pool to flush sorted tables to disk,
for compaction (branch merges) and disk-bound reads. Usually it's
3-4 threads.

These are about all threads there can be. There is only one
transaction processor thread. The Lua virtual machine is running
in this thread as well. But everything else is done in their own
thread.

Sairam M P

unread,
Apr 18, 2016, 7:02:55 AM4/18/16
to Tarantool discussion group (English)
Great response, thanks again.

The highscalability document http://highscalability.com/blog/2015/12/30/how-to-choose-an-in-memory-nosql-solution-performance-measur.html compares tarantool with different no-sql storage and concludes tarantool has the best performance.

But we have evaluated tarantool for our use case, and found tarantool has lesser throughput(at-least in our tests)  when compared to redis. Our test scenario and set up :

We have two space in tarantool :

Structure :
---------------

box.schema.space.create('SPACE_1')
box.space.SPACE_1:create_index('1_IDX', {type = 'hash', parts = {1, 'STR'}})

box.schema.space.create('SPACE_2')
box.space.SPACE_2:create_index('2_IDX', {type = 'hash', parts = {1, 'STR'}})

In both spaces there are two fields, first one is the key of type STR, the second one is lua-table. Key field is hash indexed.

There are no other space in the tarantool instance.

Values in the space : 
-----------------------------

The key length of SPACE_1 will be 64 chars. The value is a lua-table consisting of only two entries. The field is an constant with 6 chars in the lua-table and the value is an int.

The key length of SPACE_2 can be frm 15 to 25 chars. The value is a lua-table which can consists of any no. of entries from 1 to 300, the field in the lua-table is an integer and the value is an binary string (whose size can vary from 8kb to 20kb (on an average) for different entries in the lua-table. The size can even be higher (hundreds of kbs) for some entries.).

Data Size :
---------------
---
- quota_used: 11945377792
  arena_used_ratio: 37.1%
  items_used_ratio: 81.7%
  arena_size: 11747554648
  quota_size: 32212254720
  arena_used: 9747283312
...

Total 11.125 GB out of allowed 30 GB. 

Read test :
---------------

The below function is used to fetch the data from both the spaces :

box.fetch_proto = function(space1_key, space2_part_key)                                                       
map =  box.space.SPACE_1:select({space1_key})[1][2]
return box.space.SPACE_2:select({space2_part_key..'_'..map.id})[1][2][map.nkey];   //Appended value from first select to form the key.
end

Data from SPACE_1 is used to select data from SPACE_2, which returns the final output (lua-table.)

We have used https://github.com/dork/tarantool-java/blob/master/src/main/java/org/tarantool/pool/SocketChannelPooledConnectionFactory.java for conn. pooling with little modification for compilation and created two pools, one with patch for fetching binary data (as suggested by you in one of the earlier discussion in this thread) and the other without the binary patch.

Concurrency :
---------------------

We have used java's CyclicBarrier class to simulate the real time concurrency load. We have tested for 1, 11, 21,31,...,141 threads, in each thread there will be 20 selects for 20 different (random) keys. It has been noted that with increase in no. of threads the average response time tend to increase proportionally. 

NOTE : There are no other writes/reads in tarantool.

REDIS Test results :
----------------------------

The same data has been dumped to redis, in the same server and tested independently. Unlike tarantool, the increase in no. of threads doesn't affects the average response time. 


Have attached the comparison chart between tarantool & redis. Based on the attached graph, tarantool has 10 times lesser throughput than redis at the max(141) no. of threads. In the graph, T(Green) stands for tarantool and R(red) for redis.

**********************************************************
In the above we have fetched a tuple from tarantool which returns a lua-table with multiple entries and then fetched only a single entry from this lua-table. Is there a way in tarantool to fetch only this entry from tarantool without fetching all the data in its tuple. (Something like hget in redis to get a particular entry from a hash).
***********************************************************

As opposed to the results in the highscalability doc, we have got lesser throughput with tarantool, do we miss something !? It will be highly appreciated if you can guide us on achieving better performance than redis for our use case in tarantool.

Kindly get back in case of any clarification(s).

-Sairam.
Redis_Vs_Tarantool+1_1.pdf

Konstantin Osipov

unread,
Apr 18, 2016, 8:38:37 AM4/18/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/04/18 14:58]:

Hi,

Looking at the graph, your workload seems to be heavily CPU-bound.
I could only guess why without having access to a standalone
benchmark.

The tests on highscalability compare tarantool vs. redis head to
head in ycsb workload.

You seem to be attempting to emulate Redis data model with Lua
stored procedures in Tarantool. Let's see what can be done about it.

>
> *Structure :*
> *---------------*
>
> box.schema.space.create('SPACE_1')
> box.space.SPACE_1:create_index('1_IDX', {type = 'hash', parts = {1, 'STR'}})
>
> box.schema.space.create('SPACE_2')
> box.space.SPACE_2:create_index('2_IDX', {type = 'hash', parts = {1, 'STR'}})
>
> In both spaces there are two fields, first one is the key of type STR, the
> second one is lua-table. Key field is hash indexed.
>
> There are no other space in the tarantool instance.
>
> *Values in the space : *
> *-----------------------------*
>
> The key length of SPACE_1 will be *64 chars*. The value is a lua-table
> consisting of only two entries. The field is an *constant with 6 chars* in
> the *lua-table* and the value is an *int*.

Why do you need this second space? It would be best to put all the
data from it into the first one, and build a secondary index.
By having a secondary key you would be able to slash the number of
operations the database has to do in half.

Actually, looking at your read test below, it makes sense to put
data into the second space, and have two indexes: on field #1, and
on map field (map.nkey). This way you would be able to get the
necessary value as a single select, without a stored procedure.

> *Read test :*
> *---------------*
>
> The below function is used to fetch the data from both the spaces :
>
> box.fetch_proto = function(space1_key, space2_part_key)
>
> map = box.space.SPACE_1:select({space1_key})[1][2]
> return
> box.space.SPACE_2:select({space2_part_key..'_'..map.id})[1][2][map.nkey];
> //Appended value from first select to form the key.
> end

Here are 2 performance issues with this stored procedure:
- it generates Lua garbage. On every call, you push two tuples
into Lua stack and two Lua tables.
- it fetches a single key of a potentially huge map. Maps are
represented in a compact manner in memory, so getting a single
key from a map invokes unpacking the map into a Lua table. The
cost is proportional to the size of the map.

I think the best data modeling approach would be to "normalize"
the map into a single space and build a multi-part index on
space1_key, map.nkey. To fetch the entire map, you could then
use a single-part lookup. To fetch the entire map, you would use
a fully specified key. You would need a unique TREE index for
that.

> *REDIS Test results :*
> *----------------------------*
>
> The *same data has been dumped to redis, in the same server *and tested
> independently. Unlike tarantool, the increase in no. of threads *doesn't
> affects the average response time.*
>
> *Have attached the comparison chart between tarantool & redis. Based on the
> attached graph, tarantool has 10 times lesser throughput than redis at the
> max(141) no. of threads. In the graph, T(Green) stands for tarantool and
> R(red) for redis.*

What is the RPS that you were able to extract from both systems?
What is the actual data model that you used with Redis?

> ************************************************************
> *In the above we have fetched a tuple from tarantool which returns a
> lua-table with multiple entries and then fetched only a single entry from
> this lua-table. Is there a way in tarantool to fetch only this entry from
> tarantool without fetching all the data in its tuple. (Something like hget
> in redis to get a particular entry from a hash).*
> *************************************************************

I hope my explanation provides a clue. You would need to re-model
your data to fit our data structures nicely if you want to get the
best out of the product.

Sairam M P

unread,
Apr 20, 2016, 11:08:11 AM4/20/16
to Tarantool discussion group (English)
Osipav,

>I think the best data modeling approach would be to "normalize" 
>the map into a single space and build a multi-part index on 
>space1_key, map.nkey. To fetch the entire map, you could then 
>use a single-part lookup. To fetch the entire map, you would use 
>a fully specified key. You would need a unique TREE index for 
>that. 

We tried to create a single space and dump the data to it. The structure of the newly created space is :

box.schema.space.create('SPACE_3')
box.space.SPACE_3:create_index('SPACE_3_IDX', {type = 'hash', parts = {1, 'STR'}})

This space will have 5 fields in which the first field will be the pk. This space has only one index(hash) on filed-1 (pk) of type STR. 

Values in the space : 
----------------------------

Field -1 : PK field - Contain alpha-numeric string consisting of 64 chars
Field -2 : Binary string (whose size can vary from 1kb to 10kb (on an average) for different entries in the lua-table. The size can even be higher (hundreds of kbs) for some entries.).
Field -3 : Integer.
Field -4 : String with 7 or 4 chars.
Field -5 : Integer.

All the tuples will have values for all the fields.

Data size :
--------------

unix/:/tarantool_test/test.control> box.space.SPACE_3:len()
---
- 5852278
...

unix/:/tarantool_test/test.control> box.slab.info()
---
- quota_used: 1744830464
  arena_used_ratio: 5.4%
  items_used_ratio: 77.1%
  arena_size: 1524988760
  quota_size: 32212254720
  arena_used: 1197379744
...

For inserting 5852278 tuples it took ~20 hours. ~80 inserts per second. Our insert code works sequentially, but still the insert rate is very slow. Kindly through some lights on this slowness during inserts. Can the 64 chars pk field be the reason for slow inserts !!?? But the previous data-model, which also had this 64 char field as pk and a lua-table in the second field was not this much slower for inserts.

Else can the data in the other fields causing this slowness.

Please clarify.

-Sairam.

Konstantin Osipov

unread,
Apr 21, 2016, 7:49:34 AM4/21/16
to tara...@googlegroups.com
Hi,

What's box.slab.info() output on the inserted data?

What client are you using to insert data?

What hardware?

It would be best if you shared the insert code and tarantool
configuration or provided access to your installation to quickly
identify the cause.

Thanks,

* Sairam M P <smart...@gmail.com> [16/04/21 14:45]:

> >I think the best data modeling approach would be to "normalize"
> >the map into a single space and build a multi-part index on
> >space1_key, map.nkey. To fetch the entire map, you could then
> >use a single-part lookup. To fetch the entire map, you would
> >use a fully specified key. You would need a unique TREE index
> >for that.
>
> We tried to create a single space and dump the data to it. The structure of
> the newly created space is :
>
> box.schema.space.create('SPACE_3')
> box.space.SPACE_3:create_index('SPACE_3_IDX', {type = 'hash', parts = {1,
> 'STR'}})
>
> This space will have 5 fields in which the first field will be the pk. This
> space has only one index(hash) on filed-1 (pk) of type STR.
>
> *Values in the space : *
> *----------------------------*
>
> Field -1 : *PK field* - Contain alpha-numeric string consisting of *64
> chars*.
> Field -2 : *Binary string* (whose size can vary from* 1kb to 10kb *(on an
> average) for different entries in the lua-table. The size can even be
> higher (hundreds of kbs) for some entries.).
> Field -3 : Integer.
> Field -4 : String with 7 or 4 chars.
> Field -5 : Integer.
>
> All the tuples will have values for all the fields.
>
> *Data size :*
> *--------------*
>
> unix/:/tarantool_test/test.control> box.space.SPACE_3:len()
> ---
> - 5852278
> ...
>
> unix/:/tarantool_test/test.control> box.slab.info()
> ---
> - quota_used: 1744830464
> arena_used_ratio: 5.4%
> items_used_ratio: 77.1%
> arena_size: 1524988760
> quota_size: 32212254720
> arena_used: 1197379744
> ...
>
> For inserting *5852278* tuples it took ~*20 hours*. *~80 inserts per
> second. *Our insert code works sequentially, but still the insert rate is
> very slow. Kindly through some lights on this slowness during inserts. Can
> the 64 chars pk field be the reason for slow inserts !!?? But the previous
> data-model, which also had this 64 char field as pk and a lua-table in the
> second field was not this much slower for inserts.
>
> Else can the data in the other fields causing this slowness.




Sairam M P

unread,
Apr 25, 2016, 6:25:07 PM4/25/16
to Tarantool discussion group (English)
Hi Osipov,

Thanks a lot for the response. We have analysed our code and optimized the same to get a better throughput. Also we have tested with a fresh program to generate random keys and insert in the tarantool instance and now we are able to achieve ~40k inserts per second.

We are trying to satisfy our other use cases, it will be helpful if you can guide us on the same :

In the space created above we have 5 fields, field-1 being the pk, our gets will be based on field-1 but we have two cases for deletes.

Case 1 : 
----------

We need to delete more than 1 tuple by specifying the pk. i.e., Is there a syntax to delete the tuple(s) whose field-1 matches 'a' or 'b' or 'c'. field-1 being the pk(hash index.). As far as we tried looks like we can specify only one key for delete. If there is no support for multi-keys delete, is there an alternate for this case ?

Case 2 :
----------

delete from space where field-2 = 'a' and field-3 = 'b'

Delete all the tuples whose field-2 value matches 'a' and field-3 value matches 'b'. Both are STR fields and not part of pk. What are the index we might need to create to and how can we achieve this delete.

Case 3 :
----------

Is there an equivalent for SET (in redis) in tarantool ?. Our space will have two fields, field-1 (STR) the pk, field-2 will contain SET of ids. We might need to do the following with the SET : 

1. Insert new elements in the SET.
2. Fetch all the elements of a SET based on the pk key.

For this case we were using redis SET, its SMEMBERS, SADD. Trying to reproduce the same functionality with lua-table as the value, but is there an option to append new members to the table without fetching the previous values in the lua-table ? Or is there an better option to imitate SET in tarantool.

Fetching both binary & non-binary data from the same tuple :
---------------------------------------------------------------------------------------

You have suggested us to patch the tarantool connection code(above in this thread) to fetch binary data, but our space has both binary (only one field) and non-binary data(other fields). Seems if we use the patched connection we can't retrieve the other fields data from the returned tuples since they are non-binary. Are we missing something !?

Slab allocation :
---------------------

As i am beginner in the slab allocation concepts, kindly clarify on :

1. What will happen if my tarantool instance consumes all of the allocated memory. Say if my tarantool instance is allowed to use 4 GB of memory but my dataset grows beyond that, will tarantool throw error or is there an option to configure automated LRU in case of max memory consumption ?

2. Can i check the size of a particular space(out of many) in my tarantool instance ?


-Sairam.

Konstantin Osipov

unread,
Apr 26, 2016, 12:21:52 AM4/26/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/04/26 06:49]:
>
> *Case 1 : *
> *----------*
>
> We need to delete more than 1 tuple by specifying the pk. i.e., Is there a
> syntax to delete the tuple(s) whose field-1 matches* 'a' or 'b' or 'c'*.
> field-1 being the pk(hash index.). As far as we tried looks like we can
> specify only one key for delete. If there is no support for multi-keys
> delete, is there an alternate for this case ?

A stored procedure with a loop over all keys

local function multi_delete(space, keys)
box.begin()
for k, v in pairs(keys) do
space:delete(v)
end
box.commit()
end

>
> *Case 2 :*
> *----------*
>
> delete from space where field-2 = 'a' and field-3 = 'b'
>
> Delete all the tuples whose field-2 value matches 'a' *and* field-3 value
> matches 'b'. Both are STR fields and not part of pk. What are the index we
> might need to create to and how can we achieve this delete.

You will need to create a secondary key over field-2 and field-3:

box.space.myspace:create_index('secondary', { parts = {2, 'STR', 3, 'STR' }})

If the index is unique, then you can simply delete using it:

box.space.myspace.index.secondary:delete{'a', 'b'}

Otherwise a similar loop is necessary:

local tuple = myspace.index.secondary:get{'a', 'b'}
while tuple ~= nil do
myspace:delete{tuple[1]}
tuple = myspace.index.secondary:get{'a', 'b'}
while tuple ~= nil do
end

> *Case 3 :*
> *----------*
>
> Is there an equivalent for SET (in redis) in tarantool ?. Our space will
> have two fields, field-1 (STR) the pk, field-2 will contain SET of ids. We
> might need to do the following with the SET :
>
> 1. Insert new elements in the SET.
> 2. Fetch all the elements of a SET based on the pk key.
>
> For this case we were using redis SET, its SMEMBERS, SADD. Trying to
> reproduce the same functionality with lua-table as the value, but is there
> an option to append new members to the table without fetching the previous
> values in the lua-table ? Or is there an better option to imitate SET in
> tarantool.

There is no direct equivalent, the implementation strategy depends
on your average set size. If your set is within a few dozen
elements, it's best to store it in Tarantool using a single tuple.

In this case, the primary key = set name, and all set members are
tuple fields. Each time you need to add a member, you load the set tuple,
use tuple:find() to check if the member already exists, and
if it doesn't, use space:update() in which you push the new member
to tuple tail. If you need to delete, you produce an update() with
delete operator.

This achieves very good performance and compact binary log.

If your average set size is over a hundred values, it's best to
store each set member as its own tuple. In this case your primary
key is set_name, set_member.

The third strategy is for huge sets, say over a few hundred
thousand keys each. For such sets, you might be better off coming
up with an own space per set.

>
> *Fetching both binary & non-binary data from the same tuple :*
> *---------------------------------------------------------------------------------------*
>
> You have suggested us to patch the tarantool connection code(above in this
> thread) to fetch binary data, but our space has both binary (only one
> field) and non-binary data(other fields). Seems if we use the patched
> connection we can't retrieve the other fields data from the returned tuples
> since they are non-binary. Are we missing something !?

I can't remember suggesting this, could you paste here the code
you're having trouble with?

> *Slab allocation :*
> *---------------------*
>
> As i am beginner in the slab allocation concepts, kindly clarify on :
>
> 1. What will happen if my tarantool instance consumes all of the allocated
> memory. Say if my tarantool instance is allowed to use 4 GB of memory but
> my dataset grows beyond that, will tarantool throw error or is there an
> option to configure automated LRU in case of max memory consumption ?

We have 'expirationd' Lua rock which you could use to implement
LRU. When tarantool runs out of memory, it stops accepting new
updates until you delete stuff to free memory.

This is, BTW, a popular feature request for us.

> 2. Can i check the size of a particular space(out of many) in my tarantool
> instance ?

Not out of the box. You would have to add :bsize() result for each
space index and :bsize() result for each space tuple. So you could
do it, but we don't maintain this statistics for a whole space.

This is, also, BTW, a popular feature request for us :)

Sairam M P

unread,
Apr 27, 2016, 5:54:43 PM4/27/16
to Tarantool discussion group (English)
Hi,

Case 2 :
-----------

> local tuple = myspace.index.secondary:get{'a', 'b'} 
> while tuple ~= nil do 
>     myspace:delete{tuple[1]} 
>     tuple = myspace.index.secondary:get{'a', 'b'} 
> while tuple ~= nil do 
> end 
  
The above get call fails with :  - error: More than one tuple found by get()

Should be because of : 

Note

box.space.space-name.index.index-name:select(...)[1]`. can be replaced by box.space.space-name.index.index-name:get(...). That is, get can be used as a convenient shorthand to get the first tuple in the tuple set that would be returned by select. However, if there is more than one tuple in the tuple set, then get returns an error.


The above note from the doc.

So we have modified the index:get(...) call to index:select(...)[1] and the code works. The secondary index is a 'tree' index with unique=false. There are two fields in the index (field-3 and field-4 of the space.) Both the field is of type STR. field-3 actually contains int/long in the form of string. field-4 contains a constant with 7 or 4 chars.

Performance :
-------------------

But this function takes 14 secs to iterate and delete 5418 tuples. No other operations in tarantool during this delete. Also the cpu usage spikes to 100% during this delete call. 

Our application might have considerable amount of such delete calls, the time taken and cpu usage shows we might need to tweak to get better performance. Is there any optimization(s) that we have to do, so that the deletes can be faster ? OR is there any other better possible ways to delete the tuples without iterations ?

Clarification :
-----------------

> If the index is unique, then you can simply delete using it: 

> box.space.myspace.index.secondary:delete{'a', 'b'} 

Guess if the above also works with non-unique indices, the delete performance should be better. Curious to know why don't we have that option in tarantool.

Case 3 :
-----------

> In this case, the primary key = set name, and all set members are 
> tuple fields. Each time you need to add a member, you load the set tuple, 
> use tuple:find() to check if the member already exists, and 
> if it doesn't, use space:update() in which you push the new member 
> to tuple tail. If you need to delete, you produce an update() with 
> delete operator. 

Practically on an average the no. of elements in the set can be anything from 1 to 70, but theoretically it can even grow upto 5k to 10k. Will doing an tuple:find() for each addition can be an overhead ? We might have 25 million tuples on an average for a single space in which each tuple will have a SET. For every addition to any of these SETs if we are doing tuple:find(),is that still the recommended approach !? 

Is it possible to model the SET elements as lua-table and add specific elements to the lua-table without re-fetching the whole lua-table and remove particular element(s) from the lula-table ? 

> If your average set size is over a hundred values, it's best to 
> store each set member as its own tuple. In this case your primary 
> key is set_name, set_member. 

As mentioned above, our space can hold 25 million tuples and if we consider on an average each tuple's SET has 20 elements then the total no. of tuples can grow to 25million * 20 = 500 million tuples. Also the pk has to be modified as multi-part. 

> The third strategy is for huge sets, say over a few hundred 
> thousand keys each. For such sets, you might be better off coming 
> up with an own space per set. 

Provided the above stats, own space per set should not scale right ? Also like to know on what can be the max. no. of spaces we can create in an tarantool instance ?


-Sairam.

Konstantin Osipov

unread,
Apr 28, 2016, 2:54:16 AM4/28/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/04/28 00:55]:
> *Case 2 :*
> *-----------*
>
> > local tuple =* myspace.index.secondary:get{'a', 'b'} *
> > while tuple ~= nil do
> > myspace:delete{tuple[1]}
> > tuple = *myspace.index.secondary:get{'a', 'b'} *
> > while tuple ~= nil do
> > end
>
> The above get call fails with : * - error: More than one tuple found by
> get()*
>
> Should be because of :
>
> Note
>
> box.space.*space-name*.index.*index-name*:select(...)[1]`. can be replaced
> by box.space.*space-name*.index.*index-name*:get(...). That is, get can be
> used as a convenient shorthand to get the first tuple in the tuple set that
> would be returned by select.* However, if there is more than one tuple in
> the tuple set, then get returns an error.*
>
> The above note from the doc.
>
> So we have modified the *index:get(...) *call to* index:select(...)[1] *and
> the code works. The secondary index is a* 'tree' index *with *unique=false.
> *There are two fields in the index (field-3 and field-4 of the space.) Both
> the field is of type *STR*. field-3 actually contains int/long in the form
> of string. field-4 contains a constant with 7 or 4 chars.
>
> *Performance :*
> *-------------------*
>
> But this function takes* 14 secs *to iterate and delete *5418 tuples. *No
> other operations in tarantool during this delete. Also the* cpu usage
> spikes to 100% *during this delete call.

if you select{} from a non-unique index, and need just one tuple,
pass limit=1 to select options.

>
> Guess if the above also works with non-unique indices, the delete
> performance should be better. Curious to know why don't we have that option
> in tarantool.

Simply haven't gotten to implementing it.

> Practically on an average the *no. of elements in the set *can be anything
> from *1 to 70,* but theoretically it can even grow upto *5k to 10k.* Will
> doing an* tuple:find()* for each addition can be an overhead ? We might
> have 25 million tuples on an average for a single space in which each tuple
> will have a SET. For every addition to any of these SETs if we are doing
> tuple:find(),is that still the recommended approach !?

It's going to be the fastest approach is the number of fields in
the tuple is under 100. But if it can grow to 10k, then these
large tuples will slow things down for all users, so I wouldn't go
with it.

> Is it possible to model the SET elements as lua-table and add specific
> elements to the lua-table without re-fetching the whole lua-table and
> remove particular element(s) from the lula-table ?

Lua tables are the fastest way to store non-persistent data: 10x
or more faster than Tarantool spaces. But you will quickly run out
of memory - the limit is 1G per instance (2G in 1.7).

> > up with an own space per set.
>
> Provided the above stats, own space per set should not scale right ? Also
> like to know on what can be the max. no. of spaces we can create in an
> tarantool instance ?

64000

Sairam M P

unread,
May 3, 2016, 8:34:33 AM5/3/16
to Tarantool discussion group (English)
Hi,

We are trying to create some space(s) in tarantool whose names will be based on some pattern, so it can exceed the default size allowed for space name. Is there an option where we can increase this limit for the size of the space name ?

-Sairam.

Sairam M P

unread,
May 3, 2016, 12:03:06 PM5/3/16
to Tarantool discussion group (English)

In box.schema there is an property like the below :

NAME_MAX:32 

Tried increasing this property but still the space name is not allowed if its greater than 32 chars.

-Sairam.
P.S: Osipov any chat medium in which we can interact directly & instantly.?

Konstantin Osipov

unread,
May 3, 2016, 2:02:39 PM5/3/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/05/03 15:38]:
> We are trying to create some space(s) in tarantool whose names will be
> based on some pattern, so it can exceed the default size allowed for space
> name. Is there an option where we can increase this limit for the size of
> the space name ?

It's a compile time limit. Right now it's 32 bytes. We plan to
make all names dynamic in 1.8. What limit is good enough for you
in 1.6?

Konstantin Osipov

unread,
May 3, 2016, 2:03:28 PM5/3/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/05/03 21:02]:

> -Sairam.
> *P.S: *Osipov any chat medium in which we can interact directly &
> instantly.?

Sure, http://telegram.me/tarantool

Sairam M P

unread,
May 3, 2016, 2:18:25 PM5/3/16
to tara...@googlegroups.com
In general we feel anything from 64 to 128 bytes should be better.

Yes we are in 1.6.

-Sairam.
--
You received this message because you are subscribed to a topic in the Google Groups "Tarantool  discussion group (English)" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/tarantool/ZtL6fI7TZ0M/unsubscribe.
To unsubscribe from this group and all its topics, send an email to tarantool+...@googlegroups.com.

Sairam M P

unread,
May 6, 2016, 7:45:34 PM5/6/16
to Tarantool discussion group (English)
Hi,

In one of our use case we have created multiple spaces(265 as of now) in tarantool and each space has two fields. Field-1 of type STR and field-2 of type NUM. Both the fields participates in the multip-part primary index. The primary index is an unique TREE index. In most of the cases both the fields will contain an integer data. Down the line there are chances that the total no. of such spaces might grow.

Our app. is designed in such a way that each request might need to upsert a tuple in multiple spaces. The no. of spaces that the tuple is updated/inserted can be anything from 1 to 20. To achieve better write performance we have used the below lua script, which accepts the list of spaces and the values to be inserted. The lua function iterates the list of spaces and upserts the value.

function insertData(spaces, rid, rkey)
for k,v in pairs(spaces) do
box.space[v]:upsert({rid, rkey}, {{'=',1,rid},{'=',2,rkey}})
end
end

In redis we have worked out the above use case using pipeline(http://redis.io/topics/pipelining) concept. When we compared the throughput of both redis and tarantool, the average response time of tarantool is 4 times higher than redis for around 100 to 150 threads. It is also evident that with increase in no. of threads the response time in tarantool tends to increase proportionally. 

Kindly suggest us on better ways to achieve the above use case with better throughput. Also is there an equivalent in tarantool like pipeline in redis.

Is the above lua script transacted by default ? Else do i need to use box.being() and box.commit() to transact the 'for' loop in the above lua function.?

Some other Clarifications :

1. Is there an option / java-api to create space(s) in tarantool dynamically .? From the java-doc of 1.6.4, we see there are no api's to create space(s) dynamically. Also the java-api accepts only id to identify a space, can we use the space-name instead of its id somehow ?

2. How can we fetch the definition of the function that is defined in tarantool.

3. The rps for all types of commads(obtained from box.stat()) is 0 in our tarantool instance, while there is some value for DELETE, INSERT, CALL, etc., Do we need to enable some config for capturing the rps ?

4. Redis has a command MONITOR to output its current execution flow, kindly let us know if we have something similar in tarantool.

5. Is there an command to know the number of connected clients, iops, hits, misses, etc., Like redis has 'info' command.

Thanks in advance.

-Sairam.

Sairam M P

unread,
May 6, 2016, 8:13:42 PM5/6/16
to Tarantool discussion group (English)

One more clarification regarding max. clients : 

What is the maximum no. of clients that can connect to tarantool server at a given point of time ? Is there an configuration for this ?

-Sairam.

Konstantin Osipov

unread,
May 8, 2016, 5:23:43 PM5/8/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/05/07 10:16]:
> *function insertData(spaces, rid, rkey)*
> *for k,v in pairs(spaces) do*
> *box.space[v]:upsert({rid, rkey}, {{'=',1,rid},{'=',2,rkey}})*
> *end*
> *end*
>
> In redis we have worked out the above use case using
> pipeline(http://redis.io/topics/pipelining) concept. When we compared the
> throughput of both redis and tarantool, the *average response time of* *tarantool
> is 4 times higher than redis* for around *100 to 150 threads.* It is also
> evident that with increase in no. of threads the response time in tarantool
> tends to increase proportionally.
>
> *Kindly suggest us on better ways to achieve the above use case with better
> throughput. Also is there an equivalent in tarantool like pipeline in
> redis.*

Use transactions.

The code should look like this:

function insertData(spaces, rid, rkey)
box.begin()
for k,v in pairs(spaces) do
box.space[v]:upsert({rid, rkey}, {{'=',1,rid},{'=',2,rkey}})
end
box.commit()
end

> Is the above lua script transacted by default ? Else do i need to use
> box.being() and box.commit() to transact the *'for' loop* in the above lua
> function.?

Each statement runs in autocommit mode by default.

> 1. Is there an option / java-api to create space(s) in tarantool
> dynamically .? From the java-doc of 1.6.4, we see there are no api's to
> create space(s) dynamically. Also the java-api accepts only id to identify
> a space, can we use the space-name instead of its id somehow ?

You can invoke any Lua code from Java driver, by using eval(), so
anything you do in the script, you can do from Java, provided the
user has sufficient privileges.

> 2. How can we fetch the definition of the function that is defined in
> tarantool.

You could use debug.getinfo() and "source", you
could use string.dump(), or, if you function is defined in a file,
you could simply load that file from disk and send it wherever you
need, with Tarantool app server functions.

> 3. The rps for all types of commads(obtained from box.stat()) is 0 in our
> tarantool instance, while there is some value for DELETE, INSERT, CALL,
> etc., Do we need to enable some config for capturing the rps ?

No.

> 4. Redis has a command MONITOR to output its current execution flow, kindly
> let us know if we have something similar in tarantool.

No.

> 5. Is there an command to know *the number of connected clients, iops,
> hits, misses, etc., *Like redis has *'info'* command.

No.

Try box.stat.net(), but it mostly gives you the idea of the
network thread <-> tx thread message bus performance.

Konstantin Osipov

unread,
May 8, 2016, 5:24:27 PM5/8/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/05/07 10:16]:
>
> *One more clarification regarding max. clients : *
>
> What is the maximum no. of clients that can connect to tarantool server at
> a given point of time ? Is there an configuration for this ?

The only limit is the number of open files set by your system, you
can modify it with 'ulimit'.

Sairam M P

unread,
May 12, 2016, 6:00:06 AM5/12/16
to Tarantool discussion group (English)
Thank you Osipov for the clarifications.

> You can invoke any Lua code from Java driver, by using eval(), so 
> anything you do in the script, you can do from Java, provided the 
> user has sufficient privileges. 

But what about the java-api for insert, update, upsert, delete using space-name, is there one ? i.e., Can i use the space-name for these operations instead of space-id. Because we might create space(s) dynamically from our app, and the app knows only the space-name. 

>> 4. Redis has a command MONITOR to output its current execution flow, kindly 
>> let us know if we have something similar in tarantool. 

> No. 

Any other option to debug the server status. If in case the tarantool server performs slow / hangs, any command to debug the status of the tarantool server ? Checked for the doc of debug package in tarantool.org but unable to find one.

>> 5. Is there an command to know *the number of connected clients, iops, 
>> hits, misses, etc., *Like redis has *'info'* command. 

> No. 

No other way to know the clients connected, hits, misses at a particular point of time ? This will really help us in knowing the load that we are giving to tarantool server and the performance that we get out of it.

Select all from a space : 

During our testing with tarantool server, by mistake we selected all the tuples without specifying match / limit. As our tarantool server / space had > 10GB of data, the server hanged and we are unable to connect even one more client. It took around 5 mins to get the server back to normal. We understand the command that we executed is wrong but it can happen if the app. developer overlooks. So we like to know if there is an option to avoid such mishaps. (For example : in redis we can rename a command through which we can avoid destructive operations happening in the system.)


-Sairam.

Konstantin Osipov

unread,
May 12, 2016, 7:33:19 AM5/12/16
to tara...@googlegroups.com
* Sairam M P <smart...@gmail.com> [16/05/12 13:04]:

> > You can invoke any Lua code from Java driver, by using eval(), so
> > anything you do in the script, you can do from Java, provided the
> > user has sufficient privileges.
>
> But what about the java-api for insert, update, upsert, delete using
> space-name, is there one ? i.e., Can i use the space-name for these
> operations instead of space-id. Because we might create space(s)
> dynamically from our app, and the app knows only the space-name.

Yes. If you send a Java fragment which doesn't do what you expect,
I'll be happy to provide a more detailed answer.

> >> 4. Redis has a command MONITOR to output its current execution flow,
> kindly
> >> let us know if we have something similar in tarantool.
>
> > No.
>
> Any other option to debug the server status. If in case the tarantool
> server performs slow / hangs, any command to debug the status of the
> tarantool server ? Checked for the doc of debug package in tarantool.org
> but unable to find one.

Tarantool has infinitely more options to shoot yourself in the
foot than Redis, and it's hard to provide a general recipe without
sufficient background. It would help if you describe the
specific problem you have in mind.

> >> 5. Is there an command to know *the number of connected clients, iops,
> >> hits, misses, etc., *Like redis has *'info'* command.
>
> > No.

> No other way to know the clients connected, hits, misses at a particular
> point of time ? This will really help us in knowing the load that we are
> giving to tarantool server and the performance that we get out of it.

Use
netstat -ant | grep :3303
to find out the number of active connections on port 3303 and
their state.

box.stat() gives you the request per second breakdown.

I didn't understand what you meant by "misses".

> *Select all from a space : *
>
> During our testing with tarantool server, by mistake we selected all the
> tuples without specifying match / limit. As our tarantool server / space
> had > 10GB of data, the server hanged and we are unable to connect even one
> more client. It took around 5 mins to get the server back to normal. We
> understand the command that we executed is wrong but it can happen if the
> app. developer overlooks. So we like to know if there is an *option* to
> avoid such mishaps. (*For example *: in redis we can rename a command
> through which we can avoid destructive operations happening in the system.)

Yes, this request sounds reasonable, reminds me of

http://dev.mysql.com/doc/refman/5.7/en/mysql-command-options.html#option_mysql_safe-updates

We don't have anything like that yet, sorry.

Sairam M P

unread,
Nov 10, 2016, 7:46:04 AM11/10/16
to Tarantool discussion group (English)
Hi Dmitry,

The below solution for inserting and selecting byte[] data used to work with TarantoolConnection16Impl, but when i change to TarantoolAsyncConnection16Impl it throws the below exception :

[17:55:03:807][10-11-2016][SYSERR][INFO][120]: java.nio.BufferUnderflowException
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at java.nio.Buffer.nextGetIndex(Buffer.java:492)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:135)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.ByteBufferStreams$2.read(ByteBufferStreams.java:33)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:201)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpackList(MsgPackLite.java:282)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:264)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpackList(MsgPackLite.java:282)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:248)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpackMap(MsgPackLite.java:294)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:266)
[17:55:03:807][10-11-2016][SYSERR][INFO][120]:  at com.pool.TarantoolPool$PatchedConnectionState.unpack(TarantoolPool.java:190)
[17:55:03:808][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.TarantoolAsyncConnection16Impl.read(TarantoolAsyncConnection16Impl.java:72)
[17:55:03:808][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.TarantoolAsyncConnection16Impl.read(TarantoolAsyncConnection16Impl.java:70)
[17:55:03:808][10-11-2016][SYSERR][INFO][120]:  at org.tarantool.TarantoolSelectorWorker.run(TarantoolSelectorWorker.java:79)
[17:55:03:808][10-11-2016][SYSERR][INFO][120]:  at java.lang.Thread.run(Thread.java:745)
[17:55:03:808][10-11-2016][com.pool.MyWorker][SEVERE][120]: Error in TarantoolSelectorWorker.
[17:55:03:809][10-11-2016][SYSERR][INFO][98]: java.util.concurrent.ExecutionException: java.nio.BufferUnderflowException
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.AsyncQuery.get(AsyncQuery.java:64)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at com.pool.TarantoolConnection.select(TarantoolConnection.java:281)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.jsp.login.test_jsp._jspService(test_jsp.java:94)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.jasper.runtime.HttpJspBase.service(HttpJspBase.java:70)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.jasper.servlet.JspServletWrapper.service(JspServletWrapper.java:439)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.jasper.servlet.JspServlet.serviceJspFile(JspServlet.java:395)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.jasper.servlet.JspServlet.service(JspServlet.java:339)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at javax.servlet.http.HttpServlet.service(HttpServlet.java:731)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:809][10-11-2016][SYSERR][INFO][98]:   at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at com.MyFilter.doFilter(GACFilter.java:66)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at com.OldFilter.doFilter(InstrumentFilter.java:60)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at com.ATFilter.doFilter(AllocationTracker.java:87)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at com.UDFilter.doFilter(UserDetailsFilter.java:62)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at com.AFilter.doFilter(AuditFilter.java:43)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at com.ALFilter.doFilter(AccessLogFilter.java:43)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:241)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:208)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:220)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:122)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:505)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:169)
[17:55:03:810][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:103)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.authenticator.SingleSignOn.invoke(SingleSignOn.java:270)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:116)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:436)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1078)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:625)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:316)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at java.lang.Thread.run(Thread.java:745)
[17:55:03:811][10-11-2016][SYSERR][INFO][98]: Caused by: java.nio.BufferUnderflowException
[17:55:03:811][10-11-2016][SYSERR][INFO][98]:   at java.nio.Buffer.nextGetIndex(Buffer.java:492)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at java.nio.HeapByteBuffer.get(HeapByteBuffer.java:135)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.ByteBufferStreams$2.read(ByteBufferStreams.java:33)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at java.io.FilterInputStream.read(FilterInputStream.java:83)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:201)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpackList(MsgPackLite.java:282)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:264)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpackList(MsgPackLite.java:282)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:248)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpackMap(MsgPackLite.java:294)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.MsgPackLite.unpack(MsgPackLite.java:266)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at com.pool.TarantoolPool$PatchedConnectionState.unpack(TarantoolPool.java:190)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.TarantoolAsyncConnection16Impl.read(TarantoolAsyncConnection16Impl.java:72)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.TarantoolAsyncConnection16Impl.read(TarantoolAsyncConnection16Impl.java:70)
[17:55:03:812][10-11-2016][SYSERR][INFO][98]:   at org.tarantool.TarantoolSelectorWorker.run(TarantoolSelectorWorker.java:79)

Kindly guide us on inserting and selecting byte[] data using TarantoolAsyncConnection16Impl.

-Sairam.

On Monday, March 14, 2016 at 2:51:32 AM UTC+5:30, Dmitry Grytsovets wrote:
Hi, you should create field like msgPackOptions in your ConState and set required value for each query you made.
Schema method requires OPTION_UNPACK_RAW_AS_STRING. So you should set OPTION_UNPACK_RAW_AS_BYTE_BUFFER only for queries which requires ByteBuffer or you should set options to zero for queries requires byte[].

пятница, 11 марта 2016 г., 17:35:14 UTC+3 пользователь Sairam M P написал:
Hi Dmitry,

Thanks a lot for the response.

We tried the following changes, but we are encountering null pointer exception in schema initializations. 

java.lang.NullPointerException at org.tarantool.TarantoolConnection16Base.schema(TarantoolConnection16Base.java:182)
 
Our code snippet for the below changes :

The main class :

                       TarantoolConnection16 con = new TarantoolConnection16Impl("x.x.x.x", 3301);
                        Field f = TarantoolConnection16Base.class.getDeclaredField("state");
                        f.setAccessible(true);
                        f.set(con, new ConState());
                        final KeyValueSchema schema = new KeyValueSchema();
                        con.schema(schema);  // -----------------Getting null pointer here.---------------------------

The inner class :

        private static class ConState extends ConnectionState {
                @Override
                public void unpack() {
                        ByteBuffer buf = buffer.getBuf();
                        buf.limit(buf.position());
                        buf.rewind();
                        body.clear();
                        header.clear();
                        try {
                                toKeyMap(MsgPackLite.unpack(buffer.asInputStream(), MsgPackLite.OPTION_UNPACK_RAW_AS_STRING), header); // Do we need to change in header also ?  But even changing here to BYTE_BUFFER doesn't works. Getting same null pointer exception.
                                if (buf.remaining() > 0) {
                                        toKeyMap(MsgPackLite.unpack(buffer.asInputStream(), MsgPackLite.OPTION_UNPACK_RAW_AS_BYTE_BUFFER), body); //Changes done.
                                }
                        } catch (IOException e) {
                                // this shouldn't happens
                                throw new IllegalStateException(e);
                        }
                }

                private void toKeyMap(Object unpack, EnumMap<Key, Object> result) {
                Map<Integer, Object> map = (Map<Integer, Object>) unpack;
                for (Map.Entry<Integer, Object> entry : map.entrySet()) {
                    Key key = Key.getById(entry.getKey());
                    if (key != null) {
                        result.put(key, entry.getValue());
                    }
                }
            }

        }

Kindly help us in resolving the above one.

-Sairam.

On Friday, March 11, 2016 at 4:12:35 PM UTC+5:30, Dmitry Grytsovets wrote:

This happens due to MsgPackLite.OPTION_UNPACK_RAW_AS_STRING. 
I will give ability to set this options in the next driver release. 

At this moment I could advice workaround:

Override ConnectionState upack method to

public void unpack() {
...
 toKeyMap(MsgPackLite.unpack(buffer.asInputStream(), 0), body);
...
}

then change TarantoolConnection16Impl state to overriden one immediatly after construction.

четверг, 10 марта 2016 г., 22:17:11 UTC+3 пользователь Sairam M P написал:
Yes we are using Java driver.

Here is the snippet : 

try {

TarantoolConnection16 con;

byte[] buf = "156457".getBytes();

con = new TarantoolConnection16Impl("x.x.x.x", 3301);

con.insert(513, Arrays.asList("dummykey",buf));

out.println(((ArrayList)(con.select(513, 0, Arrays.asList("dummykey"), 0,100,0).get(0))).get(1).getClass());//expecting byte[] getting String

}catch(Exception e) {

out.println(e.getMessage());

}


-Sairam.


On Friday, March 11, 2016, Konstantin Osipov <> wrote:
* Sairam M P <> [16/03/10 21:07]:
> We were trying to dump our data to tarantool and get the same from
> tarantool. Our data is in byte array format, we were able to insert the
> byte array data to tarantool and the data is appended as binary in the
> tarantool db. However when we try to fetch this data through *select*
> method we are only able to get this data as string and not as byte array.
> Kindly let me know on how to select the binary data from tarantool as
> byte[].

Are you using the Java driver?

Do you have an example program in Java I could comment on?



--
Konstantin Osipov, Moscow, Russia, +7 903 626 22 32
http://tarantool.org - www.twitter.com/kostja_osipov

--
You received this message because you are subscribed to a topic in the Google Groups "Tarantool  discussion group (English)" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/tarantool/ZtL6fI7TZ0M/unsubscribe.
To unsubscribe from this group and all its topics, send an email to tarantool+unsubscribe@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages