25 Commits

Author SHA1 Message Date
7ce6c907dd Update README.md 2026-08-22 19:53:33 +00:00
f7c78d3d7c Update README.md 2026-08-22 19:50:08 +00:00
d1aadacfdc Update README.md 2026-08-22 19:49:00 +00:00
a8ca85d311 Update README.md 2026-08-22 19:46:22 +00:00
4e5a8837ba Update README.md 2026-08-22 19:45:26 +00:00
04104e07d7 Update README.md 2026-08-22 19:41:16 +00:00
43c870a92e Update README.md 2026-08-22 19:40:09 +00:00
c252f462e7 Update README.md 2026-08-22 19:36:26 +00:00
755919fd29 Update README.md 2026-08-22 19:35:41 +00:00
601af066c1 Update README.md 2026-08-22 19:34:28 +00:00
7edeab3b10 Update README.md 2026-08-21 22:10:10 +00:00
c0f6146308 Upload files to "internal/storage" 2026-08-19 17:50:05 +00:00
d46c3fa730 Delete internal/storage/fsync.go 2026-08-19 17:49:32 +00:00
46aa587a10 Delete internal/migration/11schema_migrator.go 2026-08-17 21:06:55 +00:00
49daa6aa76 Upload files to "internal/migration" 2026-08-17 21:06:43 +00:00
b12689bea0 Update internal/migration/11schema_migrator.go 2026-08-17 21:06:14 +00:00
2982bec7ae Upload files to "internal/storage" 2026-08-17 21:05:00 +00:00
362f3d6d71 Delete internal/storage/persistence.go 2026-08-17 21:04:30 +00:00
51c5119194 Update cmd/futriis/main.go 2026-08-12 20:37:26 +00:00
2f3d1450d8 Update README.md 2026-08-07 20:13:38 +00:00
dd9b9353f1 Update README.md 2026-08-07 20:12:33 +00:00
a769f72993 Update README.md 2026-08-07 20:10:15 +00:00
bc10d7af53 Update README.md 2026-08-07 20:08:19 +00:00
6b30d43a45 Update README.md 2026-08-07 20:07:09 +00:00
4e053c1dfc Update README.md 2026-08-07 20:06:37 +00:00
5 changed files with 141 additions and 23 deletions

View File

@@ -31,6 +31,7 @@
<li><a href="#о-проекте">О проекте</a></li> <li><a href="#о-проекте">О проекте</a></li>
<li><a href="#лицензия">Лицензия</a></li> <li><a href="#лицензия">Лицензия</a></li>
<li><a href="#глоссарий">Глоссарий</a></li> <li><a href="#глоссарий">Глоссарий</a></li>
<li><a href="#почему-illumos-openindiana">Почему Illumos OpenIndiana?</a></li>
<li><a href="#архитектурные-примечания-и-предложения-безопасности">Архитектурные примечания и предложения безопасности</a></li> <li><a href="#архитектурные-примечания-и-предложения-безопасности">Архитектурные примечания и предложения безопасности</a></li>
<li><a href="#алгоритмы-и-структуры-данных">Алгоритмы и структуры данных</a></li> <li><a href="#алгоритмы-и-структуры-данных">Алгоритмы и структуры данных</a></li>
<li><a href="#системные-требования">Системные требования</a></li> <li><a href="#системные-требования">Системные требования</a></li>
@@ -52,6 +53,7 @@
<li><a href="#триггеры">Триггеры</a></li> <li><a href="#триггеры">Триггеры</a></li>
<li><a href="#сжатие-данных">Сжатие данных</a></li> <li><a href="#сжатие-данных">Сжатие данных</a></li>
<li><a href="#графический-интерфейс">Графический интерфейс</a></li> <li><a href="#графический-интерфейс">Графический интерфейс</a></li>
<li><a href="#чаво">ЧАВО</a></li>
<li><a href="#план-развития">План развития</a></li> <li><a href="#план-развития">План развития</a></li>
<li><a href="#контакты">Контакты</a></li> <li><a href="#контакты">Контакты</a></li>
</ol> </ol>
@@ -173,6 +175,24 @@ futriix — это лёгкая распределённая NoSQLСУБД н
<p align="right">(<a href="#readme-top">К началу</a>)</p> <p align="right">(<a href="#readme-top">К началу</a>)</p>
## Почему Illumos OpenIndiana?
Несмотря на то, что `futriix` сохраняет кроссплатформенную совместимость и полностью поддерживает Linux, в качестве основной целевой платформы выбрана операционная система **Illumos (OpenIndiana)**. Для резидентных (in-memory) NoSQL-СУБД, эксплуатируемых в Замкнутых Программных Средах (ЗПС), ОС общего назначения (такие как Linux) часто вносят недетерминированные задержки (ресурсный джиттер) и обладают избыточным архитектурным оверхедом. Родословная Solaris в Illumos обеспечивает критически важный фундамент, построенный на строгом детерминизме и эталонном системном проектировании.
**Ключевые архитектурные причины приоритета ядра Illumos:**
* **Строгий детерминизм ядра и планирование реального времени:** В отличие от планировщика Linux (CFS/EEVDF), оптимизированного под компромисс между пропускной способностью и интерактивностью, диспетчер задач Illumos гарантирует жесткое и предсказуемое распределение ресурсов процессора. Это исключает внезапные задержки (jitter), критические для wait-free/lock-free алгоритмов обработки транзакций в памяти.
* **Масштабируемые порты событий (`port_create`):** Для организации асинхронного высоконагруженного ввода-вывода `futriix` задействует системный интерфейс Event Ports вместо Linux `epoll`. Архитектурное преимущество портов событий заключается в нативном устранении проблемы «эффекта проснувшегося стада» (thundering herd) на уровне ядра и лучшей локальности процессорного кэша при работе с пулами потоков.
* **Нативная архитектура управления сбоями (FMA):** Эксплуатация СУБД типа in-memory сопряжена с рисками аппаратных сбоев памяти. Подсистема Illumos FMA непрерывно анализирует телеметрию оборудования: если планка оперативной памяти начинает деградировать, FMA изолирует сбойные страницы «на лету» *до* того, как произойдет Kernel Panic, обеспечивая непрерывную доступность СУБД.
* **Эталонная целостность WAL через нативную ZFS:** Сегментированный лог упреждающей записи (WAL) требует абсолютной надежности хранения. В Illumos файловая система ZFS является родной и глубоко интегрирована в слой виртуальной памяти ядра. Она верифицирует блоки данных с помощью криптографических контрольных сумм и автоматически исправляет скрытые повреждения бит (*silent data corruption*), гарантируя надежность (Durability) без потери производительности.
* **Жесткая изоляция на уровне ОС (Zones):** Технология контейнеризации Zones «вшита» непосредственно в ядро системы, в отличие от контейнеров Linux, реализованных в виде надстройки через пространства имен (namespaces) и cgroups. Это обеспечивает чистую, математически строгую логическую и физическую изоляцию распределенных узлов СУБД, запущенных на общем оборудовании.
* **Лицензия CDDL для применения в ЗПС:** Лицензия Common Development and Distribution License (CDDL) позволяет модифицировать и усиливать безопасность ядра ОС под требования отечественных регуляторов без необходимости раскрывать производный код (в отличие от жестких копилефт-требований GPL в Linux). Это радикально упрощает создание доверенных, сертифицированных программно-аппаратных комплексов.
<p align="right">(<a href="#readme-top">К началу</a>)</p>
## Архитектурные примечания и предложения безопасности ## Архитектурные примечания и предложения безопасности
> [!IMPORTANT] > [!IMPORTANT]
@@ -215,7 +235,7 @@ futriix — это лёгкая распределённая NoSQLСУБД н
|----------|----------|----------| |----------|----------|----------|
| **Хранилище (Storage)** | `sync.Map` | Конкурентная хэш‑таблица для хранения баз данных, обеспечивает waitfree чтение и запись | | **Хранилище (Storage)** | `sync.Map` | Конкурентная хэш‑таблица для хранения баз данных, обеспечивает waitfree чтение и запись |
| | Атомарные счётчики (`atomic.Int64`) | Для отслеживания общего количества документов без блокировок | | | Атомарные счётчики (`atomic.Int64`) | Для отслеживания общего количества документов без блокировок |
| **База данных (Database)** | `sync.Map` коллекций | Аналог слайса в реляционных СУБД | | **База данных (Database)** | `sync.Map` коллекций | Синоним термнина "база данных" в реляционных СУБД |
| | Мьютексы (`sync.RWMutex`) | Для операций изменения структуры (создание/удаление коллекций) | | | Мьютексы (`sync.RWMutex`) | Для операций изменения структуры (создание/удаление коллекций) |
| **Коллекция (Collection)** | `sync.Map` документов | Ключ: ID документа, значение: указатель на `Document` | | **Коллекция (Collection)** | `sync.Map` документов | Ключ: ID документа, значение: указатель на `Document` |
| | `sync.Map` индексов | Отдельное хранилище для инвертированных индексов | | | `sync.Map` индексов | Отдельное хранилище для инвертированных индексов |
@@ -2510,10 +2530,24 @@ futriix:~> doc compression employees 550e8400-e29b-41d4-a716-446655440000
<img src="wui.png" height=400 weight=400 alt="wui.png"></img> <img src="wui.png" height=400 weight=400 alt="wui.png"></img>
<p align="right">(<a href="#readme-top">К началу</a>)</p>
## ЧАВО
В данном разделе- **ЧАВО (Часто задаваемые Вопросы и Ответы)** собраны ответы на самые частые вопросы — чтобы вы быстро разобрались в работе проекта и не тратили время на поиск очевидных вещей. Если не нашли нужного ответа — напишите в `issues`: обратная связь помогает нам расставлять приоритеты.<br> Дальше — раздел «План развития»: там показаны, какие улучшения уже реализованы, а каких ждать в будущем.
**Вопрос: Почему версия обозначена как «Futriix 2 i²», а не просто «2.0»?**<br>
<br>
<br>
**Ответ: «2» — номер версии: здесь собраны ключевые компоненты (шардинг, WAL, MVCC и др.).**
**i2i2 — математическая метафора: хотя ii — мнимая единица, её квадрат даёт абсолютно точный результат (i2=1i2=1). Так и Futriix: даже в сложной распределённой среде с параллельными операциями (waitfree, lockfree) система выдаёт строго предсказуемые результаты — за счёт Raft, ACID и MVCC. Это близко философии OpenIndiana/Illumos: детерминизм и контроль над ресурсами.**
<p align="right">(<a href="#readme-top">К началу</a>)</p> <p align="right">(<a href="#readme-top">К началу</a>)</p>
## План развития ## План развития
- [x] Реализовать поддержку хранимых процедур - [x] Реализовать поддержку хранимых процедур

View File

@@ -85,7 +85,7 @@ func main() {
aclManager := acl.NewACLManager() aclManager := acl.NewACLManager()
logger.Info("ACL manager initialized") logger.Info("ACL manager initialized")
// Исправлено: передаём store как второй аргумент // Передаём store как второй аргумент
raftCoordinator, err := cluster.NewRaftCoordinator(cfg, store, logger) raftCoordinator, err := cluster.NewRaftCoordinator(cfg, store, logger)
if err != nil { if err != nil {
logger.Error("Failed to start Raft coordinator: " + err.Error()) logger.Error("Failed to start Raft coordinator: " + err.Error())
@@ -185,7 +185,7 @@ func main() {
node := cluster.NewNode(cfg.Cluster.NodeIP, cfg.Cluster.NodePort, store, logger) node := cluster.NewNode(cfg.Cluster.NodeIP, cfg.Cluster.NodePort, store, logger)
// Исправлено: объявляем переменную maxRetries здесь, чтобы она была доступна // Объявляем переменную maxRetries здесь, чтобы она была доступна
maxRetries := 5 maxRetries := 5
var registerErr error var registerErr error
for i := 0; i < maxRetries; i++ { for i := 0; i < maxRetries; i++ {
@@ -205,7 +205,7 @@ func main() {
os.Exit(1) os.Exit(1)
} }
// Исправлено: передаём отдельные параметры из конфигурации плагинов // Передаём отдельные параметры из конфигурации плагинов
pluginManager := plugin.NewPluginManager( pluginManager := plugin.NewPluginManager(
cfg.Plugins.ScriptDir, cfg.Plugins.ScriptDir,
logger, logger,

View File

@@ -225,6 +225,7 @@ func (sm *SchemaMigrator) createDefaultSchema() {
UpdatedAt: time.Now().UnixMilli(), UpdatedAt: time.Now().UnixMilli(),
} }
sm.currentVersion = "1.0.0"
sm.saveSchema() sm.saveSchema()
} }
@@ -388,7 +389,13 @@ func (sm *SchemaMigrator) MigrateSchema(targetVersion string) error {
} }
// Начинаем транзакцию для миграции // Начинаем транзакцию для миграции
// Используем storage.BeginTransaction() из пакета storage
tx := storage.BeginTransaction() tx := storage.BeginTransaction()
if tx == nil {
return fmt.Errorf("failed to begin transaction")
}
// Обработка паники и откат транзакции
defer func() { defer func() {
if r := recover(); r != nil { if r := recover(); r != nil {
storage.AbortCurrentTransaction() storage.AbortCurrentTransaction()
@@ -437,12 +444,17 @@ func (sm *SchemaMigrator) MigrateSchema(targetVersion string) error {
return fmt.Errorf("failed to save schema: %v", err) return fmt.Errorf("failed to save schema: %v", err)
} }
// Сохраняем применённые миграции
if err := sm.saveAppliedMigrations(); err != nil {
storage.AbortCurrentTransaction()
return fmt.Errorf("failed to save applied migrations: %v", err)
}
// Коммитим транзакцию // Коммитим транзакцию
if err := storage.CommitCurrentTransaction(); err != nil { if err := storage.CommitCurrentTransaction(); err != nil {
return fmt.Errorf("failed to commit schema migration: %v", err) return fmt.Errorf("failed to commit schema migration: %v", err)
} }
sm.saveAppliedMigrations()
sm.currentVersion = targetVersion sm.currentVersion = targetVersion
if sm.logger != nil { if sm.logger != nil {

View File

@@ -10,6 +10,7 @@
// Файл: internal/storage/fsync.go // Файл: internal/storage/fsync.go
// Назначение: Реальная синхронизация с диском (fsync) для WAL // Назначение: Реальная синхронизация с диском (fsync) для WAL
// Поддерживает все платформы: Linux, macOS, BSD, Windows, Solaris/Illumos (OpenIndiana)
package storage package storage
@@ -17,21 +18,29 @@ import (
"fmt" "fmt"
"os" "os"
"runtime" "runtime"
"syscall"
"time" "time"
) )
// RealFsync выполняет реальный fsync на файле // RealFsync выполняет реальный fsync на файле.
// Использует стандартный метод Sync() для максимальной совместимости
// со всеми платформами включая OpenIndiana (Solaris/Illumos).
func RealFsync(file *os.File) error { func RealFsync(file *os.File) error {
if file == nil { if file == nil {
return nil return nil
} }
// Вызываем системный fsync // Вызываем системный fsync через стандартный метод Sync
// Это работает на всех платформах: Linux, macOS, BSD, Windows, Solaris/Illumos
return file.Sync() return file.Sync()
} }
// RealFsyncWithRetry выполняет fsync с повторными попытками // RealFsyncWithRetry выполняет fsync с повторными попытками.
// Параметры:
// - file: файл для синхронизации
// - maxRetries: максимальное количество попыток
// - retryDelay: задержка между попытками
//
// Возвращает ошибку, если все попытки не удались.
func RealFsyncWithRetry(file *os.File, maxRetries int, retryDelay time.Duration) error { func RealFsyncWithRetry(file *os.File, maxRetries int, retryDelay time.Duration) error {
if file == nil { if file == nil {
return nil return nil
@@ -52,7 +61,8 @@ func RealFsyncWithRetry(file *os.File, maxRetries int, retryDelay time.Duration)
return lastErr return lastErr
} }
// FsyncDir синхронизирует директорию (для гарантии, что создание файла записано на диск) // FsyncDir синхронизирует директорию (для гарантии, что создание файла записано на диск).
// На некоторых платформах (Windows, Solaris/Illumos) fsync для директорий не поддерживается.
func FsyncDir(dirPath string) error { func FsyncDir(dirPath string) error {
dir, err := os.Open(dirPath) dir, err := os.Open(dirPath)
if err != nil { if err != nil {
@@ -60,24 +70,84 @@ func FsyncDir(dirPath string) error {
} }
defer dir.Close() defer dir.Close()
if runtime.GOOS == "windows" { // На Windows и Solaris/Illumos нет прямой поддержки fsync для директорий
// На Windows нет прямой поддержки fsync для директорий // Используем стандартный Sync если доступен
switch runtime.GOOS {
case "windows", "solaris", "illumos", "openindiana":
// На этих платформах fsync для директорий не поддерживается или не нужен
// Возвращаем nil, так как данные уже записаны через fsync файлов
return nil return nil
default:
// Для Linux, BSD, macOS используем Sync
return dir.Sync()
} }
return dir.Sync()
} }
// FsyncDataOnly выполняет fsync только для данных (не метаданных) // FsyncDataOnly выполняет fsync только для данных (не метаданных).
// Использует стандартный Sync для всех платформ.
// Это обеспечивает запись данных на диск без синхронизации метаданных.
func FsyncDataOnly(file *os.File) error { func FsyncDataOnly(file *os.File) error {
if file == nil { if file == nil {
return nil return nil
} }
fd := int(file.Fd()) // Используем стандартный Sync для всех платформ
_, _, err := syscall.Syscall(syscall.SYS_FSYNC, uintptr(fd), 0, 0) // Это обеспечит запись данных на диск
if err != 0 { return file.Sync()
return fmt.Errorf("fsync failed: %v", err) }
}
return nil // FsyncWithTimeout выполняет fsync с таймаутом.
// Если операция не завершается за указанное время, возвращается ошибка.
func FsyncWithTimeout(file *os.File, timeout time.Duration) error {
if file == nil {
return nil
}
// Создаём канал для результата
result := make(chan error, 1)
// Запускаем fsync в отдельной горутине
go func() {
result <- file.Sync()
}()
// Ожидаем результат или таймаут
select {
case err := <-result:
return err
case <-time.After(timeout):
return fmt.Errorf("fsync timeout after %v", timeout)
}
}
// MustFsync выполняет fsync и паникует при ошибке.
// Используется только в критических секциях, где ошибка синхронизации фатальна.
func MustFsync(file *os.File) {
if file == nil {
return
}
if err := file.Sync(); err != nil {
panic(fmt.Sprintf("Fatal: fsync failed: %v", err))
}
}
// IsFsyncSupported возвращает true если платформа поддерживает fsync.
// Все платформы поддерживают fsync через file.Sync().
func IsFsyncSupported() bool {
return true
}
// GetFsyncPlatformInfo возвращает информацию о платформе для диагностики.
func GetFsyncPlatformInfo() map[string]interface{} {
return map[string]interface{}{
"os": runtime.GOOS,
"arch": runtime.GOARCH,
"fsync_supported": IsFsyncSupported(),
"dir_fsync_supported": !(runtime.GOOS == "windows" ||
runtime.GOOS == "solaris" ||
runtime.GOOS == "illumos" ||
runtime.GOOS == "openindiana"),
"method": "file.Sync()",
}
} }

View File

@@ -857,6 +857,7 @@ func (pm *PersistenceManager) LoadDatabase(dbName string) error {
} }
// applyWALEntry применяет одну запись WAL к базе данных // applyWALEntry применяет одну запись WAL к базе данных
// TransactionCommitted = 1 (определено в transactions.go)
func (pm *PersistenceManager) applyWALEntry(db *Database, entry WALEntry) error { func (pm *PersistenceManager) applyWALEntry(db *Database, entry WALEntry) error {
// Проверяем тип записи (1 = Transaction) // Проверяем тип записи (1 = Transaction)
if entry.Type != "1" { if entry.Type != "1" {
@@ -882,12 +883,13 @@ func (pm *PersistenceManager) applyWALEntry(db *Database, entry WALEntry) error
} }
// Применяем только закоммиченные транзакции (state = 1) // Применяем только закоммиченные транзакции (state = 1)
if txRecord.State != 1 { // TransactionCommitted // TransactionCommitted = 1 (определено в transactions.go через iota)
if txRecord.State != 1 {
return nil return nil
} }
for _, op := range txRecord.Operations { for _, op := range txRecord.Operations {
if op.Database != db.name { if op.Database != db.Name() {
continue continue
} }