Application threads
Application threads allow application code to use additional threads for offloading work from the main thread.
Like the main thread, each application thread runs an independent event loop and a Lua interpreter and has most Tarantool built-in modules available.
To enable application threads, add the top-level section threads with
the sub-section groups to the configuration file. For example, to
create two application thread groups, http with one thread and app
with four threads, use the following configuration:
threads:groups:- name: httpsize: 1- name: appsize: 4
The thread group tx with one thread is always created implicitly.
It can be used for calling the main thread from other threads, as
described below.
For interacting with application threads, the built-in threads Lua module was introduced. It offers the following functions:
threads.call()calls a function on a thread group.threads.export()exports a local Lua function so that it can be called withthreads.call()from other threads.threads.eval()executes a Lua expression on a thread group.threads.info()returns information about configured thread groups and the current thread.
The threads module is available in all threads. It can be used in the main thread
even before Tarantool is configured, but only with the tx thread group as a target
apparently, because application thread groups are created during configuration.
Like the main thread,
application threads can expose functions through
IPROTO, by calling box.iproto.export. If a function is exposed in one
or more threads, IPROTO will forward CALL requests for this function to
one of the threads where the function is exposed. To call a function in
an application thread, the user must have the lua_call privilege for
this function or be the admin.
Below is an example of exposing a function through IPROTO in application threads.
iproto:listen:- uri: localhost:3301credentials:users:client:password: secretprivileges:- permissions: [execute]lua_call: [test_func]threads:groups:- name: appsize: 4app:module: test_maingroups:group-001:replicasets:replicaset-001:instances:instance-001: {}
-- test_main.lualocal threads = require('threads')threads.eval('app', [[require('test_thread')]])
-- test_thread.lualocal threads = require('threads')box.iproto.export('test_func', function()local info = threads.info()return string.format('Hello from thread %s/%d',info.group_name, info.thread_id)end)
-- test_client.lualocal net = require('net.box')local conn = net.connect(3301, {user = 'client', password = 'secret'})for i = 1, 10 doprint(conn:call('test_func'))endos.exit()
To run the example, execute the following command:
tarantool -c config.yaml -n instance-001
And run the client in another terminal:
tarantool test_client.lua
It will print something like this:
Hello from thread app/4Hello from thread app/2Hello from thread app/1Hello from thread app/2Hello from thread app/4Hello from thread app/4Hello from thread app/4Hello from thread app/3Hello from thread app/2Hello from thread app/2
A read view opened in the main thread can be shared with an application
thread started via box.cfg{app_threads = ...}. To do this, pass the read
view's id to the application thread and open it there by id:
box.cfg{app_threads = 1}box.schema.space.create('test')box.space.test:create_index('primary')box.space.test:replace({1, 'x'})local rv = box.read_view.open({name = 'shared_read_view'})local threads = require('threads')threads.eval('app', [[local rv = box.read_view.open({id = ...})return rv.space.test:select()]], {rv.id})
A new read view can still be created only in the main thread: calling
box.read_view.open() without id in an application thread raises an
error. Similarly, box.read_view.list() can be called only in the main
thread.
If a read view in use by an application thread is closed in the main
thread, it isn't deleted immediately — it switches to the close_pending
status. A read view in this status can't be used in the main thread or
reopened in another thread, but threads that already opened it can keep
using it until they stop. Once the last user stops using it, the read
view is deleted globally — either explicitly or via Lua garbage
collection.