-
Notifications
You must be signed in to change notification settings - Fork 0
Plugin API
In Tarantool, in addition to Lua stored procedurs, it's possible to write C plugins. These plugins have full access to server core and add the features not present in the s tock version. For example, they make it possible to implement a custom client/server protocol or turn Tarantool into HTTP server, or connect to a database.
The first two existing plugins are connectors to PostgreSQL and MySQL.
They make it possible to run queries against these databases from within stored procedures. For example, this is how a procedure pulling data from MySQL to a Tarantool space may look like:
local dbh = box.net.sql.connect('mysql', 'host', port, user, password, database)
local cards = dbh:select('SELECT * FROM cards WHERE time > ?', box.time())
for i, card in pairs(cards) do
box.replace(space, card.id, card.name, card.title)
end
Let's consider the steps necessary to write your own plugin using an example. Imagine Tarantool is used as a statistics store, and it's necessary to publish various counters in a monitoring application once in a while. We must also assume that the protocol to work with this system is an external library (since otherwise it would be simpler to just open a box.socket instance and push a packet into it). Perhaps it's a proprietary system and the protocol is closed.
To sum up, imagine we have
- an external library,
libmonitor - a library header file,
monitor.h
To use the library in a C++ program, one typically writes a code like this:
#include <monitor.h>
struct monitor *mon = mon_connect("host", "auth");
mon_push(mon, value);
mon_close(mon);Both methods, mon_connect and mon_push are "blocking", i.e. block the
current thread while they execute, so can't be used directly in Lua, e.g.
via FFI. Let's see how the library can be wrapped into a plugin, and
its methods used in a non-blocking manner. For simplicity, exceptions and error handling
is omitted.
git clone --recursive git@github.com:mailru/tarantool.git add_subdirectory(monitor)(файл src/plugins/monitor/CMakeLists.txt):
add_library(monitor SHARED monitor.cc)
install(TARGETS monitor LIBRARY DESTINATION ${PLUGIN_DIR})Первая директива говорит что весь наш плагин будет упакован в 'so'-файл
с именем libmonitor.so и создан на базе файла monitor.cc (который мы
создадим ниже). Вторая директива говорит о том что make install будет устанавливать эту
библиотеку в тот же каталог куда устанавливаются все плагины.
Каждый плагин для тарантула может использовать произвольные инструменты
внутри тарантула (см. заголовочные файлы из директории include, а так
же C/C++-файлы из директории src), для корректной работы плагин должен
минимально задекларировать следующие вещи:
- Имя плагина
- Версия плагина (целое число)
- Инициализационная функция
- Возможно несколько дополнительных параметров.
для декларации этих параметров используется макрос DECLARE_PLUGIN.
Итак, напишем наш плагин:
extern "C" {
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
}
#include <plugin.h>
#include <monitor.h>
#include <coeio.h>
#include <tarantool_ev.h>
static ssize_t
my_mon_connect(va_list ap)
{
struct monitor **mon = va_arg(ap, typeof(mon));
const char *host = va_arg(ap, typeof(host));
const char *auth = va_arg(ap, typeof(auth));
*mon = mon_connect(host, auth);
return 0;
}
static ssize_t
my_mon_push(va_list ap)
{
struct monitor **mon = va_arg(ap, typeof(mon));
int value = va_arg(ap, typeof(value));
mon_push(mon, value);
return 0;
}
static int
lua_mon_send(struct lua_State *L)
{
struct monitor *mon = *(struct monitor **)lua_touserdata(L, 1);
int value = lua_tointeger(L, 2);
coeio_custom(my_mon_push, TIMEOUT_INFINITY, mon, value);
return 0;
}
static int
lua_mon_close(struct lua_State *L)
{
struct monitor *mon = *(struct monitor **)lua_touserdata(L, 1);
mon_close(mon);
}
static int
lua_mon_connect(struct lua_State *L)
{
const char *host = lua_tostring(L, 1);
const char *auth = lua_tostring(L, 2);
struct monitor *mon = NULL;
coeio_custom(my_mon_connect, TIMEOUT_INFINITY, &mon, host, auth);
struct monitor **ptr = (struct monitor **)lua_newuserdata(sizeof(mon));
ptr[0] = mon;
lua_newtable(L);
/* "деструктор" */
lua_pushstring(L, "__gc");
lua_pushcfunction(L, lua_mon_close);
lua_rawset(L, -3);
/* метод отправки данных */
lua_pushstring(L, "__call");
lua_pushcfunction(L, lua_mon_send);
lua_rawset(L, -3);
/* метатаблица для установленного соединения */
lua_setmetatable(L, -2);
return 1;
}
static void
init (struct lua_State *L)
{
lua_pushcfunction(L, lua_mon_connect);
lua_setfield(L, LUA_GLOBALSINDEX, "mon_connect");
}
DECLARE_PLUGIN("monitor", 1, init, NULL);
Итак, что мы видим в данном плагине:
- Заголовочные файлы
lua*.hпозволяют оперировать контекстом Lua из нашего плагина (подробнее см. документацию на lua и luajit) -
plugin.hопределяем макросDECLARE_PLUGIN -
monitor.hописан выше -
coeio.h- интерфейс доступа к блокирующим операциям из событийной -
tarantool_ev.h- константаTIMEOUT_INFINITYмашины тарантула.
Работа плагина начинается с функции init. Эта функция вызывается в момент
инициализации плагина. Ей передается lua-контекст тарантула. В этой функции
мы регистрируем глобальную функцию mon_connect, которую в дальнейшем
можно использовать в наших хранимых процедурах:
local mon = mon_connect(host, auth) -- приконнектиться к монитору
mon(123) -- отправить число 123 в монитор
mon(234) -- отправить число 234 в мониторПри вызове функции mon_connect в lua, будет неявно вызвана функция
lua_mon_connect. Которая при помощи coeio_custom сделает блокирующий
вызов mon_connect и результаты преобразует в lua-объект с двумя метаметодами:
-
__gc- деструктор, вызываетmon_close -
__call- будет вызыватьmon_push
cmake .
makeВ каталоге src/plugins/monitor будет собран плагин libmonitor.so.
- Тарантул загружает плагины из директории, определяемой стандартным префиксом
cmake(обычно/usr{/local}/lib/tarantool), а так же по путям, определенным в переменной окруженияTARANTOOL_PLUGIN_DIR - По факту загрузки каждого плагина в логе тарантула имеется соответствующая запись.
- Мониторить загруженные плагины можно используя команду
show pluginsкомандного интерфейса.
Architecture Specifications
- Server architecture
- Feature specifications
- What's in a good specification
- Functional indexes
- Space _index structure
- R tree index quick start and usage
- LuaJIT
- Vinyl
- SQL
- Testing
- Performance
How To ...?
- ... add new fuzzers
- ... build RPM or Deb package using packpack
- ... calculate memory size
- ... debug core dump of stripped tarantool
- ... debug core from different OS
- ... debug Lua state with GDB
- ... generate new bootstrap snapshot
- ... use Address Sanitizer
- ... collect a coredump
Lua modules
Useful links