(К началу)
## Лицензия Проект распространяется под лицензией **`CDDL 1.0`**. Подробнсти в файлах `LICENSE`. Эта лицензия позволяет вам производить копирование, модификацию, распространение, включение в другие проекты, получение патентных прав, распространение бинарных файлов с доступом к их исходному коду. Она запрещает вам добавление новых ограничений, скрытие изменений, удаление оригинальных уведомлений, несоблюдение условий CDDL 1.0 при перераспределении, неправильное связывание с другими лицензиями. Все дополнительное программное обеспечение (включая скрипт компиляции проекта `build.sh`) предоставляются "как есть", без гарантий и обязательств со стороны разработчиков. Разработчики не несут ответственности за прямой или косвенный ущерб, вызванный использованием открытого кода Futriix и futriix или технических решений, использующих этот код.(К началу)
## Глоссарий * **База Данных(БД)** - это структурированное, организованное хранилище данных, которое позволяет удобно собирать, хранить, управлять и извлекать информацию. * **Система Управления Базами Данных(СУБД)** - это программное обеспечение, которое позволяет создавать, управлять и взаимодействовать с базами данных * **Мультимодельная СУБД** - это СУБД, которая объединяет в себе поддержку нескольких моделей данных (реляционной, документной, графовой, ключ-значение и др.) в рамках единого интегрированного ядра. * **Резидентная СУБД** - это СУБД, которая работает непрерывно в оперативной памяти (RAM). * **Eventual consistency** — модель согласованности в распределённых системах, при которой после прекращения новых обновлений все реплики данных со временем приходят к одинаковому состоянию. `Ключевые особенности Eventual consistency применительно к архитектуре СУБД:` 1. **Допускает временные расхождения**(К началу)
## Архитектурные примечания и предложения безопасности > [!IMPORTANT] > **Архитектурные примечания** > > Futriix изначально разрабатывался как монолит, внутреннее ядро которого, включающее фрейморк, для реализации WUI носило кодовое имя > «Futriis». > С переходом к финальному названию «Futriix»- внешние интерфейсы были переименованы, > но внутренние переменные сохранили исходное имя в целях минимизации изменений. А также фрейморк, для реализации WUI сохранил своё прежнее название "Futriis" и был интегрирован в ядро субд. > Считайте `futriis` внутренним псевдонимом `futriix` > И помните, что в настоящий момент «Futriis — это историческое название ядра Futriix» > [!IMPORTANT] > **Аутентификация в веб-интерфейсе** > >**Общие положения**(К началу)
## Алгоритмы и структуры данных В данном разделе перечислены основные алгоритмы и структуры данных, использующиеся в субд `futriix` **Структуры данных** | Структура | Компонент | Описание | |----------|----------|----------| | **Хранилище (Storage)** | `sync.Map` | Конкурентная хэш‑таблица для хранения баз данных, обеспечивает wait‑free чтение и запись | | | Атомарные счётчики (`atomic.Int64`) | Для отслеживания общего количества документов без блокировок | | **База данных (Database)** | `sync.Map` коллекций | Аналог слайса в реляционных СУБД | | | Мьютексы (`sync.RWMutex`) | Для операций изменения структуры (создание/удаление коллекций) | | **Коллекция (Collection)** | `sync.Map` документов | Ключ: ID документа, значение: указатель на `Document` | | | `sync.Map` индексов | Отдельное хранилище для инвертированных индексов | | | Атомарные счётчики | Количество документов (`docCount`) и размер в байтах (`sizeBytes`) | | **Индекс (Index)** | `sync.Map` (значение → ID) | Реализация инвертированного индекса | | | Уникальный индекс | Прямая lookup‑операция $O(1)$ | | | Неуникальный индекс | Range‑сканирование с оптимизированным сравнением | | | Составной индекс | Конкатенация значений полей с разделителем `"|"` | | **Документ (Document)** | `map[string]interface{}` | Динамическая схема полей | | | Версия (`uint64`) | Для оптимистичных блокировок | | | Временные метки | Создание, обновление, удаление (Unix‑миллисекунды) | | **Транзакция (Transaction)** | WAL (`Write‑Ahead Log`) | Журнал упреждающей записи для durability | | | Кэш операций | Локальное хранение изменений до коммита | | | Список блокировок | Отслеживание затронутых документов | | **Триггер (Trigger)** | `sync.Map` с составным ключом | `collection|event|name` для быстрого поиска | | | Условия (`TriggerCondition`) | Хранятся как структура с полями `field/operator/value` | | | Очередь выполнения | Буферизированный канал для асинхронной обработки | | **ACL (Access Control List)** | `map[string]bool` для каждой операции | `read`, `write`, `delete`, `admin` | | | История изменений | Массив с временными метками для аудита |(К началу)
## Системные требования > [!CAUTION] > **СУБД работает только на Unix‑подобных ОС. Поддержка Windows и macOS не предусмотрена!** > **Важно: из‑за архитектурных особенностей (из‑за архитектурных особенностей в т.ч. низкоуровневых системных вызовов, специфичных для POSIX‑совместимых ядер) запуск на Windows(включая WSL) и macOS не поддерживается!**(К началу)
## Быстрый старт Быстрый старт — это краткое руководство для тех, кто хочет сразу попробовать **futriix** в деле. Здесь вы найдёте минимально необходимые команды для установки, запуска и первого взаимодействия с СУБД. Мы намеренно опустили тонкости настройки, чтобы вы могли оценить основные возможности без погружения в документацию. Полный список параметров и режимов работы описан в следующих разделах. 1. Клонируйте репозиторий: ```bash $ git clone https://github.com/futriix/futriix $ cd futriix ``` > [!IMPORTANT] > **Важно: Шаги с 1.1 по 1.3(включительно) необходимы тогда и только тогда, когда вы используете ЗПС** > **Данный шаг позволит скачать вам все зависимости в архиве "vendor.zip"** **1.1 Скачайте локальные зависимости в архиве "vendor.zip"** ```bash $ curl -L -o vendor.zip \ -H "User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36" \ -H "Referer: https://futriix.ru:8083/" \ "https://futriix.ru:8083/fm/?r=/download&path=L3dlYi9mdXRyaWl4LnJ1L3B1YmxpY19odG1sL2Rvd25sb2Fkcy92ZW5kb3Iuemlw" ``` **1.2 Поместите архив `vendor.zip` в один каталог с проектом и распакуйте его командой:** ```bash $ unzip vendor.zip ``` **1.3 Скомпилируйте проект с помощью специального скрипта для ЗПС `build_vendor.sh`** ```bash $ ./build_vendor.sh ``` 2. Скомпилируйте и запустите: ```bash # Стандартная сборка для ОС на базе Linux $ ./build.sh # Сборка для операционных систем на базе Illumos $ cd scripts/ $ ./build_illumos.sh # Показать справку $./build.sh --help $ ./futriix ```(К началу)
### Логирование В субд **"futriix"** используется два журнала для ведение логов: `"futriix.log"`-основной журнал, в котором ведутся логи при работе в субд через терминал, и `"webui.log"`-основной журнал, в котором ведутся логи при работе в субд через веб-интрефейс. **futriix.log** — основной системный журнал, фиксирующий все события жизненного цикла СУБД: запуск/остановку сервера, инициализацию компонентов (транзакции, Raft-координатор, ACL), состояние кластера и критические ошибки выполнения запросов. **webui.log** — специализированный журнал веб-интерфейса, регистрирующий только действия пользователей через Web UI: успешные и неудачные попытки входа, управление аватарами, создание/удаление триггеров и индексов, а также операции импорта/экспорта данных. Оба журнала используют структурированный JSON-формат (для webui.log) и текстовый формат с временными метками (для futriis.log), что обеспечивает удобный парсинг и интеграцию с системами мониторинга. Журналы автоматически ротируются и ограничены по размеру (по умолчанию 10000 записей для webui.log), предотвращая неконтролируемый рост дискового пространства при длительной работе сервера.(К началу)
### Тестирование Для проверки корректности функционирования субд на уровне исходного кода, был разработа набор из пяти тестов: (регрессионный, smoke-тест, функциональный, интеграционный и нагрузочный). Разработанный набор из пяти вышеупомянутых тестов на языке Lua обеспечивает комплексную проверку всех ключевых компонентов СУБД: CRUD-операций, индексов, транзакций, ограничений целостности, ACL, триггеров, MVCC-версионирования, а также взаимодействия API с хранилищем и кластерной координации. Регрессионный тест гарантирует, что изменения кода не нарушили существующую функциональность, smoke-тест выполняет быструю проверку доступности и базовой работоспособности системы. Функциональный и интеграционный тесты проверяют корректность реализации бизнес-требований и взаимодействие между компонентами, а нагрузочный тест оценивает производительность (латентность, пропускную способность) под различными сценариями использования. Исходный код всех тестов можно найти в директории `/futriix/tests/` Команды для запуска тестов приведены ниже: > [!IMPORTANT] > 1. Перед запуском тестов убедитесь, что СУБД запущена и HTTP API доступен на порту 8080 > 2. Load test может занять несколько минут при больших объёмах данных ```bash # Установка зависимостей для Lua-тестирования sudo apt install lua5.3 lua-socket # Запуск регрессионного теста lua test_regression.lua # Запуск smoke-теста lua test_smoke.lua # Запуск функционального теста lua test_functional.lua # Запуск интеграционного теста lua test_integration.lua # Запуск нагрузочного теста lua test_performance.lua # Запуск всех тестов последовательно for test in test_regression.lua test_smoke.lua test_functional.lua test_integration.lua test_performance.lua; do echo "=== Running $test ===" lua "$test" echo "" done ```(К началу)
## CRUD операции > [!TIP] >**Совместимость с MongoDB**(К началу)
## Индексы Futriix поддерживает два базовых типов индексов: **первичные (по _id)** и вторичные индексы, хранящиеся отдельно от документов. Кроме того в субд присутствуют уникальные и составные индексы, поиск по точному значению и префиксу, а также автоматическое обновление индексов при изменениях документов. ```sh # Создание обычного индекса futriix:~> create index employees name_idx name ✓ Index 'name_idx' created on collection 'employees' at 2026-01-15 10:50:15.123 # Создание уникального индекса futriix:~> create index employees email_idx email unique ✓ Index 'email_idx' created on collection 'employees' at 2026-01-15 10:50:18.456 # Создание составного индекса futriix:~> create index employees dept_age_idx department,age ✓ Index 'dept_age_idx' created on collection 'employees' at 2026-01-15 10:50:21.789 # Просмотр всех индексов (с временем создания) futriix:~> show indexes employees Indexes on collection 'employees': - _id_ (created: 2026-01-15 10:32:15.234) - name_idx (created: 2026-01-15 10:50:15.123) - email_idx (unique) (created: 2026-01-15 10:50:18.456) - dept_age_idx (created: 2026-01-15 10:50:21.789) # Удаление индекса (с временем удаления) futriix:~> drop index employees dept_age_idx ✓ Index 'dept_age_idx' dropped from collection 'employees' at 2026-01-15 10:51:05.234 # Начало сессии (с временем создания сессии) futriix:~> db.startSession() ✓ Session started: session_12345 at 2026-01-15 10:55:15.123 ```(К началу)
## Транзакции В СУБД **futriix** для обеспечения надёжности (durability) и быстрого восстановления БД из резервной копии реализована полноценная поддержка WAL (Write-Ahead Logging). Журнал WAL по умолчанию хранится в файле futriix.wal, находящемся в каталоге futriix. СУБД поддерживает ACID‑транзакции с MVCC (Multi‑Version Concurrency Control) — для локальной атомарности и изоляции на уровне отдельных узлов кластера. Для распределённых операций используется паттерн SAGA с оркестратором: глобальная согласованность достигается через последовательность компенсируемых локальных транзакций, управляемых централизованным оркестратором, который отслеживает состояние шагов, инициирует компенсацию при сбоях и гарантирует завершение сценария в согласованном состоянии. Парадигма eventual consistency обеспечивает согласованность данных в распределённой среде: изменения распространяются асинхронно между узлами, а финальное согласованное состояние достигается со временем с учётом гарантий доставки событий и правил разрешения конфликтов. Такой подход позволяет сохранить высокую доступность и производительность даже при временных сетевых сбоях. Доступны команды, с восстановлением после сбоев через журнал предзаписи: * `startSession()`- Начать сессию (устойчивый контекст взаимодействия клиента с СУБД, в рамках которого поддерживается состояние) в субд в рамках которой будет открыта транзакция * `startTransaction()`- Начать транзакцию * `commitTransaction()`- Сделать коммит транзакцию * `abortTransaction()` - Прервать транзакцию > [!IMPORTANT] > **Матрица гарантий согласованности**(К началу)
## Кластеризация и шардинг Субд `futriix` является распределённой субд. Согласованность узлов в распределённом кластере определяется на основе протокола **Raft** с автоматическими выборами лидера. Поддерживаются одноузловой (для запуска на одном узле, без организации кластера) и многокластерный режимы, репликация данных (синхронная/асинхронная), мастер-мастер репликация и health-мониторинг узлов. Протокол Raft в субд был реализован для отказоустойчивости. Безопасность каналов и аутентификация узлов возложены на инфраструктуру ЗПС (изоляция сети, статическая маршрутизация, контроль администратора). Модель угроз ЗПС не предполагает наличие атакующего внутри кластерной сети. Шифрование Raft-трафика не реализовано, так как при развёртывании в ЗПС все межсерверные соединения находятся в пределах одного физически изолированного сегмента. В случае требования шифрования на уровне приложения, администратор ЗПС может использовать туннелирование (IPsec, WireGuard) средствами нижележащей сетевой инфраструктуры. Автомасштабирование реализовано на основе комбинированного алгоритма **PHA (Predictive Horizontal Autoscaler)- Горизонтальный автомасштабировщик с прогнозированием нагрузки** **Полная блок-схема алгоритма** ```sh ┌─────────────────────────────────────────────────────────────┐ │ Цикл оценки (каждые 30 сек) │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 1. Сбор метрик со всех узлов │ │ - CPU, Memory, QPS, Latency, Storage │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 2. Расчёт композитной нагрузки для каждого узла │ │ node_load = Σ(metric/ threshold) * weight │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 3. Вычисление средней нагрузки по кластеру │ │ avg_load = Σ(node_load) / total_nodes │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 4. Прогнозирование (линейная регрессия на окне из N точек) │ │ predicted_load = avg_load + slope │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 5. Проверка cooldown периодов │ │ if time_since_last_scale < cooldown → NoChange │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 6. Принятие решения │ │ if predicted_load > scale_up_threshold → ScaleUp │ │ if predicted_load < scale_down_threshold → ScaleDown │ │ else → NoChange │ └─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ ▼ ▼ ▼ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ ScaleUp │ │ScaleDown │ │NoChange │ └──────────┘ └──────────┘ └──────────┘ │ │ ▼ ▼ ┌─────────────────┐ ┌─────────────────┐ │ 7a. Добавление │ │ 7b. Удаление │ │ узлов │ │ узлов │ │ │ │ │ │ nodes_to_add = │ │ nodes_to_remove │ │ ceil(ratio * N) │ │ = ceil(ratio*N) │ └─────────────────┘ └─────────────────┘ ``` **Основной алгоритм принятия решений** ```sh Решение = f(текущая_нагрузка, прогнозируемая_нагрузка, история, cooldown) ``` **Базовый принцип:** * Сравнение средневзвешенной нагрузки с двумя порогами (верхним и нижним) * Учёт нескольких метрик с разными весами * Использование скользящего окна для сглаживания выбросов **Алгоритм расчёта нагрузки на узел** Для каждого узла вычисляется композитный показатель нагрузки по формуле: ```sh node_load = Σ (metric_value / metric_threshold) * metric_weight ───────────────────────────────────────────────── Σ metric_weights ``` **Пример с весами по умолчанию:** * CPU: 40% веса, порог 75% * Memory: 30% веса, порог 80% * QPS: 20% веса, порог 70% * Latency: 10% веса, порог 65% Если CPU = 90%, Memory = 85%, QPS = 80%, Latency = 50%: ```sh CPU_score = (0.90 / 0.75) = 1.20 Memory_score = (0.85 / 0.80) = 1.0625 QPS_score = (0.80 / 0.70) = 1.1429 Latency_score = (0.50 / 0.65) = 0.7692 node_load = (1.20*0.4 + 1.0625*0.3 + 1.1429*0.2 + 0.7692*0.1) / 1.0 = 1.10 ``` **Вывод из вышеуказанных данных: Перегрузка свыше 1.0 → нужно масштабирование** **Алгоритм прогнозирования нагрузки(линейная регрессия)** Используется метод наименьших квадратов для прогноза нагрузки на следующий период: ```sh slope = (n*Σxy - Σx*Σy) / (n*Σx² - (Σx)²) predicted = avg(y) + slope ``` Где: * x - временные шаги (0, 1, 2, ...) * y - исторические значения нагрузки * n - размер окна (по умолчанию 10 точек) **Пример прогнозирования:** ```sh История нагрузки: [0.65, 0.68, 0.72, 0.75, 0.78, 0.82, 0.85, 0.88, 0.91, 0.95] Тренд (slope) = ~0.033 Прогноз = 0.95 + 0.033 = 0.983 ``` **Алгоритм определения количества узлов** Добавление новых узлов в кластер (Scale Up) рассчитывается по формулам: ```sh excess = current_load - scale_up_threshold ratio = excess / scale_up_threshold nodes_to_add = ceil(ratio * current_nodes) ``` Пример: ```sh current_load = 1.25, threshold = 0.75, current_nodes = 4 excess = 1.25 - 0.75 = 0.50 ratio = 0.50 / 0.75 = 0.667 nodes_to_add = ceil(0.667 * 4) = ceil(2.668) = 3 ``` Удаление новых узлов из кластера (Scale Up) рассчитывается по формулам: ```sh deficit = scale_down_threshold - current_load ratio = deficit / scale_down_threshold nodes_to_remove = ceil(ratio * (current_nodes - 1)) ``` **Алгоритм стабилизации (Anti-Flapping)** Данный алгоритм необходим для предотвращения частых переключений масштабирования, основывается на **Скользящей средней** ```sh stabilized_load = Σ(load[i]) / N, где N = stabilization_window / evaluation_interval ``` **Алгоритм выбора узлов для удаления (при Scale Down)** При "масштабировании вниз"(при автоматическом удалении узлов из кластера) узлы выбираются согласно их приоритету: ```sh priority_for_removal = f( node.load, // чем ниже нагрузка, тем выше приоритет node.age, // чем новее узел, тем выше приоритет node.replica_count // чем меньше реплик, тем ниже приоритет ) score_for_removal = (1 - node.load) * 0.6 + (node_uptime_hours / max_uptime) * 0.4 ``` **Краевые условия и ограничения** ```sh // Границы кластера min_nodes = 1 // минимум 1 узел max_nodes = 10 // максимум 10 узлов // Ограничения на изменение max_scale_up_nodes = 3 // за раз можно добавить не более 3 узлов max_scale_down_nodes = 2 // за раз можно удалить не более 2 узлов ``` **Примеры работы алгоритма** **Пример 1: Нагрузка растёт** ```sh T=0: nodes=4, load=0.65, прогноз=0.68 → NoChange T=30: nodes=4, load=0.72, прогноз=0.78 → NoChange T=60: nodes=4, load=0.80, прогноз=0.85 → NoChange T=90: nodes=4, load=0.88, прогноз=0.94 → ScaleUp(+2 узла) T=120: nodes=6, load=0.78, прогноз=0.81 → NoChange (стабилизация) ``` **Пример 2: Нагрузка падает** ```sh T=0: nodes=6, load=0.45, прогноз=0.42 → NoChange T=30: nodes=6, load=0.38, прогноз=0.35 → NoChange T=60: nodes=6, load=0.32, прогноз=0.29 → ScaleDown(-1 узел) T=90: nodes=5, load=0.35, прогноз=0.33 → NoChange (стабилизация) ``` **Преимущества алгоритма PHA:** 1. Проактивность - прогнозирование предотвращает перегрузку до её наступления 2. Стабильность - moving average и cooldown предотвращают "маятник" 3. Гибкость - разные веса метрик позволяют адаптироваться под разные нагрузки 4. Безопасность - границы min/max и лимиты на скорость изменения 5. Эффективность - пропорциональное масштабирование (чем выше перегрузка, тем больше узлов) ```sh # Просмотр статуса кластера (режим лидера) futriix:~> status === Cluster Status === ✓ Role: LEADER Cluster Name: production Node: 192.168.1.100:8080 Raft Port: 7000 Cluster created: 2026-01-15 10:30:45.123 Leader since: 2026-01-15 12:15:22.456 Elections: 3 Health: healthy Last health check: 2026-01-15 15:30:00.000 # В режиме follower futriix:~> status === Cluster Status === ⚠ Role: FOLLOWER Cluster Name: production Node: 192.168.1.101:8080 Raft Port: 7000 Cluster created: 2026-01-15 10:30:45.123 Leader: 192.168.1.100:8080 Joined cluster: 2026-01-15 10:31:12.789 Last heartbeat: 2026-01-15 15:29:58.456 Health: healthy # Просмотр всех узлов кластера (с временными метками) futriix:~> nodes === Cluster Nodes === * 192.168.1.100:8080 (LEADER) Joined: 2026-01-15 10:30:45.123 Last seen: 2026-01-15 15:29:59.001 Status: active Uptime: 5h 0m 14s 192.168.1.101:8080 (FOLLOWER) Joined: 2026-01-15 10:31:12.789 Last seen: 2026-01-15 15:29:58.456 Status: active Uptime: 4h 59m 46s 192.168.1.102:8080 (FOLLOWER) Joined: 2026-01-15 10:31:45.012 Last seen: 2026-01-15 15:29:57.234 Status: syncing Uptime: 4h 59m 12s # Подробная информация об узле futriix:~> node info 192.168.1.100:8080 === Node Information === ID: node-001 IP: 192.168.1.100 Port: 8080 Raft Port: 7000 Status: active Role: LEADER Timestamps: Created: 2026-01-15 10:30:45.123 Joined: 2026-01-15 10:30:45.123 Last seen: 2026-01-15 15:29:59.001 Leader since: 2026-01-15 12:15:22.456 Metrics: Uptime: 5h 0m 14s Elections: 3 Request count: 15234 Bytes received: 12.5 MB Bytes sent: 45.2 MB # Проверка здоровья кластера futriix:~> cluster health === Cluster Health === Overall score: 95.5 Checked at: 2026-01-15 15:30:00.000 Recommendation: Cluster is healthy, all systems operational Nodes: 192.168.1.100:8080 - active (latency: 1ms, last success: 15:29:59.001) 192.168.1.101:8080 - active (latency: 2ms, last success: 15:29:58.456) 192.168.1.102:8080 - syncing (latency: 5ms, last success: 15:29:57.234) ```(К началу)
## Backpressure Для равномерной загрузки каждого узла кластера, в субд futriix применяется механизм **"backpressure**. **Backpressure** (Обратное давление) — это механизм управления потоками данных, предотвращающий переполнение узла кластера, когда он не успевает обрабатывать поступающие события. В нашем проекте данный механизм реализован для защиты системы от резких пиковых нагрузок: если буфер входящих сообщений достигает заданного порога, источник данных автоматически замедляется или приостанавливается до тех пор, пока потребитель не освободит ресурсы. Это гарантирует стабильность работы, отсутствие потерь данных и отказ от бесконечного накопления задач в очереди. **Математическая формула вероятностного отклонения в Backpressure** В системе Backpressure используется адаптивная вероятностная модель для отклонения запросов при перегрузке, которое рассчитывается по следующей формуле: ```sh P_reject = f(level) × g(load) × h(time) Где: * P_reject — итоговая вероятность отклонения запроса (0.0 - 1.0) * f(level) — коэффициент на основе уровня перегрузки * g(load) — коэффициент на основе текущей нагрузки * h(time) — коэффициент на основе времени (для защиты от "thundering herd") ``` **Компоненты формулы** **Коэффициент уровня перегрузки f(level)** ```sh f(level) = { 0.00, если level = None 0.00, если level = Low (только задержка) 0.30, если level = Medium 0.70, если level = High 0.90, если level = Critical } ``` **Коэффициент нагрузки g(load)** ```sh g(load) = (cpu_usage + memory_usage + queue_factor + connection_factor) / 4 где: cpu_usage = current_cpu / cpu_threshold memory_usage = current_memory / memory_threshold queue_factor = min(queue_size / queue_threshold, 1.0) connection_factor = min(connections / connection_threshold, 1.0) ``` **Коэффициент времени h(time) (экспоненциальное сглаживание)** ```sh h(time) = 1 - e^(-λ × Δt) где: λ = 0.1 (константа скорости затухания) Δt = время с последнего отклонения в секундах ``` **Итоговая формула вероятности отклонения** ```sh P_reject = f(level) × g(load) × (1 - e^(-0.1 × Δt)) ``` **Формула задержки (для уровня Low)** ```sh D = D_base × (1 + α × load_factor) где: D_base = 100ms (базовая задержка) α = 2.0 (коэффициент усиления) load_factor = (cpu_usage + memory_usage) / 2 ``` **Графическое представление** ```sh Вероятность отклонения P_reject | 1.0 | * * * Critical (90%) | * 0.9 | * High (70%) | * 0.7 | * Medium (30%) | * 0.5 | * | * 0.3 | * Low (0% - только задержка) | * 0.1 |* |_____________________________ Нагрузка 0 0.2 0.4 0.6 0.8 1.0 ``` #### Пример рассчёта **Исходные данные:** * Уровень: High → f(level) = 0.70 * CPU: 85% → cpu_usage = 0.85/0.80 = 1.0625 * Memory: 75% → memory_usage = 0.75/0.85 = 0.882 * Queue: 8000/10000 = 0.8 * Connections: 4000/5000 = 0.8 * Время с последнего отклонения: 2 секунды **Рассчёт** ```sh load_factor = (1.0625 + 0.882 + 0.8 + 0.8) / 4 = 0.886 time_factor = 1 - e^(-0.1 × 2) = 1 - e^(-0.2) = 1 - 0.819 = 0.181 P_reject = 0.70 × 0.886 × 0.181 = 0.112 = 11.2% ``` **Результат: ~11% запросов будут отклонены.** **Преимущества формулы** 1. **Адаптивность** — реагирует на изменение нагрузки в реальном времени 2. **Сглаживание** — предотвращает резкие скачки отклонений 3. **Самовосстановление** — при снижении нагрузки вероятность автоматически уменьшается 4. **Предсказуемость** — поведение системы становится детерминированным и предсказуемым(К началу)
## Геораспределенная миграция **Механизм кросс-датацентровой миграции** Кросс-датацентровая миграция данных позволяет переносить данные между географически распределенными кластерами futriix без остановки работы системы. Миграция основана на асинхронной репликации с использованием CDC (Change Data Capture) и устойчивой очереди изменений. Миграция данных между датацентрами реализована на основе асинхронной репликации с **CDC (Change Data Capture)** и включает следующие ключевые компоненты: 1. Change Queue — устойчивая очередь изменений (до 100 000 записей), сохраняемая на диск. Каждое изменение получает уникальный LSN (Log Sequence Number) для отслеживания прогресса. 2. Checkpoint System — система чекпоинтов, позволяющая возобновить миграцию с места остановки. Чекпоинты сохраняются каждые 30 секунд и содержат информацию о последнем LSN и обработанных документах. 3. Delta Sync — после основной миграции система продолжает синхронизировать изменения, произошедшие во время миграции, обеспечивая консистентность данных. 4. Валидация — после завершения миграции выполняется выборочная проверка данных (по умолчанию 10% документов) с использованием SHA-256 контрольных сумм. **Жизненный цикл миграции:** ```sh Idle → Preparing → Migrating → Delta Sync → Validating → Completed ``` **Режимы работы:** 1. manual — полностью ручное управление 2. semi_auto — основная миграция вручную, дельта-синхронизация автоматическая 3. auto — полностью автоматическая непрерывная миграция ### Команды управления: * migration start(К началу)
## Ограничения В субд `futriix` также как и в традиционных субд, есть механизм **Ограничений (Constraints)**. **Ограничения (Constraints)**-это декларативные гарантии целостности (правила), действующие локально на каждом узле и согласующиеся через оркестратор SAGA. Они обеспечивают, что каждый шаг распределённого сценария сохраняет инварианты данных; конфликты и нарушения фиксируются до фиксации шага, а при необходимости компенсируются в рамках eventual consistency. Ограничения на уровне коллекции поддерживают: `обязательные поля (required)`, `уникальность (unique)`, `минимальные/максимальные значения (min/max)`, `regex-паттерны` и `enum-списки`(фиксированные наборы допустимых значений, которые жёстко задают «разрешённые состояния» поля. Они работают как "строгая шпаргалка" для данных: нельзя сохранить ничего лишнего, а движок сразу фиксирует несоответствия — ещё **до попадания в WAL** и **до участия в MVCC‑снапшотах**.) допустимых значений. Все ограничения проверяются автоматически при вставке и обновлении документов. ```sh # Добавление обязательного поля futriix:~> add required employees email ✓ Required field 'email' added to collection 'employees' # Добавление ограничения уникальности futriix:~> add unique employees phone ✓ Unique constraint added for field 'phone' on collection 'employees' # Добавление минимального значения futriix:~> add min employees age 18 ✓ Min constraint added for field 'age' on collection 'employees' (min: 18.00) # Добавление максимального значения futriix:~> add max employees age 65 ✓ Max constraint added for field 'age' on collection 'employees' (max: 65.00) # Добавление enum-ограничения (допустимые значения) futriix:~> add enum employees status active,inactive,on_leave ✓ Enum constraint added for field 'status' on collection 'employees' (allowed: [active inactive on_leave]) ```(К началу)
## Импорт-Экспорт Для создания бекапов, в субд существуют команды `export` и `import`, позволяющие выгружать/загружать целые базы данных в формате **MessagePack**. Экспорт сохраняет документы с метаданными (версии, временные метки), импорт поддерживает пропуск существующих документов и детальную статистику. ```sh # Экспорт базы данных в файл MessagePack futriix:~> export "company" "company_backup.msgpack" ✓ Database 'company' exported to company_backup.msgpack # Экспорт с автоматическим добавлением расширения futriix:~> export "shop" "shop_backup" ✓ Database 'shop' exported to shop_backup.msgpack # Импорт базы данных из файла futriix:~> import "company" "company_backup.msgpack" Importing data from company_backup.msgpack to database 'company'... ✓ Database 'company' imported successfully from company_backup.msgpack Collections imported: 2 Documents imported: 150 Documents skipped (already exist): 0 Documents failed: 0 # Импорт в новую базу данных futriix:~> import "company_restore" "company_backup.msgpack" Created database 'company_restore' ✓ Database 'company_restore' imported successfully from company_backup.msgpack Collections imported: 2 Documents imported: 150 ```(К началу)
## HTTP API СУБД Futriix предусмотрен удобный сетевой интерфейс для интеграции с веб‑приложениями — это RESTful API, который покрывает все основные операции над данными и служебными объектами системы. API спроектирован с учётом требований современной веб‑разработки: * Поддержка CORS позволяет выполнять запросы к СУБД из браузерных приложений, размещённых на других доменах, без проблем с политикой безопасности браузеров.(К началу)
## 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 у движка) загрузка прерывается с соответствующим сообщением.(К началу)
## Контроль доступа В futriix реализована многоуровневая система контроля доступа, основанная на **ACL (Access Contol Lists -Списки контроля доступа**) с аутентификацией по сессиям. В ней поддерживаются следующие роли (read, write, delete, admin) с гранулярным контролем на уровне базы данных и коллекции. ```sh # Вход в систему futriix:~> acl login admin admin ✓ Logged in as 'admin' with role 'admin' # Выход из системы futriix:~> acl logout ✓ Logged out # Назначение прав доступа (после входа как admin) futriix:~> acl login admin admin ✓ Logged in as 'admin' with role 'admin' futriix:~> use company ✓ Switched to database 'company' # Назначение прав на чтение futriix:~> acl grant employees reader r ✓ Permissions 'r' granted to role 'reader' on collection 'employees' # Назначение прав на чтение и запись futriix:~> acl grant employees editor rw ✓ Permissions 'rw' granted to role 'editor' on collection 'employees' # Назначение полных прав (администратор коллекции) futriix:~> acl grant employees admin rwda ✓ Permissions 'rwda' granted to role 'admin' on collection 'employees' # Пример использования разных прав: # r - read (чтение) # w - write (запись) # d - delete (удаление) # a - admin (администратор) ```(К началу)
## 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 у движка) загрузка прерывается с соответствующим сообщением.(К началу)
## Триггеры **Триггер**— это действие в базе данных, автоматически запускаемое при добавлении, изменении или удалении записи. Чаще всего триггеры нужны для математических вычислений, а также проведения аудита (для автоматической записи в лог или базу данных действий, которые необходимо отслеживать в рамках проведения аудита.)(К началу)
## Сжатие данных Сжатие данных в СУБД futriix, использует алгоритм компрессии **Brotli**, предназначенного для уменьшения объёма хранимых документов в оперативной памяти и на жёстком диске, что позволяет эффективнее использовать доступные ресурсы при работе с большими объёмами информации. Алгоритм Brotli, разработанный компанией Google, обеспечивает сжатие с коэффициентом, на 20–26% лучшим по сравнению с классическим Gzip при сопоставимой скорости распаковки, что делает его оптимальным выбором для систем с интенсивными операциями чтения. Основные преимущества Brotli включают: использование предопределённого словаря часто встречающихся последовательностей байт, адаптивное кодирование с переменной длиной кода, поддержку 11 уровней сжатия (от быстрого до максимально плотного) и высокую скорость распаковки, критически важную для быстрого доступа к документам. В futriix сжатие применяется автоматически при превышении порогового размера документа (настраивается через compression.MinSize), при этом каждый документ хранит флаг Compressed и оригинальный размер для последующего контроля эффективности. ```sh # Просмотр конфигурации сжатия futriix:~> compression config === Compression Configuration === Enabled: true Algorithm: snappy Level: 3 Min Size: 1 KB Available Algorithms: snappy - Fast compression/decompression, good balance (default) lz4 - Extremely fast, lower compression ratio zstd - High compression ratio, slower # Просмотр статистики сжатия futriix:~> compression stats === Compression Statistics === Total Documents: 1250 Compressed Documents: 890 Compression Rate: 71.20% Size Reduction: 45.30% Original Size: 15.2 MB Compressed Size: 8.3 MB Algorithm: snappy Compression Level: 3 Min Size Threshold: 1 KB # Ручное сжатие коллекции futriix:~> compress collection employees Compressing collection 'employees'... ✓ Compressed 45 documents in collection 'employees' # Просмотр информации о сжатии документа futriix:~> doc compression employees 550e8400-e29b-41d4-a716-446655440000 === Compression Info for Document: 550e8400-e29b-41d4-a716-446655440000 === Compressed: true Ratio: 35.20% Original Size: 2.5 KB Current Size: 1.6 KB ```(К началу)
## Графический интерфейс В субд futriix для упрощения администрирования реализован **WUI (Web User Inerface) Веб-интерфейс**, с помощью которого через веб-браузер можно управлять субд, быстро просто и удобно. На фото ниже, приведён пример загруженного веб-интерфейса субд по умолчанию.
(К началу)
## План развития - [x] Реализовать поддержку хранимых процедур - [x] Реализовать поддержку триггеров (обратных вызовов) - [x] Реализовать поддержку многопоточности - [x] Реализовать неблокирующие чтение/запись - [x] Реализовать неблокирующие транзакции - [x] Реализовать constraints (Ограничения) - [x] Реализовать мульти-мастер асинхронную репликацию через файл конфигурации - [x] Реализовать логирование для субд - [x] Реализовать поддержку синхронной мастер-мастер репликации - [x] Реализовать базовую поддержку протокола Raft - [x] Реализовать поддержку индексов (первичные индексы, вторичные индексы) - [x] Реализовать поддержку протокола MessagePack - [x] Написать базовые тесты (интеграционный, функциональный, регрессионный, нагрузочный, smoke-тест для ядра субд "futriix") - [x] Добавить механизм плагинов на языке lua, загружаемых в субд при её запуске, расширяющих её базовый функционал, не изменяя исходный код субд - [x] Реализовать поддержку HTTP-restfull API - [x] Реализовать сжатия данных в субд на основании протокола "Brotli" - [x] Реализовать импорт и экспорт дампа субд в формате "MessagePack" - [x] Исправить ошибки записи журнала логов (в журнал лога кроме текущего времени добавить текущий год) - [x] Реализовать веб-интерфейс (WUI),на основе самописного движка "futriis", с помощью которого пользователь сможет управлять субд через веб-браузер - [x] В веб-интерфейсе, слева от надписи "admin" в левой нижней части экрана, реализовать возможность добавлять фото (маленького размера) - [x] В веб-интерфейсе удалить "вшитые в исходный код" логин и пароль (admin; admin) - [x] В веб-интерфейсе, реализовать возможность смены логина и пароля для авторизации в нём, при этом логин и пароль (по умолчанию admin; admin) храни в скрытом файле ".credentials", расположенном в каталоге "futriix" - [x] В веб-интерфейс добавить возможность читать файл логов "futriix.log", в котором отображаются все операции, выполненные в веб-интерфейсе за сеанс (например, создана база данных, или удалена коллекция), включая те операции, которые не были выполнены в виду какой-либо ошибки - [x] В веб-интерфейсе, добавить возможность добавления нового пользователя администратора, данные которого (логин и пароль) будут хранится в скрытом файле ".credentionals", расположенном в каталоге "futriix". - [x] В веб-интерфейсе, добавить возможность управлять плагинами (включать, отключать) - [x] Скрипты сборки "build.sh" и "vendor_build.sh" переписаны таким образом, чтобы проект не зависел от компилятора "gcc", т.е. напиши реализацию так чтобы его не нужно было устанавливать отдельно в операционной системе "OpenIndiana Hipster" - [x] Библиотека "raft-boltdb" заменить на встроенное файловое хранилище - [x] Реализовать уникальные и составные индексы - [x] Реализовать временные метки для основных объектов субд (таппл, коллекция, документ, поле, индекс, транзакция, ACL, узел кластера) - [x] Оптимизировать транзакции под высокие нагрузки (Реализовать: Пакетную запись WAL - вместо синхронной записи каждой операции, используется асинхронная запись пакетами по 100 записей, Буферизацию записей - 64KB буфер для WAL записей, Периодическую синхронизацию - синхронизация с диском каждые 5 секунд вместо каждой записи, Чекпоинты - периодическое создание контрольных точек состояния, Атомарные операции - использование atomic.Value и sync.Map для wait-free доступа) - [x] Реализовать MVCC версионирование - поддержка множественных версий документов - [x] Реализовать временные метки для объектов субд (Ограничений, Импорта-Экспорта, Триггеров, Lua-плагинов) - [x] Реализовать обработку состояний "split-brain" - [x] Реализовать Pipeline репликации-группировку нескольких команд в один Raft лог для уменьшения сети - [x] Реализовать Batch commit- коммит нескольких операций за один цикл, чтобы снизить fsync - [x] Реализовать Динамическое перераспределение шардов-автоматический re-sharding при добавлении новых узлов в кластер - [x] Реализовать Observability (Наблюдаемость)- в простейшем варианте через веб-интерфейс - [x] Реализовать "Joint consensus" (безопасная смена конфигурации кластера) - [x] Реализовать логи с уровнями (DEBUG/INFO/WARN/ERROR) для отслеживания событий - [x] Реализовать Segmented WAL - разбивка WAL на сегменты по 64MB с автоматической ротацией - [x] Реализовать Parallel Recovery - параллельное восстановление транзакций с worker pool (4 воркера) - [x] Реализовать WAL Index - индекс для быстрого поиска записей по LSN - [x] Реализовать Checksum per record - CRC32 с включением LSN в контрольную сумму - [x] Реализовать Visibility Map - битовая карта видимости версий для быстрых snapshot read - [x] Реализовать Version Pruning Policy - автоматическая очистка старых версий (настраиваемая политика) - [x] Реализовать Read Timestamp Cache - кэш для быстрого доступа к версиям по timestamp - [x] Реализовать Gap Detection - обнаружение пропусков в версиях документов - [x] Реализовать Distributed Transactions - двухфазный коммит для распределённых транзакций - [x] Реализовать Deadlock Detection - циклический детектор дедлоков с таймаутами (DFS по графу ожидания) - [x] Реализовать Transaction Timeout - настраиваемый таймаут для долгих транзакций - [x] Реализовать Savepoints - точки сохранения внутри транзакции для частичного отката - [x] Вынести настройи настройки MVCC,WAL и настройки плагинов из исходного кода проекта в файл конфигурации "config.toml" - [x] Реализовать выделение жирным шифтом все информационные сообщения в скриптах сборки - [x] Реализовать ограничения на количество создаваемых Lua-состояний в плагинах - [x] Реализовать WAL recovery сделать асинхронным для ускорения скорости работы субд - [x] Реализовать SAGA c оркестратором (Распределённые транзакции) - [x] Голосование узлов перед коммитом в протоколе Raft - [x] Реализовать автоматическое масштабирование - [x] Реализовать TLS, backpressure - [x] Реализовать runtime-ограничения для коллекций, миграции схемы - [x] Реализовать систему "горячих бекапов" - [x] Реализовать скрипт установки Golang в операционных системах на базе Illumos - [x] Реализовать Автоматический шардинг - [x] Реализовать TLS для межузлового общения - [x] Реализовать валидацию сертификатов при взаимной аутентификации и ротации ключей шифрования (в простейшем виде) - [x] Реализовать backpressure (механизм контроля перегрузки кластера), работающего при перегрузке - [x] Реализовать ограничения на размер коллекции/документа в рантайме - [x] Реализовать Автоматическое масштабирование (перед реализацией кратко расскажи, на каком алгоритме оно будет основано и как будет работать) - [x] Реализовать Прозрачное обновление схемы данных - [x] Реализовать в системе бекапов только "планировщик бэкапов" и "инкрементальность (на основе WAL)" - [x] Реализовать механизм плагинов, с помощью которого можно на языке lua реализовать движки для субд, не изменяя её исходный код - [x] Реализовать атомарную запись через временный файл (Обеспечение целостности чекпоинтов (снапшотов) базы данных) - [x] Реализовать контрольную сумму SHA-256 для чекпоинтов - [x] Реализовать блокировку при восстановлении - [x] Реализовать проверку целостности при загрузке - [x] Реализовать вывод информации о восстановлении - [x] Реализовать блокировку при восстановлении - [x] Реализовать очистку WAL сегментов после бэкапа - [x] Реализовать Проверку версии бэкапа - [x] Реализовать статусы бэкапов (pending-running-completed-failed) - [x] Реализовать атомарнуя запись бэкапов (Обеспечение целостности файлов бэкапов) - [x] Реализована оптимизация ядра субд (удалён файл "/internal/storage/storage.go", методы "Storage" и "NewStorage" вынесены в "/internal/storage/engine.go") - [x] Реализация записи "пакетами записи" для WAL-журнала (flushBatch) - [x] Реализовать геораспределённую мигацию, между двумя датацентрами, размещёнными в одной стране и одном городе - [x] Реализовать Multi-Raft (Несколько независимых Raft групп для разных шардов) - [x] Реализовать Gossip Protocol (Протокол сплетен) (механизм автоматического обнаружения узлов в кластере, обмен информацией между узлами нагрузка,health про протоколу UDP, поддержка seed-узлов для начального обнаружения, автоматическое обновление списка членов кластера) - [x] Реализовать Self-Healings (Механизмы самоисцеления) (Автоматический мониторинг состояния всех узлов кластера,Обнаружение сбоев с пороговым значением (Failure Threshold, автоматическое восстановление: перезапуск, переподключение, перебалансировка, отслеживание истории восстановлений и сбор метрик) - [x] Реализовать Dynamic Configuration (Динамическая конфигурация) (централизованное хранение конфигурации в Raft-логе, изменение параметров на лету без перезапуска узлов, версионирование и история изменений, подписка на изменения для компонентов системы) - [ ] Реализовать (Linearizable чтения) чтения через лидера с кворумом для строгой консистентности в кластере - [ ] Реализовать Learner узлы узлы для репликации без права голоса (для бэкапов) - [ ] Реализовать полноценную поддержку алгоритма Raft (с автоматическим перевывыбором лидера, с доменом отказа) - [ ] Реализовать геораспределённую миграцию (Региональные зоны Поддержка нескольких availability zones с приоритетом выбора,Чтение из ближайшей реплики Маршрутизация запросов к географически ближайшему узлу,Асинхронная межрегиональная репликация Снижение задержек для глобальных кластеров) - [ ] Реализовать Read-only лидер (Оптимизация запросов на чтение через локальные индексы) - [ ] Реализовать Snapshot streaming (Передача снапшотов через отдельный канал без блокировки) - [ ] Интеграцию с мониторинговыми системами (Prometheus, Grafana) - [ ] Реализовать полноценную систему бекапирования с возможностью определения корректности созданного бекапа и кроссдацентровых решений по автоматическому копироваю бекапа в другой дацентр - [ ] Реализовать коннекторы к современным языкам программирования (C, C++, Java, Python, Go) - [ ] Реализовать утилиту тестирования сервера на количество запросов на чтение/запись См. [Открытые проблемы](https://source.futriix.ru/gvsafronov/futriixw/issues) полный список предлагаемых функций (и известных проблем).(К началу)
## Контакты По вопросам эксплуатации, производительности и консультации по настройки конфигурации, просьба обращаться по контактам указанными ниже в данном разделе.(К началу)