522 lines
17 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// /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
}