diff --git a/README.md b/README.md index 24d0061..7d6c739 100644 --- a/README.md +++ b/README.md @@ -2382,345 +2382,6 @@ futriix:~> acl grant employees admin rwda

(К началу)

-## Lua-плагины - -Для расширения функциональных возможностей субд **без изменения её исходного кода**, в futriix была реализована система расширения функциональности через Lua-скрипты с изолированным окружением. Плагины имеют доступ к БД, транзакциям, триггерам, могут логировать события и взаимодействовать через событийную шину, а также они доступны в веб-интерфейсе. -А кроме того плагины могут использоваться для написания движков для субд, без изменения её исходного кода, на языке lua. - -```sh -# Просмотр информации о системе плагинов -futriix:~> plugin status -=== Plugin System Status === - Enabled: true - Plugins Directory: ./plugins - Loaded Plugins: 3 - Total Executions: 125 - -# Список загруженных плагинов -futriix:~> plugin list -=== Loaded Plugins === - validation (v1.0.0) by admin - Document validation rules - Status: RUNNING - audit (v2.1.0) by security - Audit trail logger - Status: RUNNING - notify (v1.2.0) by devops - Email and webhook notifications - Status: RUNNING - -# Загрузка плагина из файла -futriix:~> plugin load email_notifier ./plugins/email_notifier.lua -✓ Plugin 'email_notifier' loaded successfully - Version: 1.0.0 - Author: admin - Description: Send email notifications on database events - -# Запуск/остановка плагина -futriix:~> plugin start email_notifier -✓ Plugin 'email_notifier' started - -futriix:~> plugin stop email_notifier -✓ Plugin 'email_notifier' stopped - -# Выгрузка плагина -futriix:~> plugin unload email_notifier -✓ Plugin 'email_notifier' unloaded -``` - -**Пример плагина валидации документов** - -В директорию `plugins` добавляем файл `validation.lua`, следдующего содержания: - -```sh --- Метаданные плагина -version = "1.0.0" -author = "admin" -description = "Document validation rules for employees collection" - --- Функция инициализации -function on_load() - plugin_log("info", "Validation plugin loaded") - return true -end - --- Функция запуска -function on_start() - plugin_log("info", "Validation plugin started") - return true -end - --- Функция остановки -function on_stop() - plugin_log("info", "Validation plugin stopped") - return true -end - --- Функция выгрузки -function on_unload() - plugin_log("info", "Validation plugin unloaded") - return true -end - --- Обработчик событий -function on_event(event) - plugin_log("debug", "Received event: " .. event.type) - - if event.type == "BEFORE_INSERT" then - return validate_document(event.data) - end - - return true -end - --- Функция валидации документа -function validate_document(doc) - -- Проверка обязательных полей - if doc.name == nil or doc.name == "" then - plugin_log("error", "Document missing required field: name") - return false, "Field 'name' is required" - end - - -- Проверка возраста - if doc.age ~= nil then - if doc.age < 18 then - plugin_log("warn", "Age validation failed: " .. doc.age) - return false, "Employee must be at least 18 years old" - end - if doc.age > 65 then - plugin_log("warn", "Age validation failed: " .. doc.age) - return false, "Employee cannot be older than 65 years" - end - end - - -- Проверка email - if doc.email ~= nil then - if string.match(doc.email, "^[%w._-]+@[%w._-]+%.[%w]+$") == nil then - plugin_log("error", "Invalid email format: " .. doc.email) - return false, "Invalid email format" - end - end - - -- Проверка зарплаты - if doc.salary ~= nil then - if doc.salary < 30000 then - plugin_log("warn", "Salary below minimum: " .. doc.salary) - return false, "Salary must be at least 30000" - end - end - - plugin_log("info", "Document validation passed for: " .. doc.name) - return true -end - --- Пользовательская функция для массовой валидации -function validate_collection(collection_name) - local coll = get_collection("company", collection_name) - if coll == nil then - plugin_log("error", "Collection not found: " .. collection_name) - return 0 - end - - -- Здесь можно реализовать массовую валидацию - plugin_log("info", "Validating collection: " .. collection_name) - return 0 -end -``` - -**Использование плагина валидации документов** - -```sh -# Создание базы данных и коллекции -futriix:~> create database company -✓ Database 'company' created - -futriix:~> use company -✓ Switched to database 'company' - -futriix:~> create collection employees -✓ Collection 'employees' created in database 'company' - -# Загрузка и запуск плагина валидации -futriix:~> plugin load validation ./plugins/validation.lua -✓ Plugin 'validation' loaded successfully - Version: 1.0.0 - Author: admin - Description: Document validation rules for employees collection - -futriix:~> plugin start validation -✓ Plugin 'validation' started - -# Вставка валидного документа -futriix:~> insert employees name=John Doe,age=25,email=john@company.com,salary=45000 -✓ Document inserted with ID: emp_001 - -# Вставка невалидного документа (возраст < 18) -futriix:~> insert employees name=Jane Smith,age=16,email=jane@company.com,salary=20000 -Error: Employee must be at least 18 years old - -# Вставка невалидного документа (некорректный email) -futriix:~> insert employees name=Bob Johnson,age=30,email=invalid-email,salary=50000 -Error: Invalid email format - -# Выполнение пользовательской функции плагина -futriix:~> plugin call validation validate_collection employees -✓ Function returned: 0 -``` - -**Управление плагинами через HTTP API** - -```sh -# Получение списка плагинов через API -curl -X GET "http://localhost:8080/api/plugin/list" \ - -H "X-Session-ID: abc123" - -# Загрузка плагина через API -curl -X POST http://localhost:8080/api/plugin/load \ - -H "Content-Type: application/json" \ - -H "X-Session-ID: abc123" \ - -d '{"name":"validation","path":"./plugins/validation.lua"}' - -# Запуск плагина через API -curl -X POST "http://localhost:8080/api/plugin/start/validation" \ - -H "X-Session-ID: abc123" - -# Выполнение функции плагина через API -curl -X POST http://localhost:8080/api/plugin/call \ - -H "Content-Type: application/json" \ - -H "X-Session-ID: abc123" \ - -d '{"plugin":"validation","function":"validate_collection","args":["employees"]}' -``` - -**Плагины как инструмент написания движков для futriix** - -Для написания нового движка (LSM-дерева, key-value или time-series) необходимо два файла: файл с названием самого движка с расширением **.lua** -и файл **Манифест-плагина** с расширением **.json** - -**Манифест плагина-движка** — это JSON-файл, который сопровождает Lua-скрипт плагина и предоставляет системе метаданные о плагине. Манифест необходим для корректной загрузки, регистрации и управления плагинами, реализующими кастомные движки хранения данных. -Расположение и именование - -Манифест должен располагаться в той же директории, что и Lua-скрипт плагина (по умолчанию `/futriix/plugins`), и иметь идентичное имя файла с расширением **.json**. Например: - -```sh -plugins/ -├── timescale_engine.lua # Lua-скрипт движка -└── timescale_engine.json # Манифест движка -``` - -**Структура манифеста** - -```sh -{ - "name": "timescale_engine", - "version": "1.0.0", - "author": "Example Corp", - "description": "Time-series storage engine with automatic partitioning", - "api_version": "1.0", - "engine_type": "timescale", - "min_go_version": "1.21", - "dependencies": [ - { - "name": "base_engine", - "version": "2.0.0", - "min_version": "2.0.0", - "max_version": "3.0.0", - "optional": false - } - ], - "entry_point": "create_engine", - "created_at": 1704067200000, - "updated_at": 1704153600000 -} - -``` - -**Поля манифеста** - -| Поле | Тип | Обязательное | Описание | -|------|-----|--------------|----------| -| `name` | string | ✅ | Уникальное имя плагина. Должно совпадать с именем Lua-файла (без расширения) | -| `version` | string | ✅ | Версия плагина в формате Semantic Versioning (X.Y.Z) | -| `author` | string | ❌ | Автор или организация-разработчик плагина | -| `description` | string | ❌ | Краткое описание функциональности плагина | -| `api_version` | string | ✅ | Версия API СУБД, с которой совместим плагин | -| `engine_type` | string | ✅ | **Ключевое поле**. Определяет, что плагин является движком хранения. Значение используется как идентификатор движка при создании коллекций | -| `min_go_version` | string | ❌ | Минимальная версия Go, необходимая для работы плагина | -| `dependencies` | array | ❌ | Список зависимостей от других плагинов | -| `entry_point` | string | ❌ | Имя Lua-функции-фабрики, создающей экземпляр движка. По умолчанию: `create_engine` | -| `created_at` | int64 | ❌ | Время создания манифеста (Unix timestamp в миллисекундах) | -| `updated_at` | int64 | ❌ | Время последнего обновления манифеста | - - - -**Структура зависимости** - -Каждая зависимость в массиве `dependencies` имеет следующую структуру: - - -| Поле | Тип | Обязательное | Описание | -|------|-----|--------------|----------| -| `name` | string | ✅ | Имя зависимого плагина | -| `version` | string | ❌ | Точная версия зависимого плагина (если указана, требует точного соответствия) | -| `min_version` | string | ❌ | Минимальная допустимая версия (включительно) | -| `max_version` | string | ❌ | Максимальная допустимая версия (исключительно) | -| `optional` | boolean | ❌ | Является ли зависимость опциональной. По умолчанию: `false` | - - - -**Процесс загрузки плагина с манифестом** - -1. Обнаружение: Система сканирует директорию плагинов и находит Lua-файлы -2. Чтение манифеста: При наличии JSON-файла с тем же именем система загружает и парсит его. - - Валидация: - - * Проверяется наличие обязательных полей (name, version, api_version) - * Проверяется, что engine_type не пуст (для движков) - * Проверяется совместимость версий зависимостей - -3. Регистрация движка: Если engine_type указан, система автоматически регистрирует плагин в EngineRegistry под этим именем -4. Загрузка Lua-скрипта: Выполняется Lua-скрипт, который должен экспортировать фабричную функцию (по умолчанию create_engine) -5. Вызов фабрики: При создании экземпляра движка для конкретной коллекции вызывается фабричная функция с передачей конфигурации - -**Пример использования** - -**Создание манифеста для Time-Series движка:** - -```sh -{ - "name": "ts_engine", - "version": "1.2.0", - "author": "Futriix Team", - "description": "High-performance time-series storage engine with automatic downsampling", - "api_version": "1.0", - "engine_type": "timeseries", - "dependencies": [ - { - "name": "compression_plugin", - "min_version": "1.0.0", - "optional": false - } - ], - "entry_point": "new_timeseries_engine", - "created_at": 1704067200000, - "updated_at": 1704153600000 -} -``` - -**Рекомендации по версионированию** - -* Используйте Semantic Versioning (MAJOR.MINOR.PATCH) -* Увеличивайте MAJOR-версию при несовместимых изменениях API движка -* Увеличивайте MINOR-версию при добавлении новой функциональности -* Увеличивайте PATCH-версию при исправлении ошибок - - -**Обработка ошибок** - -При отсутствии манифеста система пытается загрузить плагин в "упрощённом режиме", используя значения по умолчанию. Однако для плагинов-движков манифест является обязательным, так как поле engine_type необходимо для корректной регистрации. - -Ошибки при разборе манифеста логируются, но не препятствуют загрузке Lua-скрипта (если это не плагин-движок). При критических ошибках (отсутствие engine_type у движка) загрузка прерывается с соответствующим сообщением. - - -

(К началу)

- ## Триггеры