Tarantool 3.1
Release date: April 16, 2024
Releases on GitHub: v. 3.1.2, v. 3.1.1, v. 3.1.0
The 3.1 release of Tarantool continues the development of a new cluster configuration approach introduced in the 3.0 version and adds the following main product features and improvements for the Community and Enterprise editions:
- Community Edition (CE)
- Improved developer experience for handling errors using the
box.error
module. - Introduced fixed-size numeric field types:
uint8
,int8
,uint16
, and more. - Added RPC functionality for accessing custom roles from the configuration.
- Made the
tt
utility used to manage instances fully compatible with the latest Tarantool version.
- Improved developer experience for handling errors using the
- Enterprise Edition (EE)
- Introduced an external coordinator for automatic and manual failover.
- Improved the stability of work with the centralized configuration stored in etcd.
This release improves the developer experience for handling errors using the box.error module. Below are listed the most notable features and changes.
With the 3.1 release, you can add a custom payload to an error.
The payload is passed as key-value pairs where a key is a string and a value is any Lua object.
In the example below, the description
key is used to keep the custom payload.
custom_error = box.error.new({ type = 'CustomInternalError',
message = 'Internal server error',
description = 'Some error details' -- payload
})
A payload field value can be accessed using the dot syntax:
tarantool> custom_error.description
---
- Some error details
...
The 3.1 release simplifies creating error chains. In the earlier versions, you need to set an error cause using the set_prev(error_object) method, for example:
local ok, err = pcall(my_func)
if not ok then
local err2 = box.error.new{type = "MyAppError", message = "my_func failed"}
err2:set_prev(err)
err2:raise()
end
Using this approach, you need to construct a new error without raising it, then set its cause using set_prev()
, and only then raise it.
Starting with the 3.1 version, you can use a new prev
argument when constructing an error:
local ok, err = pcall(my_func)
if not ok then
box.error{type = "MyAppError", message = "my_func failed", prev = err}
end
The 3.1 release allows you to increase the verbosity of error serialization. Before the 3.1 release, a serialized error representation included only an error message:
tarantool> box.error.new({ type = 'CustomInternalError', message = 'Internal server error'})
---
- Internal server error
...
Starting with the 3.1 version, a serialized error also includes other fields that might be useful for analyzing errors:
tarantool> box.error.new({ type = 'CustomInternalError', message = 'Internal server error'})
---
- code: 0
base_type: CustomError
type: CustomInternalError
custom_type: CustomInternalError
message: Internal server error
trace:
- file: '[C]'
line: 4294967295
...
Logging an error using a built-in logging module prints an error message followed by a tab space (\t
) and all the payload fields serialized as a JSON map, for example:
main/104/app.lua/tarantool I> Internal server error {"code":0,"base_type":"CustomError","type":"CustomInternalError", ... }
Given that this change may change the behavior of existing code, a new box_error_serialize_verbose
compat option is introduced.
To try out an increased verbosity of error serialization, set this option to new
:
tarantool> require('compat').box_error_serialize_verbose = 'new'
---
...
The 3.1 release introduces fixed-size numeric types that might be useful to store data unencoded in an array for effective scanning. The following numeric types are added:
uint8
: an integer in a range[0 .. 255]
.int8
: an integer in a range[-128 .. 127]
.uint16
: an integer in a range[0 .. 65,535]
.int16
: an integer in a range[-32,768 .. 32,767]
.uint32
: an integer in a range[0 .. 4,294,967,295]
.int32
: an integer in a range[-2,147,483,648 .. 2,147,483,647]
.uint64
: an integer in a range[0 .. 18,446,744,073,709,551,615]
.int64
: an integer in a range[-9,223,372,036,854,775,808 .. 9,223,372,036,854,775,807]
.float32
: a 32-bit floating point number.float64
: a 64-bit floating point number.
A new experimental.connpool
module provides a set of features for remote connections to any cluster instance or executing remote procedure calls on an instance that meets the specified criteria.
To load the experimental.connpool
module, use the require()
directive:
sharded_cluster:router-a-001> connpool = require('experimental.connpool')
---
...
In the 3.1 version, this module provides the following API:
The
connect()
function accepts an instance name and returns the active connection to this instance:sharded_cluster:router-a-001> conn = connpool.connect("storage-b-002") --- ...
Once you have a connection, you can execute requests on a remote instance, for example, select data from a space:
sharded_cluster:router-a-001> conn.space.bands:select({}, { limit = 5 }) --- - - [3, 804, 'Ace of Base', 1987] - [7, 693, 'The Doors', 1965] - [9, 644, 'Led Zeppelin', 1968] - [10, 569, 'Queen', 1970] ...
The
filter()
function returns the names of instances that match the specified conditions. In the example below, this function returns a list of instances with thestorage
role and specified label value:sharded_cluster:router-a-001> connpool.filter({ roles = { 'storage' }, labels = { dc = 'east' }}) --- - - storage-b-002 - storage-a-002 ...
The
call()
function can be used to execute a function on a remote instance. In the example below, the following conditions are specified to choose an instance to execute thevshard.storage.buckets_count
function on:- An instance has the
storage
role. - An instance has the
dc
label set towest
. - An instance is writable.
sharded_cluster:router-a-001> connpool.call('vshard.storage.buckets_count', nil, { roles = { 'storage' }, labels = { dc = 'west' }, mode = 'rw' }) sharded_cluster:router-a-001> connpool.call('vshard.storage.buckets_count', nil, { roles = { 'storage' }, labels = { dc = 'west' }, mode = 'rw' }) --- - 500 ...
- An instance has the
Learn more in the experimental.connpool
module reference.
In Tarantool 3.0, the config module provides the ability to work with a current instance’s configuration only. Starting with the 3.1 version, you can get all the instances that constitute a cluster and obtain the configuration of any instance of this cluster.
The config:instances()
function lists all instances of the cluster:
sharded_cluster:router-a-001> require('config'):instances()
---
- storage-a-001:
group_name: storages
instance_name: storage-a-001
replicaset_name: storage-a
storage-b-002:
group_name: storages
instance_name: storage-b-002
replicaset_name: storage-b
router-a-001:
group_name: routers
instance_name: router-a-001
replicaset_name: router-a
storage-a-002:
group_name: storages
instance_name: storage-a-002
replicaset_name: storage-a
storage-b-001:
group_name: storages
instance_name: storage-b-001
replicaset_name: storage-b
...
To get the specified configuration value for a certain instance, pass an instance name as an argument to config:get()
:
sharded_cluster:router-a-001> require('config'):get('iproto', {instance = 'storage-b-001'})
---
- readahead: 16320
net_msg_max: 768
listen:
- uri: 127.0.0.1:3304
threads: 1
advertise:
peer:
login: replicator
client: null
sharding:
login: storage
...
Tarantool Enterprise Edition 3.1 introduces an external failover coordinator that monitors a Tarantool cluster and performs automatic leadership change if a current replica set leader is inaccessible.
A failover coordinator requires the replication.failover configuration option to be set to supervised
:
replication:
failover: supervised
# ...
To start a failover coordinator, execute the tarantool
command with the failover
option and pass a path to a YAML configuration file:
$ tarantool --failover --config /path/to/config
A failover coordinator connects to all the instances, polls them for their status, and controls that each replica set with replication.failover
set to supervised
has only one writable instance.
Optionally, you can configure failover timeouts and other parameters in the failover
section at the global level:
failover:
call_timeout: 1
lease_interval: 15
renew_interval: 5
stateboard:
renew_interval: 1
keepalive_interval: 5
The 3.1 release includes new sharding options that provide additional flexibility for configuring a sharded cluster.
A new sharding.weight
specifies the relative amount of data that a replica set can store.
In the example below, the storage-a
replica set can store twice as much data as storage-b
:
# ...
replicasets:
storage-a:
sharding:
weight: 2
# ...
storage-b:
sharding:
weight: 1
# ...
The sharding.rebalancer_mode
option configures whether a rebalancer is selected manually or automatically.
This option can have one of three values:
auto
(default): if there are no replica sets with therebalancer
sharding role (sharding.roles), a replica set with the rebalancer will be selected automatically among all replica sets.manual
: one of the replica sets should have therebalancer
sharding role. The rebalancer will be in this replica set.off
: rebalancing is turned off regardless of whether a replica set with therebalancer
sharding role exists or not.
With this release, the tarantoolctl
utility used to administer Tarantool instances is completely removed from Tarantool packages.
The latest version of the tt utility is fully compatible with Tarantool 3.1 and covers all the required functionality:
- Setting up a development environment: initializing the environment and installing different Tarantool versions.
- Various capabilities for developing cluster applications: creating applications from templates, managing modules, and building and packaging applications.
- Managing cluster instances: starting and stopping instances, connecting to remote instances for administration, and so on.
- Importing and exporting data (Enterprise Edition only).
Learn how to migrate from tarantoolctl
to tt
in the Migration from tarantoolctl to tt section.