Futriix-встроенная распределённая субд в сервер-приложений (falcot) с поддержкой модулей на языке lua, написанная на языке Rust
(К началу)
## **Почему именно HTAP? Анализ характеристик falcot:** OLTP (Online Transactional Processing- Онлайн оработка транзакций) черты, присущие falcot: 1. **Поддержка транзакций - есть BEGIN/COMMIT/ROLLBACK** 2. **CRUD операции - полноценные Create, Read, Update, Delete** 3. **Wait-free архитектура - оптимизирована для быстрых коротких операций** 4. **Поддержка индексов - ускоряет поиск и точки доступа** 5. **ACID-подобное поведение - на уровне отдельных операций**(К началу)
## Лицензия Проект распространяется под 3-пунктной лицензией BSD. Подробнсти смотрите в файле `LICENSE.txt`. Эта лицензия является одной из самых демократичных лицензий свободного программного обеспечения. Она позволяет использовать, изменять и распространять код в коммерческих целях без каких-либо ограничений, за исключением сохранения уведомления об авторских правах. В том числе, вы можете использовать falcot и futriix в своих коммерческих продуктах, приложениях или сервисах, не беспокоясь о каких-либо юридических ограничениях, связанных с лицензией. Все дополнительное программное обеспечение (включая модули на языке lua, утилиту тестирования) предоставляются "как есть", без гарантий и обязательств со стороны разработчиков. Разработчики не несут ответственности за прямой или косвенный ущерб, вызванный использованием открытого кода falcot и futriix или технических решений, использующих этот код.(К началу)
## Подготовка **Для операционных систем семейства Debian** выполните следующие шаги: * Устанавливаем язык программирования Rust ```sh # apt update # apt upgrade # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` **Для операционных систем семейства Red Hat (Fedora, Aurora)** выполните следующие шаги: * Устанавливаем язык программирования Rust ```sh # dnf update # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ``` **Для операционной системы Alpine** выполните следующие шаги: * Устанавливаем язык программирования Rust ```sh # apk update # curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh ```(К началу)
## Компиляция 1. Копируем репозиторий ```sh $ git clone https://source.futriix.ru/gvsafronov/falcot ``` 2. Переходим в каталог с исходном кодом src ```sh $ cd falcot/ ``` 3. Компилируем Futriix с помощью пакетного менеджера `Cargo` ```sh $ cargo build ``` > [!WARNING] > **Futriix может быть скомпилирован для следующих операционных систем: `Linux`, `OSX`, `Open Indiana`, `FreeBSD`, но сборка для этих операционных систем не проводилась!!!**(К началу)
## Тестирование В состав проекта, входит утилита `integration_tests.rs` **integration_tests.rs** - это интеграционный тестовый набор, который при запуске создает и выполняет сразу несколько различных типов тестов. С её помощью можно запустить как сразу все тесты, так и определённый тест. Вышеописанная утилита - является мощным инструментом для комплексного тестирования всей системы Falcot, позволяющий быстро выявлять проблемы на разных уровнях приложения и обеспечивать высокое качество кода. **Falcot поддерживает пять типов тестов:** * **Регрессионный тест - проверяет базовую функциональность** * **Unit-тест - тестирует конкретный модуль (индексы)** * **Smoke-тест - проверяет запуск системы** * **Нагрузочный тест - проверяет производительность** * **Стресс-тест - проверяет устойчивость при высокой нагрузке** **Запуск всех тестов** Для запуска всех тестов сразу используйте команду в терминале: ```sh $ cargo test --test integration_tests -- --nocapture ``` **Запуск конкретного теста** ```sh #### Только регрессионный тест $ cargo test --test integration_tests regression_test -- --nocapture #### Только нагрузочный тест $ cargo test --test integration_tests load_test -- --nocapture ``` **Запуск теста с фильтром** ```sh #### Все тесты, содержащие "test" в названии $ cargo test --test integration_tests test -- --nocapture ``` **Что происходит при запуске** > [!TIP] > * Создается изолированная тестовая среда - каждый тест работает с чистой копией базы данных > * Тесты выполняются параллельно (где это безопасно) > * Каждый тест независим - результаты одного теста не влияют на другие > * Выводятся подробные логи о ходе выполнения тестов(К началу)
## Примеры команд субд ```sh ./falcot config.toml В интерактивном режиме: Loading configuration from: config.toml Database initialized with system collections Falcot Database Server Version: 1.0.0 Features: Wait-Free Architecture, Master-Master Replication, Lua Scripting, HTTP/HTTPS Support HTTP server started on 127.0.0.1:8082 Starting Lua interpreter... Lua interpreter ready. Type 'inbox.start' to enter database mode. Type 'exit' to quit. lua> falcot_log("Добро пожаловать в Falcot!") LUA: Добро пожаловать в Falcot! lua> inbox.start Entering database mode. Type CRUD commands or 'inbox.stop' to exit. falcot:~> create users '{"name": "Иван", "age": 25, "city": "Москва"}' Document created with ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890 falcot:~> create users '{"name": "Мария", "age": 30, "city": "Санкт-Петербург"}' Document created with ID: b2c3d4e5-f6g7-8901-bcde-f23456789012 falcot:~> list users Documents in collection: [{"_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Иван","age":25,"city":"Москва"},{"_id":"b2c3d4e5-f6g7-8901-bcde-f23456789012","name":"Мария","age":30,"city":"Санкт-Петербург"}] falcot:~> begin trans1 Transaction started successfully falcot:~> create products '{"name": "Ноутбук", "price": 50000, "category": "электроника"}' Document created with ID: c3d4e5f6-g7h8-9012-cdef-345678901234 falcot:~> create products '{"name": "Телефон", "price": 25000, "category": "электроника"}' Document created with ID: d4e5f6g7-h8i9-0123-defg-456789012345 falcot:~> commit trans1 Transaction committed successfully falcot:~> inbox.stop Exiting database mode. Back to Lua interpreter. lua> procedure create count_users ' function count_users() local result = falcot_db.query("users", "{}") local count = 0 for _ in string.gmatch(result, "\\\"name\\\":") do count = count + 1 end return "Всего пользователей: " .. count end return count_users ' Stored procedure created successfully lua> procedure call count_users Procedure result: Всего пользователей: 2 lua> exit Shutting down Falcot server... ``` **Параллельно запускаем в другом терминале:** ```sh #### Проверка HTTP API curl http://127.0.0.1:8082/api/ #### {"status": "ok", "message": "Falcot Server is running"} #### Получение списка пользователей curl http://127.0.0.1:8082/api/query?collection=users #### [{"_id":"a1b2c3d4-e5f6-7890-abcd-ef1234567890","name":"Иван","age":25,"city":"Москва"},...] ```(К началу)
## Обработка статических страниц Запуск статических веб-страниц, из сервера приложений falcot Falcot имеет встроенную поддержку статических файлов: Настройка статических файлов: ```sh Создайте каталог `static` в корневой директории приложения: $ mkdir static ``` Разместите файлы в каталоге static: ```sh falcot/ ├── static/ │ ├── index.html │ ├── style.css │ ├── script.js │ ├── images/ │ │ └── logo.png │ └── api-docs.html └── config.toml ``` После чего HTTP сервер автоматически будет обслуживать эти файлы: ```sh Примеры доступа: # Главная страница http://127.0.0.1:8082/ # Конкретный файл http://127.0.0.1:8082/index.html http://127.0.0.1:8082/style.css http://127.0.0.1:8082/images/logo.png # API документация http://127.0.0.1:8082/api-docs.html ``` **Falcot поддерживает следующие типы файлов:** * HTML (.html) * CSS (.css) * JavaScript (.js) * Изображения (.png, .jpg, .jpeg) * Аудио (.mp3) * Видео (.mp4) * JSON (.json) * Текстовые файлы (.txt) * Файлы в формате **pdf** (.pdf) * Markdown (.md)(К началу)
## Репликация Сервер-приложенй falcot поддерживает синхронную мастер-мастер репликацию. За управление репликацией отвечает секция `[replication]` в конфигурационном файле `config.toml` ```sh # config.toml [server] host = "127.0.0.1" port = 8081 http_port = 8082 [replication] enabled = true master_nodes = [ "127.0.0.1:8081", "127.0.0.1:8083", "127.0.0.1:8085" ] sync_interval = 2000 # 2 секунды ``` **Запуск нескольких экземпляров сервера** ```sh # Терминал 1 - основной сервер на порту 8081 $ cargo run -- config1.toml # Терминал 2 - реплика на порту 8083 $ cargo run -- config2.toml # Терминал 3 - реплика на порту 8085 $ cargo run -- config3.toml ``` **Тестирование репликации** ```sh -- replication_test.lua falcot_log("Testing replication...") -- Добавляем данные на основном сервере local doc1 = falcot_db.create("test_data", '{"type": "main", "value": "from_server_1"}') local doc2 = falcot_db.create("test_data", '{"type": "main", "value": "from_server_1_again"}') falcot_log("Documents created on main server: " .. doc1 .. ", " .. doc2) -- Ждем синхронизации falcot_log("Waiting for replication...") os.execute("sleep 3") -- Ждем дольше интервала синхронизации -- Проверяем на репликах (это нужно делать с разных клиентов) falcot_log("Checking replicated data...") local all_data = falcot_db.query("test_data", "{}") falcot_log("All data: " .. all_data) ``` **Мониторинг репликации** ```sh # В Lua shell можно проверять статус репликации lua> inbox.start # Проверяем статус репликации falcot:~> print(falcot.engine.replication.status()) # Смотрим список узлов falcot:~> print("Nodes: " .. table.concat(falcot.engine.replication.get_nodes(), ", ")) # Запускаем принудительную синхронизацию falcot:~> falcot.engine.replication.sync() ```(К началу)
**Пример конфигурационных файлов репликации для узлов кластера** **config1.toml (основной сервер):** ```sh [server] host = "127.0.0.1" port = 8081 http_port = 8082 [replication] enabled = true master_nodes = ["127.0.0.1:8083", "127.0.0.1:8085"] sync_interval = 1000 [lua] scripts_dir = "lua_scripts" auto_execute = ["init.lua"] ``` **config2.toml (Реплика 1):** ```sh [server] host = "127.0.0.1" port = 8083 http_port = 8084 [replication] enabled = true master_nodes = ["127.0.0.1:8081", "127.0.0.1:8085"] sync_interval = 1000 ``` **config3.toml (Реплика 2):** ```sh [server] host = "127.0.0.1" port = 8085 http_port = 8086 [replication] enabled = true master_nodes = ["127.0.0.1:8081", "127.0.0.1:8084"] sync_interval = 1000 ``` **config4.toml (Реплика 3):** ```sh [server] host = "127.0.0.1" port = 8086 http_port = 8087 [replication] enabled = true master_nodes = ["127.0.0.1:8081", "127.0.0.1:8083"]4sync_interval = 1000 ``` **Мониторинг и управление** ```sh #### Проверка статуса репликации через HTTP API curl http://127.0.0.1:8082/api/replication/status #### Принудительная синхронизация curl -X POST http://127.0.0.1:8082/api/replication/sync #### Просмотр статистики curl http://127.0.0.1:8082/api/stats ``` **Пример lua-скрипта по обработки ошибок репликации** ```sh -- error_handling.lua falcot_log("Testing replication error handling") -- Пытаемся добавить данные при недоступности реплик local success, result = pcall(function() return falcot_db.create("test_collection", '{"data": "test"}') end) if success then falcot_log("Data created successfully: " .. result) -- Проверяем статус репликации local rep_status = falcot.engine.replication.status() if rep_status ~= "active" then falcot_log("Warning: Replication is not active. Status: " .. rep_status) end else falcot_error("Failed to create data: " .. result) -- Пытаемся работать в offline режиме falcot_log("Trying offline operation...") local offline_data = falcot_db.query("test_collection", "{}") falcot_log("Offline data available: " .. #offline_data) end ``` ## Резервное копирование Для встроенной в сервер-приложений falcot, субд **futriix** предусмотрен механизм восстановления из резервных копий (бекапов) Бекапы можно создавать c помощью языка lua. **Создание бекапа через lua** ```sh -- Создаем бэкап local result = falcot.engine.backup.start() print(result) -- "Backup created successfully: /falcot/backups/backup_20231201_143022.json" -- Или через прямое обращение к функции local result = falcot_db.backup_start() print(result) -- Создаем несколько тестовых документов перед бэкапом falcot_db.create('products', '{"name": "Laptop", "price": 999.99, "category": "electronics"}') falcot_db.create('products', '{"name": "Phone", "price": 499.99, "category": "electronics"}') falcot_db.create('users', '{"name": "Alice", "email": "alice@example.com", "role": "admin"}') -- Создаем бэкап с данными local backup_result = falcot.engine.backup.start() ``` **Восстановление из бекапа** ```sh -- Восстанавливаем из конкретного бэкапа local restore_result = falcot.engine.backup.restore('/falcot/backups/backup_20231201_143022.json') print(restore_result) -- "Backup restored successfully" -- Или через прямое обращение local restore_result = falcot_db.backup_restore('/falcot/backups/backup_20231201_143022.json') -- Проверяем, что данные восстановились local users = falcot_db.read('users', 'user123') local products = falcot_db.query('products', '{}') print('Users:', users) print('Products count:', #products) ``` **Использование бекапов в интерактивной оболочке** ```sh #### Создаем бэкап backup start # Output: Backup created successfully: /falcot/backups/backup_20231201_143022.json #### Восстанавливаем из бэкапа backup restore /falcot/backups/backup_20231201_143022.json #### Output: Backup restored successfully #### Смотрим список доступных бэкапов !ls /falcot/backups/ #### Output: backup_20231201_143022.json backup_20231202_020001.json #### Создаем тестовые данные create products '{"name": "Test Product", "price": 100}' create users '{"name": "Test User", "email": "test@example.com"}' #### Создаем бэкап и сразу проверяем восстановление backup start delete products test_product_id delete users test_user_id backup restore /falcot/backups/backup_20231201_143022.json read products test_product_id #### Данные восстановяться ``` **Интеграция с триггерами для создания автоматических бекапов** ```sh -- Триггер для создания бэкапа при критических изменениях falcot_db.add_trigger('auto_backup_on_change', 'after_update', 'config', [[ local config = json.decode(document) if config.critical_setting then -- Создаем бэкап при изменении критических настроек local backup_result = falcot_db.backup_start() falcot_log('Auto-backup created due to config change: ' .. backup_result) end ]]) -- Триггер для бэкапа перед массовыми операциями falcot_db.add_trigger('backup_before_bulk_operation', 'before_delete', 'users', [[ -- Проверяем, это одиночное удаление или массовая операция if bulk_operation then local backup_result = falcot_db.backup_start() falcot_log('Backup created before bulk operation: ' .. backup_result) end ]]) ```(К началу)
## HTTP API Использование curl для работы с HTTP API: ```sh #### Проверка статуса сервера curl http://127.0.0.1:8082/ ##### Создание документа curl -X POST http://127.0.0.1:8082/api/create \ -H "Content-Type: application/json" \ -d '{ "collection": "users", "document": {"name": "John Doe", "email": "john@example.com", "age": 30} }' #### Чтение документа curl "http://127.0.0.1:8082/api/read?collection=users&id=document_id_here" #### Запрос документов curl -X POST http://127.0.0.1:8082/api/query \ -H "Content-Type: application/json" \ -d '{ "collection": "users", "filter": {"age": {"$gt": 25}} }' #### Обновление документа curl -X PUT http://127.0.0.1:8082/api/update \ -H "Content-Type: application/json" \ -d '{ "collection": "users", "id": "document_id_here", "document": {"name": "John Updated", "age": 31} }' #### Удаление документа curl -X DELETE http://127.0.0.1:8082/api/delete \ -H "Content-Type: application/json" \ -d '{ "collection": "users", "id": "document_id_here" }' ```(К началу)
## Аутентификация Для повышения безопасности в сервере-приложений falcot используется `Аутентификация`. За параметры авторизации отвечает секция `[security]` в конфигурационном файле `/falcot/config.toml` ```sh [security] require_authentication = false jwt_secret = "your-secret-key-here" password_hashing_rounds = 12 ```(К началу)
## HTTPS Сервер-приложенй falcot поддерживает протоколы https и http2. Утилита `generate_certs` - Генерация сертификатов Назначение: Генерация самоподписанных SSL/TLS сертификатов для HTTPS. Запуск утилиты для генерации тестовых сертификатов: `cargo run --bin generate_certs` По итогу работы утитилиты создаётся следующее дерево каталогов: ```sh certs/ ├── cert.pem # Публичный сертификат └── key.pem # Приватный ключ ``` Процесс генерации сертификата: **1.Создается пара ключей (публичный и приватный)** **2.Генерируется самоподписанный сертификат** **3.Сертификат подписывается приватным ключом** **4.Файлы сохраняются в папку `certs` ** **Примеры конфигураций** **Пример 1: Базовая конфигурация** ```sh [tls] enabled = true cert_path = "certs/cert.pem" key_path = "certs/key.pem" [server] https_port = 8443 ``` **Пример 2: Генерация сертификатов и запуск** ```sh # Генерируем сертификаты cargo run --bin generate_certs # Запускаем сервер cargo run # Тестируем подключение curl -k https://localhost:8443/api/status curl http://localhost:8082/api/status ``` > [!TIP] > Безопасность: > * Самоподписанные сертификаты подходят для разработки и внутреннего использования > * Для продакшена рекомендуется использовать сертификаты от Let's Encrypt или других CA > * ACL обеспечивает дополнительный уровень безопасности > * HTTPS шифрует трафик между клиентом и сервером > * ACL защищает от нежелательных подключений, а `generate_certs` позволяет быстро настроить HTTPS для безопасного соединения.(К началу)
## ACL **ACL** - это система управления доступом, которая позволяет разрешать или запрещать подключения к серверу на основе IP-адресов. Как работает ACL в Falcot: Принцип работы: **1.Проверка запрещенных IP - если IP в списке denied_ips, доступ блокируется** **2.Проверка разрешенных IP - если список allowed_ips не пустой, проверяется наличие IP в нем** **3.Если ACL отключен - все подключения разрешены** **Примеры использования в конфигурации:** ```sh #### Разрешить только локальные подключения [acl] enabled = true allowed_ips = ["127.0.0.1", "::1"] denied_ips = [] #### Заблокировать конкретные проблемные IP [acl] enabled = true allowed_ips = [] # Пустой список = разрешены все denied_ips = ["192.168.1.100", "10.0.0.25"] #### Разрешить только внутреннюю сеть [acl] enabled = true allowed_ips = ["192.168.1.0/24", "10.0.0.0/8"] denied_ips = [] ```(К началу)
## Индексы Falcot поддерживает два стандартных типа индекса: первичные индексы и вторичные индексы **Различия между первичным и вторичным индексом** 1. **Первичный индекс (Primary Index)** **Особенности:** 1. **Обязательно уникальный - не допускает дублирования значений** 2. **Автоматически создается для поля id в каждой коллекции и не требует ручного создания** 3. **Используется для быстрого поиска документов по их идентификатору** 4. **Гарантирует целостность данных - предотвращает создание документов с одинаковым `ID`** **Пример создания** ```sh -- Поиск по ID (использует первичный индекс) local document = falcot_db.read("users", "550e8400-e29b-41d4-a716-446655440000") ``` 2. **Вторичный индекс (Secondary Index)** **Особенности:** 1. **Может быть как уникальным, так и неуникальным** 2. **Создается вручную для любых полей документа** 3. **Используется для оптимизации запросов по не-`ID полям`** 4. **Поддерживает различные типы данных (строки, числа, булевы значения)** **Пример создания** ```sh -- Неуникальный индекс по полю 'email' falcot_db.create_index("users", "email_idx", "email", false) -- Уникальный индекс по полю 'username' falcot_db.create_index("users", "username_idx", "username", true) ``` **Создание и использование индексов через Lua shell** ```sh #### Запускаем сервер $ cargo run #### Входим в режим inbox $ lua> inbox.start #### Создаем коллекцию пользователей falcot:~> create users '{"name": "Alice", "age": 30, "email": "alice@example.com"}' falcot:~> create users '{"name": "Bob", "age": 25, "email": "bob@example.com"}' falcot:~> create users '{"name": "Charlie", "age": 35, "email": "charlie@example.com"}' #### Создаем вторичный индекс по полю "name" falcot:~> index create users name_index name #### Создаем уникальный индекс по полю "email" falcot:~> index create users email_index email unique #### Ищем по индексу name falcot:~> index query users name_index "Alice" #### Ищем по индексу email falcot:~> index query users email_index "bob@example.com" #### Пытаемся создать дубликат email (должна быть ошибка) falcot:~> create users '{"name": "Another Bob", "age": 28, "email": "bob@example.com"}' ``` **Создание и использование индексов через Lua скрипты:** ```sh -- create_indexes.lua falcot_log("Creating indexes...") -- Создаем индексы falcot_db.create_index("users", "name_index", "name", false) falcot_db.create_index("users", "email_index", "email", true) -- Добавляем данные falcot_db.create("users", '{"name": "Alice", "age": 30, "email": "alice@example.com"}') falcot_db.create("users", '{"name": "Bob", "age": 25, "email": "bob@example.com"}') -- Поиск по индексу local result = falcot_db.query_by_index("users", "name_index", "Alice") falcot_log("Search result: " .. result) -- Проверка уникальности (должна вызвать ошибку) local status, err = pcall(function() falcot_db.create("users", '{"name": "Another", "age": 28, "email": "bob@example.com"}') end) if not status then falcot_error("Unique constraint violation: " .. err) end ``` **Производительность индексов** **Первичный индекс:** * ** Максимальная скорость поиска по ID** * **Всегда в памяти (кешируется)** * **Минимальные накладные расходы** **Вторичный индекс:** * **Скорость зависит от размера индексируемых данных** * **Может требовать дополнительной памяти** * **Оптимизирует конкретные типы запросов** **Рекомендации по использованию индексов** Используйте первичный индекс для: * **Быстрого доступа к документам по их ID** * **Операций обновления/удаления конкретных документов** * **Гарантии уникальности идентификаторов** Используйте вторичный индекс для: * **Поиска документов по любым другим полям** * **Оптимизации часто выполняемых запросов** * **Обеспечения уникальности бизнес-ключей (email, username и т.д.)** * **Ускорения сортировки и фильтрации** #### Обработка ошибок индексов **Первичный индекс (автоматическая проверка) на языке lua: ** ```sh -- Попытка создать документ с существующим ID вызовет ошибку local status, err = pcall(function() falcot_db.create("users", '{"id": "existing_id", "name": "Duplicate"}') end) ``` **Вторичный индекс (ручная проверка уникальности) на языке lua: :** ```sh -- При unique=true попытка дублирования вызовет ошибку local status, err = pcall(function() falcot_db.create("users", '{"email": "existing@example.com", "name": "Duplicate"}') falcot_db.create("users", '{"email": "existing@example.com", "name": "Duplicate2"}') end) if not status then falcot_error("Unique constraint violation: " .. err) end ``` Таким образом, первичный индекс в falcot - это автоматически создаваемый уникальный индекс для поля `id`, в то время как вторичные индексы создаются вручную для оптимизации произвольных запросов и могут быть как уникальными, так и неуникальными.(К началу)
## Constraints (Ограничения) **Ограничения (Constraints)** — это правила, применяемые к данным в таблице в реляционных субд (или её аналоге в не реляцционных субд) для поддержания их корректности и надёжности. Они играют важную роль в обеспечении целостности данных и соответствия бизнес-правилам. При создании таблицы или измененинии её структуры, можно определить различные ограничения,предотвращающие добавление, изменение или удаление данных, нарушающих установленные правила. Это помогает избежать нежелательных ситуаций, например таких как: * **Наличие нескольких пользователей с одинаковыми идентификаторами** * **Ссылки на несуществующие записи в других таблицах** * **Отсутствие данных обязательных для заполнения** * **Ввод некорректных значений (например, отрицательного возраста или будущей даты рождения)** Ограничения реализованы на уровне встроенной субд **futriix**, ниже приведы примеры использования Ограничений:(К началу)
## Хранимые процедуры **Хранимая процедура в falcot** – это откомпилированная во внутреннее представление сервера субд подпрограмма, хранящаяся в базе данных. Хранимые процедуры пишутся на специальном языке хранимых процедур и триггеров, в котором имеются операторы присваивания, ветвлений и циклов, и в которых можно использовать операторы SQL, такие как INSERT, DELETE, UPDATE и SELECT. **Создание хранимой процедуры** ```sh -- Создание хранимой процедуры для расчета статистики local procedure_code = [[ function calculate_stats(collection_name) local documents = falcot_db.query(collection_name, "{}") local count = 0 local total_age = 0 for doc in string.gmatch(documents, '([^,]+)') do count = count + 1 -- Здесь можно парсить JSON и считать статистику end return "Документов: " .. count .. ", Средний возраст: " .. (total_age / count) end return calculate_stats ]] ``` ```sh -- Сохраняем процедуру falcot_db.create_procedure("calculate_stats", procedure_code) ``` **Работа с хранимыми процедурами в lua интерпретаторе:** ```sh -- Создание процедуры procedure create calculate_stats ' function calculate_stats(collection_name) local result = falcot_db.query(collection_name, "{}") local count = 0 for _ in string.gmatch(result, "\\\"_id\\\":") do count = count + 1 end return "Количество документов в " .. collection_name .. ": " .. count end return calculate_stats ' -- Вызов процедуры procedure call calculate_stats ``` **Создание хранимых процедур во одноимённой встроенной субд** ```sh # Создание процедуры procedure create user_stats ' function calculate_user_stats() local users = falcot_db.query("users", "{}") local count = 0 for _ in string.gmatch(users, "\\\"name\\\":") do count = count + 1 end return "Всего пользователей: " .. count end return calculate_user_stats ' ``` ```sh #### Вызов процедуры procedure call user_stats ```(К началу)
## Триггеры Falcot помимо хранимых процедур поддерживает триггеры (обратные вызовы) Триггеры можно создавать тремя способами: через lua-shell, через интерактивную оболочку, через хранимые процедуры встроенной субд **Создание триггеров через lua-shell** ```sh -- Создаем триггер, который логирует создание документов в коллекции 'users' falcot_db.add_trigger('log_user_creation', 'after_create', 'users', [[ falcot_log('New user created with data: ' .. document) -- Можно добавить дополнительную логику, например: -- - Отправка уведомления -- - Валидация данных -- - Обновление связанных документов ]]) -- Триггер для проверки данных перед обновлением falcot_db.add_trigger('validate_user_update', 'before_update', 'users', [[ local user_data = json.decode(document) if not user_data.email or not user_data.email:match('^[^@]+@[^@]+%.[^@]+$') then error('Invalid email format') end if user_data.age and user_data.age < 0 then error('Age cannot be negative') end ]]) -- Триггер для очистки связанных данных при удалении falcot_db.add_trigger('cleanup_user_data', 'before_delete', 'users', [[ -- Удаляем связанные записи из других коллекций falcot_db.delete('user_sessions', id) falcot_db.delete('user_preferences', id) falcot_log('Cleaned up user data for ID: ' .. id) ]]) ``` **Создание триггеров через интерактивную оболочку** ```sh ##### Переходим в режим inbox inbox.start ##### Создаем триггер для логирования trigger add log_creation after_create orders "falcot_log('New order created: ' .. document)" #### Триггер для автоматического обновления статистики trigger add update_stats after_create orders [[ -- Увеличиваем счетчик заказов local stats = falcot_db.read('statistics', 'order_count') or '0' local count = tonumber(stats) + 1 falcot_db.update('statistics', 'order_count', tostring(count)) ]] #### Триггер для проверки цены перед созданием trigger add validate_price before_create products [[ local product = json.decode(document) if product.price <= 0 then error('Price must be positive') end ]] ``` **Пример создание триггеров через хранимые процедуры** ```sh -- Создаем документ - сработает триггер after_create falcot_db.create('users', '{"name": "John", "email": "john@example.com", "age": 25}') -- В логах появится: "LUA: New user created with data: {"name": "John", "email": "john@example.com", "age": 25}" -- Попытка создать пользователя с невалидным email falcot_db.create('users', '{"name": "Invalid", "email": "invalid", "age": 30}') -- Получим ошибку: "Invalid email format" -- Обновляем пользователя falcot_db.update('users', 'user123', '{"name": "John Updated", "email": "john.new@example.com", "age": 26}') -- Сработает триггер before_update для валидации -- Удаляем пользователя falcot_db.delete('users', 'user123') -- Сработает триггер before_delete для очистки связанных данных ```(К началу)
## Lua-скрипты > [!CAUTION] > **Поддержка работы lua в настоящий момент эксперементальная и может вызвать аварийное завершение сервера!!!** В Falcot существует несколько способов запуска Lua скриптов **Способ 1: Непосредственный ввод кода в lua-интерпретатор** ```sh lua> falcot_log("Привет, мир!") LUA: Привет, мир! lua> local x = 10 + 5 lua> falcot_log("Результат: " .. x) LUA: Результат: 15 ``` **Способ 2: Многострочный ввод (через конкатенацию)** ```sh lua> local script = [[ >> for i = 1, 3 do >> falcot_log("Итерация: " .. i) >> end >> ]] lua> falcot.engine.execute_script(script) LUA: Итерация: 1 LUA: Итерация: 2 LUA: Итерация: 3 ``` **Способ 3: Запуск из файла** 1. **`lua_scripts` - это каталог (директория), где хранятся Lua-скрипты для выполнения сервером Falcot** 2. **При запуске сервера автоматически выполняются скрипты из `auto_execute` списка, находящиеся в этой директории** 3. **Сервер автоматически создает этот каталог при первом запуске, если он не существует** **Как это работает:** 1. **Сервер при запуске создает каталог lua_scripts (если его нет)** 2. **В этот каталог можно помещать Lua-скрипты** 3. **Скрипты из списка auto_execute выполняются автоматически при старте сервера** 4. **Другие скрипты можно выполнять вручную через Lua-интерпретатор командой falcot.engine.lua.execute("имя_скрипта.lua")** ```sh Пример структуры каталогов falcot: lua_scripts/ ├── init.lua (автоматически выполняется при старте) ├── utils.lua (вспомогательные функции) ├── procedures/ (подкаталог для хранимых процедур) │ ├── user_management.lua │ └── data_processing.lua └── config/ (подкаталог для конфигурационных скриптов) └── system_config.lua ``` ### Примеры lua-скриптов ```sh -- Простой Lua скрипт для демонстрации falcot_log("Запуск примера Lua скрипта") -- Создание документа falcot_db.create("users", '{"name": "John", "age": 30, "email": "john@example.com"}') -- Чтение документа local result = falcot_db.read("users", "some-document-id") falcot_log("Результат чтения: " .. result) ``` ```sh -- Математические операции local x = 10 local y = 20 falcot_log("Сумма: " .. (x + y)) -- Циклы и условия for i = 1, 5 do if i % 2 == 0 then falcot_log("Четное число: " .. i) else falcot_log("Нечетное число: " .. i) end end ```(К началу)
## Транзакции Работа с транзакциями, во встроенной субд: ```sh # Начало транзакции begin transaction_123 # Несколько операций в транзакции create users '{"name": "Alice", "age": 25}' create users '{"name": "Bob", "age": 30}' create users '{"name": "Charlie", "age": 35}' # Фиксация транзакции commit transaction_123 # Или откат при ошибке begin transaction_456 create users '{"name": "David", "age": 40}' rollback transaction_456 ```(К началу)
## Шардинг Встроенная в falcot субд **futriix** поддерживает горинзонтальный шардинг, т.е. горизонтальное масштабирование. По умолчанию шардинг производится `по алгоритму Hash-based с консистентным хэшированием`. Шифрование производится согласно алгоритму `SipHasher13`. Но также поддерживается возможность управлять шардингом вручную. Ниже приведены команды по управлению шардингом: **Пример добавления узла** ```sh #### Добавляем новый узел шардинга falcot:~> shard add node1 127.0.0.1:8084 1024 #### Где: #### - node1: идентификатор узла #### - 127.0.0.1:8084: адрес узла #### - 1024: емкость в MB ``` **Пример удаления узла** ```sh #### Удаляем узел из кластера falcot:~> shard remove node1 ``` **Пример миграции между шардами узла** ```sh #### Мигрируем данные коллекции между узлами falcot:~> shard migrate users node1 node2 user_id #### Где: #### - users: коллекция для миграции #### - node1: исходный узел #### - node2: целевой узел #### - user_id: ключ шардирования ``` **Пример просмотра статуса кластера** ```sh #### Показывает статус всего кластера falcot:~> shard status #### Пример вывода: #### === Cluster Status === #### Total capacity: 2048 GB #### Total used: 512 MB #### Rebalance needed: false #### #### Node node1: #### Address: 127.0.0.1:8081 #### Capacity: 1024 MB #### Used: 256 MB #### Usage: 25.0% #### Collections: users, products ``` **Пример ручной ребалансировки кластера** ```sh #### Запускает автоматическую ребалансировку falcot:~> cluster rebalance ``` **Пример создания шардированной системы пользователей** ```sh #### Добавляем узлы falcot:~> shard add node1 127.0.0.1:8081 1024 falcot:~> shard add node2 127.0.0.1:8082 1024 #### Создаем constraints для пользователей falcot:~> constraint add users unique_email unique email falcot:~> constraint add users valid_age range age "{\"min\":13,\"max\":150}" falcot:~> constraint add users valid_username pattern username "^[a-zA-Z0-9_]{3,20}$" #### Распределяем пользователей по шардам shard migrate users node1 node2 user_id ``` **Мониторинг шардинга** ```sh #### Проверяем статус falcot:~> shard status #### Если нужно перебалансировать falcot:~> cluster rebalance #### Проверяем constraints falcot:~> constraint list users ```(К началу)
## Сферы применения Идеальными сферами применения проекта falcot, являются следующие: 1. **Микросервисы с потребностью в оперативной аналитике** 2. **Работающая в режиме реального времени панель мониторинга - для администрирования бекенда** 3. **Приложения, тербующие среднего масштабирования со смешанными рабочими нагузками (англ. workload)** 4. **Прототипирование перед выбором специализированного решения**(К началу)
## Дорожная карта - [x] Реализовать поддержку хранимых процедур - [x] Реализовать поддержку триггеров (обратных вызовов) - [x] Реализовать поддержку многопоточности - [x] Реализовать неблокирующие чтение/запись - [x] Реализовать мульти-мастер асинхронную репликацию через файл конфигурации - [x] Реализовать логирование - [x] Реализовать поддержку синхронной мастер-мастер репликации - [x] Реализовать поддержку протоколов HTTPS и HTTP2 - [x] Реализовать поддержку первичных индексов - [x] Реализовать поддержку протокола MessagePack - [x] Реализовать поддержку транзакций - [x] Реализовать поддержку первичных и вторичных индексов - [x] Добавить механизм сторонних модулей на языке lua, расширяющих базовый функционал сервера - [x] Реализовать проверку запуска сервера при запуске клиента (если сервер НЕ запущен клиент не запускается) - [x] Реализовать поддержку HTTP-restfull API - [x] Исправить ошибки записи журнала логов (в журнал лога кроме текущего времени добавить текущий год) - [x] Реализовать утилиту тестирования сервера на количество запросов на чтение/запись - [x] Переписать асинхронную мастер-мастер репликацию на синхронную мастер-мастер репликацию - [ ] Реализовать журнал WAL - [ ] Реализовать графический веб-интерфейс для упраления кластером - [x] Реализовать автоматический шардинг с консистентным хэшированием - [ ] Реализовать поддержку алгоритма Raft - [ ] Реализовать поддержку SQL - [ ] Реализовать поддержку ACID-транзакций - [ ] Интегрировать интеллектуального помощник FutBot на официальный сайт - [ ] Реализовать полноценного интеллектуального помощника FutBot, задачами которого будут быстрый поиск ответов на вопросы, возникающие при эксплуатации субд Futrix. См. [Открытые проблемы](https://source.futriix.ru/gvsafronov/futriixw/issues) полный список предлагаемых функций (и известных проблем).(К началу)
## Вклад Вклады — это то, что делает сообщество открытого исходного кода таким замечательным местом для обучения, вдохновения и творчества. Любой ваш вклад **очень ценится**. Если у вас есть предложение, которое могло бы улучшить проект **falcot && futriix**, создайте форк данного репозитория и создайте запрос на включение. Также можно просто открыть задачу с тегом «улучшение». 1. Форкните проект 2. Создайте свою ветку функций (`git checkout -b Feature/AmazingFeature`) 3. Зафиксируйте свои изменения (git commit -m 'Add some AmazingFeature'`) 4. Отправьте в ветку (`git push main Feature/AmazingFeature`) 5. Откройте запрос на включение ## Контакты Григорий Сафронов - [E-mail](gvsafronov@yandex.ru) Ссылка на Интеллектуальный помощник - [FutBot](https://t.me/Futriix_bot)(К началу)