<style type="text/css">
* { tab-size: 4; }
/* Resets & overrides */
.reveal dl { display: block; }
.reveal pre { width: auto; box-shadow: none; }
.reveal pre code { width: auto; max-height: initial; }
.reveal blockquote { width: auto; }
.reveal .slides .pdf-page { pointer-events: initial; }
.reveal .slides { text-align: left; }
.reveal section img { display:block;box-shadow: none; }
/* Font style defs */
html, .reveal {
font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
.reveal pre code {
font-family: "PTM55F", "Monaco", monospace;
}
/* Color defs */
.reveal pre code {
background: #2d2d2d;
}
/* Backgrounds */
.reveal .slide-background-content {
background-color: #222;
}
.reveal .first .slide-background-content {
background-color: #222;
}
.reveal section {
background-color: #333;
background-size: cover;
background-image: url(https://slides.hb.bizmrg.com/tarantool/dark/page.png);
}
.reveal .slides section h1,
.reveal .slides section h2,
.reveal .slides section h3,
.reveal .slides section h4,
.reveal .slides section h5,
.reveal .slides section h6{
background: rgba(0,0,0,0.25);
}
.reveal section img { background: rgba(255, 255, 255, 0.3); }
/* Font-sizes */
html {
font-size: 32px;
}
.reveal {
font-size: 1rem;
}
.reveal .slides section h1,
.reveal .slides section h2,
.reveal .slides section h3,
.reveal .slides section h4,
.reveal .slides section h5,
.reveal .slides section h6{
font-size: 40px;
}
.reveal pre {
font-size: 24px;
line-height: 1.11;
}
.reveal pre code {
font-size: inherit;
}
.reveal blockquote {
font-size: 28px;
}
/* Paddings */
.reveal .slides section {
width: 960px;
height: 540px;
}
.reveal .slides > section,
.reveal .slides > section > section {
padding: 20px;
}
.reveal .slides > section > section {
margin: 0 -20px;
}
.reveal .slides section h1,
.reveal .slides section h2,
.reveal .slides section h3,
.reveal .slides section h4,
.reveal .slides section h5,
.reveal .slides section h6{
margin: -5px -20px 15px -20px;
padding: 5px 20px;
}
.reveal dl { margin: 0; }
.reveal dd { margin-bottom: 0.25em; }
.reveal pre {
margin: 10px -20px 20px -20px;
padding: 0;
}
.reveal pre code {
padding: 10px 20px;
}
.reveal blockquote {
margin: 20px -20px 20px -20px;
padding: 0;
}
.reveal blockquote > p {
margin: 0 0 0 40px;
padding: 0 0 0 5px;
}
.reveal section img {
margin: 15px auto;
padding: 5px;
}
/*** Helpers ***/
.reveal .slides section.center.present {
display: grid !important;
align-items: center;
}
.lr {
display:grid;
grid-gap: 0 50px;
grid-template-columns: 1fr 1fr;
}
section.only-code pre {
position: absolute;
top: 10px;
bottom: 10px;
left: 0;
right: 0;
margin: 0;
}
section.only-code pre code {
height: 100%;
}
/*** Modding ***/
.reveal .slides section .fragment.current-only {
opacity: 1;
visibility: visible;
display: none;
}
.reveal .slides section .fragment.current-only.current-fragment {
display: block;
}
.reveal .slides section li.fragment.current-only.current-fragment {
display: list-item;
}
.reveal .slides section span.fragment.current-only.current-fragment {
display: inline;
}
.reveal .slides section .fragment.visible-blurred {
opacity: 1;
visibility: visible;
filter: blur(2px);
}
.reveal .slides section .fragment.visible-blurred.visible {
filter: none;
}
.reveal .slides section .fragment.visible-blurred.visible.current-fragment {
filter: none;
text-shadow: 2px 2px #676767;
}
.reveal section img {
border-width: 2px;
}
/*** Stylings ***/
.reveal section img.shape {
border: none;
background: none;
padding: 0;
margin: 0;
}
dd { font-style: italic; }
dd * { font-style: initial; }
.reveal pre code { overflow: hidden; }
.reveal blockquote > p { border-left: solid #333 10px; }
.reveal .slides section { overflow: hidden; }
section.first *[data-title] {
position: absolute;
left : 0;
bottom: 0px;
}
.reveal .slides section.first h1 {
margin-top: 25px;
background: none;
}
.reveal .slides section.first h2 {
background: none;
}
section.first .author {
position: absolute;
left : 20px;
bottom: 20px;
}
</style>
# Создание приложений Tarantool
---
## Голос из Zoom-а
- Миши Филоненко
- Программиста со стажем
- Инфокоучера `Tarantool`-а
---
## Lua
- Базовый синтаксис
- Типы
- Итераторы
- Изоляция
- Garbage Collector
- Файберы и кооперативная многозадачность
- Встроенные модули
- Транзакции
- Триггеры
---
## Что такое Lua?
- Легковстраиваемый язык
- Динамическая типизация
- Автоматическое управление памятью
- ООП на базе прототипов
- Перегрузка операторов
- Функции как типы данных (лямбды, замыкания)
- Возможен `JIT`
----
### Объявления переменных
- Все переменные *глобальные* по умолчанию
- *Локальные* объявляются словом `local`
- В рамках файла
- В рамках синтаксических блоков
- Обращение к необъявленной переменной не приводит к исключительной ситуации
----
### Присваивание
```lua=
a = 4
b = "string"
a, b = 4, 8 -- множественное присваивание
a, b = b, a -- swap
a, b, c, d = 1, 2, 3, 4, 5
a, b, c, d = 1, 2, 3 -- недостающие значения будут nil
```
----
### Условия
<div class="lr">
<div>
- `if ... then`
```lua=
if a == 0 then
print("a is zero")
end
```
<div class="fragment">
- `else`
```lua=
if a == 0 then
print("a is zero")
else
print("a is not zero")
end
```
</div>
</div>
<div class="fragment">
- `elseif`
```lua=
if a == 0 then
print("zero")
elseif a == 1 then
print("one")
elseif a == 2 then
print("two")
else
print("other")
end
```
</div>
</div>
----
### Циклы
<div class="lr">
<div>
<div class="fragment current-only">
- Со счетчиком
```lua
for i = 1, 10 do
print(i)
end
```
</div>
<div class="fragment">
- Со счетчиком
```lua
for i = 1, 10, 2 do
print(i)
end
```
</div>
<div class="fragment">
- С итератором
```lua
for key, val in pairs(....) do
print(key, val)
end
```
</div>
</div>
<div>
<div class="fragment">
- С предусловием
```lua
while i > 0 do
i = i - 1
end
```
</div>
<div class="fragment">
- С постусловием
```lua
repeat
i = i + 1
until i >= 5
```
</div>
</div>
</div>
----
### Управление циклами
- Прерывание цикла с помощью `break` или `return`
- `break` или `return` могут быть только в конце блока, то есть перед `end`
----
## Типизация в Lua
- Динамическая
- Тип переменной зависит от значения
----
### Типы
- `nil` — отсутствие значения<!--.element class="fragment" -->
- `boolean` — булевый. `true` или `false`.<!--.element class="fragment" -->
- `number` — числовой <!--.element class="fragment" -->
- `string` — строковый<!--.element class="fragment" -->
- `table` — таблица. массив или ключ-значение <!--.element class="fragment" -->
- `function` — функция<!--.element class="fragment" -->
- `userdata` — данные из `C` (`void *`)<!--.element class="fragment" -->
- `cdata` — FFI-тип из языка `C` в LuaJIT ffi.<!--.element class="fragment" -->
---
## Итераторы
- `pairs`
- `ipairs`
----
- По таблице
```lua=
for key, value in pairs(t) do
body
end
```
- По таблице, которая массив
```lua=
for i, value in ipairs(t) do
body
end
```
----
### Семантика итератора
<pre><code class="hljs">for <span class="fragment highlight-current-red" data-fragment-index=5>var_1</span>, ···, var_n in <span class="fragment highlight-current-red" data-fragment-index=1>expr</span> do
<span class="fragment highlight-current-red" data-fragment-index=6>block</span>
end
</code></pre>
<pre><code class="hljs">do
local <span class="fragment highlight-current-red" data-fragment-index=2>func</span>, <span class="fragment highlight-current-red" data-fragment-index=3>state</span>, <span class="fragment highlight-current-red" data-fragment-index=4>var</span> = <span class="fragment highlight-current-red" data-fragment-index=1>expr</span>
while true do
local <span class="fragment highlight-current-red" data-fragment-index=5>var_1</span>, ···, var_n = <span class="fragment highlight-current-red" data-fragment-index=2>func</span>(<span class="fragment highlight-current-red" data-fragment-index=3>state</span>, <span class="fragment highlight-current-red" data-fragment-index=4>var</span>)
if <span class="fragment highlight-current-red" data-fragment-index=5>var_1</span> == nil then break end
<span class="fragment highlight-current-red" data-fragment-index=4>var</span> = var_1
<span class="fragment highlight-current-red" data-fragment-index=6>block</span>
end
end
</code></pre>
Note:
Анимация процесса, важно!
---
## Изоляция (sandbox) - применение
- Окружение можно задать вручную
- Можно запретить всё
- стандартные функции
- `error`, `require`, `print`
- и так далее
- системные вызовы
- работа с файлами, сетью
- Останутся только операторы ветвления и циклов
- `while true` остается возможным
----
### `setfenv`
- Устанавливает окружение для функции
- Например, не дать ничего функции `usercode`
```lua=
usercode = function() print('Hello') end
setfenv(usercode, {}) -- пустое окружение
usercode()
-- error: attempt to call global 'print'
```
----
### `setfenv` и динамическая загрузка кода
- Сделать динамически загружаемый код
- И дать `print`
```lua=
usercode = loadstring([[
print('Hello')
]])
setfenv(usercode, {print=print})
usercode()
-- 'Hello'
```
---
## Сборщик мусора
- Точкой входа живых объектов является `_G`
- `_G` вечный
- Объекты, которые никому не принадлежат в иерархии удаляются
----
### Алгоритм Mark&Sweep
- *Mark*: проход по графу объектов начиная с `_G` и установка флага о живости объекта
- *Sweep*: проход по длинному списку всех объектов и удаление тех, у кого нет флага живости
- Между *Mark* и *Sweep* сборщик временно воскрешает объекты, которые должны быть финализированы
- Проблема: полный *stop the world*
----
### Incremental Mark&Sweep
- Инкрементальный алгоритм решает проблему остановки мира
- Проходит по объектам за несколько итераций, которые останавливают выполнение на небольшое время
----
### Управление GC
- `collectgarbage('collect')`
Один полный цикл очистки неиспользуемых объектов
- `collectgarbage('count')`
Количество используемой памяти (в килобайтах)
- `collectgarbage('stop')`
Остановка сборщика
- `collectgarbage('restart')`
Запуск сборщика
---
## Модули
- Модуль это файл с кодом расположенный в директории
- Модуль может быть написан на `Lua`
- Модуль может быть написан на `C`
----
### Как работает загрузчик модулей
Загрузчик ищет модули по путям из переменных
- `package.path` для `Lua` модулей
- `package.cpath` для `C` модулей
Часто для разработки достаточно указать одну директорию с корнем проекта
```lua
package.setsearchroot(path)
```
----
### Нюансы загрузчика модулей Tarantool
По-умолчанию `Tarantool` проводит поиск модулей
- $HOME/.luarocks/share/lua/5.1/?.lua
- $HOME/.luarocks/share/lua/?.lua
- /usr/local/share/tarantool/?.lua
- /usr/share/tarantool/?.lua
- /usr/local/share/lua/5.1/?.lua
- /usr/share/lua/5.1/?.lua
- $CWD/.rocks/share/tarantool/?.lua
Note:
* package.loaded и package.preload
----
### Реестр загруженных модулей
- Модули загружаются с помощью `require`
- Модули запоминаются в переменной `package.loaded`
```lua=
tarantool> require('app.roles.queue')
---
- put: 'function 0x11'
...
tarantool> package.loaded['app.roles.queue']
---
- put: 'function 0x11'
...
```
- Повторный `require` возвращает загруженный модуль
----
### Перехват и подмена модуля
```lua=
tarantool> package.preload['app.roles.queue'] =
> function()
> print('Перехват модуля')
> return { module = 'test' }
> end
---
...
tarantool> require( 'app.roles.queue' )
Перехват модуля
---
- module: test
...
```
----
### Горячая перезагрузка кода
Для перезагрузки модуля необходимо:
- Удалить его из реестра `package.loaded`
- Загрузить вновь с помощью `require`
```lua=
tarantool> require('app.roles.queue')
---
- put: 'function 0x11...'
...
tarantool> package.loaded['app.roles.queue'] = nil
tarantool> require('app.roles.queue')
---
- put: 'function 0x22...'
...
```
----
### Lua модули
> Встроенные модули
<div class="lr">
* clock
* http
* uri
* csv
* fun
* buffer
* digest
* crypto
<!-- -->
* socket
* fio
* json
* yaml
* msgpack
* iconv
* utf8
* uuid
</div>
---
## Библиотеки (luarocks)
- Для установки пакетов служит утилита `tarantoolctl`
- Подключен `luarocks` сервер `Tarantool`
```lua
tarantoolctl rocks install PACKET_NAME VERSION
```
- Специальные версии:
```
scm-1 - master
scm-n - branch based version
```
----
### Библиотеки
- Установка фреймворка `cartridge` 2.3.0
```lua
tarantoolctl rocks install cartridge 2.3.0
```
----
### Список полезных пакетов
<div class="lr">
<div>
- `cartridge`
- `luacheck`
- `luatest`
- `metrics`
- `http` (server)
- `graphql`
- `errors`
- `tracing`
- `icu-date`
- `lrexlib`
</div>
<div>
moonlibs:
- `package-reload`
- `xqueue`
- `indexpiration`
- `id`
- `obj`
- `emitter`
- `ffi-reloadable`
- `...`
</div>
</div>
---
## Файберы и кооперативная многозадачность
- Весь `Lua` код выполняется в одном железном потоке — TX треде
- Код распределён на файберы (сопрограммы, корутины, зеленые треды)
- В момент времени — только один файбер
- В случае `io` операции или явно — управление другому файберу
----
<div class="lr">
<div>
- file reader
<div class="fragment" data-fragment-index="1">
```lua=
f = fio.open('data.csv')
--[[
передача управления
и ожидание результата
]]
row = f:read()
```
</div>
<div class="fragment" data-fragment-index="3">
```lua=7
print(row)
f:close()
```
</div>
</div>
<div>
- http client
<div class="fragment" data-fragment-index="2">
```lua=
s = socket.connect('google.com')
content = s:read()
```
</div>
<div class="fragment" data-fragment-index="4">
```lua=3
print(content)
s:close()
```
</div>
</div>
</div>
---
## Транзакции
- `box.begin()`
- `box.commit()`/`box.rollback()`
- Нельзя илдить посередине (в бета версии `Tarantool` можно)
- `ddl` нетранзакционный((
- `box.atomic()` — сахар над парой `begin`-`commit/rollback`
----
### Транзакции — пример
```lua=
function atomic(func)
box.begin()
local status = pcall(func)
if not status then
box.rollback()
return false
end
box.commit()
return true
end
```
---
## Триггеры
- Это часть аппсервера
- Живут в скриптах луа, а не в базе
- Не реплицируются
- Могут модифицировать таплы
- Могут быть установлены на системные спейсы
- Требуют приседаний для установки
----
### Пример триггера
```lua=
logger = function(old, new, space, operation)
print(old, new, space, operation)
end
```
----
#### Установка триггера
<pre><code><span class="fragment">
box.ctl.on_schema_init(function()
<span class="fragment"> box.space._space:on_replace(function(_, sp)
<span class="fragment"> if sp.name == my_space then
box.on_commit(function()
<span class="fragment"> box.space[my_space]:before_replace(logger)</span>
end)</span>
end
end)</span>
end)</span>
</code></pre>
Note:
```
box.ctl.on_schema_init(function()
box.space._space:on_replace(function(_, sp)
if sp.name == '<Space Name>' then
box.on_commit(function()
box.space[my_space]:on_replace(logger)
end)
end
end)
end)
```
---
## tarantoolctl
- Установка зависимостей `luarocks`
- Управление узлом `Tarantool`
- Запуск/останов
- Подключение к консоли
- Ротация логов
- Просмотр файлов хранилища `snap`, `xlog`
---
## В заключение
- Tarantool — платформа для построения кластерных `in-memory` бекенд приложений
---
## Полезные ссылки
- https://learnxinyminutes.com/docs/ru-ru/lua-ru/
---
<!-- .slide: class="center" -->
### Заполните, пожалуйста, опрос о занятии по ссылке в чате
- Обязательно вставьте какой-нибудь мемчик <!-- .element: class="fragment" -->
- Раскрасте будни датасаентиста <!-- .element: class="fragment" -->
---
<!-- .slide: class="center" -->
### Спасибо за проявленное внимание
{"metaMigratedAt":"2023-06-15T17:22:59.874Z","metaMigratedFrom":"YAML","title":"OTUS — Приложение на Tarantool","breaks":true,"slideOptions":"{\"theme\":\"dark\",\"transition\":\"none\",\"backgroundTransition\":\"none\",\"width\":960,\"height\":540,\"margin\":0,\"allottedMinutes\":50}","contributors":"[{\"id\":\"b8f98924-71ad-44a5-89d6-3de16a5bd8ab\",\"add\":18525,\"del\":2242}]"}