futriis/internal/engine/engine.go

522 lines
17 KiB
Go
Raw Normal View History

2026-02-23 22:48:31 +03:00
// /futriis/internal/engine/engine.go
// Пакет engine реализует ядро СУБД, координирующее все операции
package engine
import (
"fmt"
"strings"
"futriis/internal/cluster"
"futriis/internal/lua"
"futriis/internal/replication"
"futriis/internal/storage"
"futriis/internal/transaction"
"futriis/pkg/config"
"futriis/pkg/utils"
)
// Engine представляет ядро СУБД
type Engine struct {
storage *storage.Storage
clusterMgr *cluster.ClusterManager
txMgr *transaction.TransactionManager
luaMgr *lua.PluginManager
aofMgr *replication.AOFManager
cfg *config.Config
}
// NewEngine создаёт новый экземпляр ядра СУБД
func NewEngine() *Engine {
cfg := config.Get()
// Создаём AOF менеджер
aofMgr, _ := replication.NewAOFManager(cfg.Node.AOFFile, cfg.Node.AOFEnabled)
// Воспроизводим AOF если нужно
if aofMgr != nil && cfg.Node.AOFEnabled {
// TODO: восстановление состояния из AOF
}
return &Engine{
storage: storage.NewStorage(),
clusterMgr: cluster.NewClusterManager(cfg),
txMgr: transaction.NewTransactionManager(),
luaMgr: lua.NewPluginManager(&cfg.Lua),
aofMgr: aofMgr,
cfg: cfg,
}
}
// Execute выполняет команду и возвращает результат
func (e *Engine) Execute(input string) (string, error) {
// Разбираем ввод
parts := strings.Fields(input)
if len(parts) == 0 {
return "", nil
}
command := strings.ToLower(parts[0])
args := parts[1:]
// Записываем команду в AOF
if e.aofMgr != nil {
argsInterface := make([]interface{}, len(args))
for i, v := range args {
argsInterface[i] = v
}
e.aofMgr.Append(command, argsInterface)
}
// Обработка команд
switch command {
case "help":
return e.help(), nil
case "exit", "quit":
return "exit", nil
case "create":
return e.handleCreate(args)
case "delete":
return e.handleDelete(args)
case "update":
return e.handleUpdate(args)
case "list":
return e.handleList(args)
case "show":
return e.handleShow(args)
case "begin":
return e.handleBegin()
case "commit":
return e.handleCommit()
case "rollback":
return e.handleRollback()
case "cluster.status":
return e.handleClusterStatus()
case "add.node":
return e.handleAddNode(args)
case "evict.node":
return e.handleEvictNode(args)
case "lua":
return e.handleLua(args)
default:
return "", fmt.Errorf("неизвестная команда: %s", command)
}
}
// handleCreate обрабатывает команды создания
func (e *Engine) handleCreate(args []string) (string, error) {
if len(args) < 2 {
return "", fmt.Errorf("недостаточно аргументов для команды create")
}
switch args[0] {
case "tapple":
if len(args) < 2 {
return "", fmt.Errorf("не указано имя таппла")
}
return e.createTapple(args[1])
case "slice":
if len(args) < 3 {
return "", fmt.Errorf("недостаточно аргументов для создания слайса")
}
return e.createSlice(args[1], args[2])
case "tuple":
if len(args) < 4 {
return "", fmt.Errorf("недостаточно аргументов для создания кортежа")
}
return e.createTuple(args[1], args[2], args[3], args[4:])
default:
return "", fmt.Errorf("неизвестный тип создания: %s", args[0])
}
}
// handleDelete обрабатывает команды удаления
func (e *Engine) handleDelete(args []string) (string, error) {
if len(args) < 2 {
return "", fmt.Errorf("недостаточно аргументов для команды delete")
}
switch args[0] {
case "tapple":
if len(args) < 2 {
return "", fmt.Errorf("не указано имя таппла")
}
return e.deleteTapple(args[1])
case "slice":
if len(args) < 3 {
return "", fmt.Errorf("недостаточно аргументов для удаления слайса")
}
return e.deleteSlice(args[1], args[2])
case "tuple":
if len(args) < 4 {
return "", fmt.Errorf("недостаточно аргументов для удаления кортежа")
}
return e.deleteTuple(args[1], args[2], args[3])
default:
return "", fmt.Errorf("неизвестный тип удаления: %s", args[0])
}
}
// handleUpdate обрабатывает команды обновления
func (e *Engine) handleUpdate(args []string) (string, error) {
if len(args) < 4 || args[0] != "tuple" {
return "", fmt.Errorf("неверная команда update")
}
return e.updateTuple(args[1], args[2], args[3], args[4:])
}
// handleList обрабатывает команды списка
func (e *Engine) handleList(args []string) (string, error) {
if len(args) < 1 {
return "", fmt.Errorf("недостаточно аргументов для команды list")
}
switch args[0] {
case "tapples":
return e.listTapples()
case "slices":
if len(args) < 2 {
return "", fmt.Errorf("не указано имя таппла")
}
return e.listSlices(args[1])
default:
return "", fmt.Errorf("неизвестный тип списка: %s", args[0])
}
}
// handleShow обрабатывает команды показа
func (e *Engine) handleShow(args []string) (string, error) {
if len(args) < 2 || args[0] != "tuples" {
return "", fmt.Errorf("неверная команда show")
}
if len(args) < 3 {
return "", fmt.Errorf("недостаточно аргументов для show tuples")
}
return e.showTuples(args[1], args[2])
}
// handleBegin начинает транзакцию
func (e *Engine) handleBegin() (string, error) {
id, err := e.txMgr.Begin()
if err != nil {
return "", err
}
return utils.ColorGreen + "Транзакция начата. ID: " + id + utils.ColorReset, nil
}
// handleCommit фиксирует транзакцию
func (e *Engine) handleCommit() (string, error) {
err := e.txMgr.Commit()
if err != nil {
return "", err
}
return utils.ColorGreen + "Транзакция зафиксирована" + utils.ColorReset, nil
}
// handleRollback откатывает транзакцию
func (e *Engine) handleRollback() (string, error) {
err := e.txMgr.Rollback()
if err != nil {
return "", err
}
return utils.ColorGreen + "Транзакция отменена" + utils.ColorReset, nil
}
// handleClusterStatus показывает статус кластера
func (e *Engine) handleClusterStatus() (string, error) {
status := e.clusterMgr.GetClusterStatus()
result := utils.ColorCyan + "Статус кластера:" + utils.ColorReset + "\n"
result += fmt.Sprintf(" Всего узлов: %d\n", status["total_nodes"])
result += fmt.Sprintf(" Активных узлов: %d\n", status["active_nodes"])
result += fmt.Sprintf(" Координатор: %v\n", status["coordinator"])
nodes, _ := status["nodes"].([]map[string]interface{})
if len(nodes) > 0 {
result += utils.ColorCyan + "\nУзлы кластера:" + utils.ColorReset + "\n"
for _, node := range nodes {
result += fmt.Sprintf(" %s (%s) - %s, last seen: %s\n",
node["id"], node["address"], node["state"], node["last_seen"])
}
}
return result, nil
}
// handleAddNode добавляет узел в кластер
func (e *Engine) handleAddNode(args []string) (string, error) {
if len(args) < 1 {
return "", fmt.Errorf("укажите адрес узла")
}
address := args[0]
err := e.clusterMgr.AddNode(address)
if err != nil {
return "", err
}
return utils.ColorGreen + "Узел " + address + " добавлен в кластер" + utils.ColorReset, nil
}
// handleEvictNode удаляет узел из кластера
func (e *Engine) handleEvictNode(args []string) (string, error) {
if len(args) < 1 {
return "", fmt.Errorf("укажите ID узла или адрес")
}
nodeID := args[0]
err := e.clusterMgr.RemoveNode(nodeID)
if err != nil {
return "", err
}
return utils.ColorGreen + "Узел " + nodeID + " удален из кластера" + utils.ColorReset, nil
}
// handleLua выполняет Lua скрипт
func (e *Engine) handleLua(args []string) (string, error) {
if len(args) < 1 {
return "", fmt.Errorf("укажите имя плагина")
}
pluginName := args[0]
err := e.luaMgr.ExecutePlugin(pluginName)
if err != nil {
return "", err
}
return utils.ColorGreen + "Плагин выполнен" + utils.ColorReset, nil
}
// Методы для работы с тапплами
func (e *Engine) createTapple(name string) (string, error) {
tapple, err := e.storage.GetTappleManager().CreateTapple(name)
if err != nil {
return "", err
}
return utils.ColorGreen + "Таппл '" + tapple.Name + "' успешно создан" + utils.ColorReset, nil
}
func (e *Engine) deleteTapple(name string) (string, error) {
err := e.storage.GetTappleManager().DeleteTapple(name)
if err != nil {
return "", err
}
return utils.ColorGreen + "Таппл '" + name + "' успешно удален" + utils.ColorReset, nil
}
func (e *Engine) listTapples() (string, error) {
tapples := e.storage.GetTappleManager().ListTapples()
if len(tapples) == 0 {
return utils.ColorYellow + "Тапплы не найдены" + utils.ColorReset, nil
}
result := utils.ColorCyan + "Список тапплов:" + utils.ColorReset + "\n"
for _, t := range tapples {
result += " " + utils.ColorGreen + t + utils.ColorReset + "\n"
}
return result, nil
}
// Методы для работы со слайсами
func (e *Engine) createSlice(tappleName, sliceName string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
slice, err := e.storage.GetTappleManager().GetSliceManager().CreateSlice(tapple, sliceName)
if err != nil {
return "", err
}
return utils.ColorGreen + "Слайс '" + slice.Name + "' в таппле '" + tappleName + "' успешно создан" + utils.ColorReset, nil
}
func (e *Engine) deleteSlice(tappleName, sliceName string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
err = e.storage.GetTappleManager().GetSliceManager().DeleteSlice(tapple, sliceName)
if err != nil {
return "", err
}
return utils.ColorGreen + "Слайс '" + sliceName + "' в таппле '" + tappleName + "' успешно удален" + utils.ColorReset, nil
}
func (e *Engine) listSlices(tappleName string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
slices := e.storage.GetTappleManager().GetSliceManager().ListSlices(tapple)
if len(slices) == 0 {
return utils.ColorYellow + "Слайсы в таппле '" + tappleName + "' не найдены" + utils.ColorReset, nil
}
result := utils.ColorCyan + "Список слайсов в таппле '" + tappleName + "':" + utils.ColorReset + "\n"
for _, s := range slices {
result += " " + utils.ColorGreen + s + utils.ColorReset + "\n"
}
return result, nil
}
// Методы для работы с кортежами
func (e *Engine) createTuple(tappleName, sliceName, tupleID string, fieldsArgs []string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName)
if err != nil {
return "", err
}
// Парсим поля
fields := make(map[string]interface{})
for _, arg := range fieldsArgs {
parts := strings.SplitN(arg, "=", 2)
if len(parts) == 2 {
fields[parts[0]] = parts[1]
}
}
tuple, err := e.storage.GetTappleManager().GetSliceManager().GetTupleManager().CreateTuple(slice, tupleID, fields)
if err != nil {
return "", err
}
return utils.ColorGreen + "Кортеж '" + tuple.ID + "' в слайсе '" + sliceName + "' успешно создан" + utils.ColorReset, nil
}
func (e *Engine) deleteTuple(tappleName, sliceName, tupleID string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName)
if err != nil {
return "", err
}
err = e.storage.GetTappleManager().GetSliceManager().GetTupleManager().DeleteTuple(slice, tupleID)
if err != nil {
return "", err
}
return utils.ColorGreen + "Кортеж '" + tupleID + "' в слайсе '" + sliceName + "' успешно удален" + utils.ColorReset, nil
}
func (e *Engine) updateTuple(tappleName, sliceName, tupleID string, fieldsArgs []string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName)
if err != nil {
return "", err
}
// Парсим поля
fields := make(map[string]interface{})
for _, arg := range fieldsArgs {
parts := strings.SplitN(arg, "=", 2)
if len(parts) == 2 {
fields[parts[0]] = parts[1]
}
}
tuple, err := e.storage.GetTappleManager().GetSliceManager().GetTupleManager().UpdateTuple(slice, tupleID, fields)
if err != nil {
return "", err
}
return utils.ColorGreen + "Кортеж '" + tuple.ID + "' в слайсе '" + sliceName + "' успешно обновлен" + utils.ColorReset, nil
}
// showTuples показывает все кортежи в слайсе
func (e *Engine) showTuples(tappleName, sliceName string) (string, error) {
tapple, err := e.storage.GetTappleManager().GetTapple(tappleName)
if err != nil {
return "", err
}
slice, err := e.storage.GetTappleManager().GetSliceManager().GetSlice(tapple, sliceName)
if err != nil {
return "", err
}
slice.RLock()
defer slice.RUnlock()
if len(slice.Tuples) == 0 {
return utils.ColorYellow + "Кортежи в слайсе '" + sliceName + "' не найдены" + utils.ColorReset, nil
}
result := utils.ColorCyan + "Список кортежей в слайсе '" + sliceName + "':" + utils.ColorReset + "\n"
for id, tuple := range slice.Tuples {
result += " " + utils.ColorGreen + "ID: " + id + utils.ColorReset + "\n"
for k, v := range tuple.Fields {
// ИСПРАВЛЕНО: заменено ColorPrompt на ColorPromptCode
result += " " + utils.ColorYellow + k + utils.ColorReset + ": " + utils.ColorPromptCode + fmt.Sprint(v) + utils.ColorReset + "\n"
}
result += "\n"
}
return result, nil
}
// help возвращает справку по командам
func (e *Engine) help() string {
help := utils.ColorCyan + "Доступные команды:" + utils.ColorReset + "\n"
help += "\n" + utils.ColorYellow + "Основные команды:" + utils.ColorReset + "\n"
help += " " + utils.ColorGreen + "create tapple <name>" + utils.ColorReset + " - создать новый таппл (базу данных)\n"
help += " " + utils.ColorGreen + "create slice <tapple> <name>" + utils.ColorReset + " - создать новый слайс (таблицу)\n"
help += " " + utils.ColorGreen + "create tuple <tapple> <slice> <id> [key=value...]" + utils.ColorReset + " - создать новый кортеж (запись)\n"
help += " " + utils.ColorGreen + "delete tapple <name>" + utils.ColorReset + " - удалить таппл\n"
help += " " + utils.ColorGreen + "delete slice <tapple> <name>" + utils.ColorReset + " - удалить слайс\n"
help += " " + utils.ColorGreen + "delete tuple <tapple> <slice> <id>" + utils.ColorReset + " - удалить кортеж\n"
help += " " + utils.ColorGreen + "update tuple <tapple> <slice> <id> [key=value...]" + utils.ColorReset + " - обновить кортеж\n"
help += " " + utils.ColorGreen + "list tapples" + utils.ColorReset + " - показать все тапплы\n"
help += " " + utils.ColorGreen + "list slices <tapple>" + utils.ColorReset + " - показать все слайсы в таппле\n"
help += " " + utils.ColorGreen + "show tuples <tapple> <slice>" + utils.ColorReset + " - показать все кортежи в слайсе\n"
help += "\n" + utils.ColorYellow + "Транзакции:" + utils.ColorReset + "\n"
help += " " + utils.ColorGreen + "begin" + utils.ColorReset + " - начать транзакцию\n"
help += " " + utils.ColorGreen + "commit" + utils.ColorReset + " - зафиксировать транзакцию\n"
help += " " + utils.ColorGreen + "rollback" + utils.ColorReset + " - отменить транзакцию\n"
help += "\n" + utils.ColorYellow + "Управление кластером:" + utils.ColorReset + "\n"
help += " " + utils.ColorGreen + "cluster.status" + utils.ColorReset + " - показать статус кластера\n"
help += " " + utils.ColorGreen + "add.node <address>" + utils.ColorReset + " - добавить узел в кластер\n"
help += " " + utils.ColorGreen + "evict.node <node_id>" + utils.ColorReset + " - удалить узел из кластера\n"
help += "\n" + utils.ColorYellow + "Lua плагины:" + utils.ColorReset + "\n"
help += " " + utils.ColorGreen + "lua <plugin_name>" + utils.ColorReset + " - выполнить Lua плагин\n"
help += "\n" + utils.ColorYellow + "Прочее:" + utils.ColorReset + "\n"
help += " " + utils.ColorGreen + "exit/quit" + utils.ColorReset + " - выйти из СУБД\n"
return help
}