<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}]"}
    365 views