994 lines
32 KiB
Go
994 lines
32 KiB
Go
|
|
// /futriis/internal/engine/engine.go
|
|||
|
|
// Пакет engine реализует ядро СУБД Futriis, координирующее все операции.
|
|||
|
|
// Выступает в роли центрального компонента, связывающего хранилище, кластерное управление, транзакции, Lua плагины и AOF (Append-Only File) для персистентности.
|
|||
|
|
|
|||
|
|
package engine
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"reflect"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"futriis/internal/cluster"
|
|||
|
|
"futriis/internal/lua"
|
|||
|
|
"futriis/internal/replication"
|
|||
|
|
"futriis/internal/storage"
|
|||
|
|
"futriis/internal/transaction"
|
|||
|
|
"futriis/pkg/config"
|
|||
|
|
"futriis/pkg/types"
|
|||
|
|
"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
|
|||
|
|
aofRecovered bool // Флаг, было ли восстановление из AOF
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// NewEngine создаёт новый экземпляр ядра СУБД
|
|||
|
|
func NewEngine() *Engine {
|
|||
|
|
cfg := config.Get()
|
|||
|
|
|
|||
|
|
// Создаём AOF менеджер
|
|||
|
|
aofMgr, _ := replication.NewAOFManager(cfg.Node.AOFFile, cfg.Node.AOFEnabled)
|
|||
|
|
|
|||
|
|
// Воспроизводим AOF если нужно
|
|||
|
|
aofRecovered := false
|
|||
|
|
if aofMgr != nil && cfg.Node.AOFEnabled {
|
|||
|
|
// Восстановление состояния из AOF
|
|||
|
|
if err := replayAOF(aofMgr); err != nil {
|
|||
|
|
utils.PrintError("Error recovering from AOF: %v", err)
|
|||
|
|
} else {
|
|||
|
|
aofRecovered = true
|
|||
|
|
// Сообщение будет показано в handler.go
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return &Engine{
|
|||
|
|
storage: storage.NewStorage(),
|
|||
|
|
clusterMgr: cluster.NewClusterManager(cfg),
|
|||
|
|
txMgr: transaction.NewTransactionManager(),
|
|||
|
|
luaMgr: lua.NewPluginManager(&cfg.Lua),
|
|||
|
|
aofMgr: aofMgr,
|
|||
|
|
cfg: cfg,
|
|||
|
|
aofRecovered: aofRecovered,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// GetConfig возвращает конфигурацию
|
|||
|
|
func (e *Engine) GetConfig() *config.Config {
|
|||
|
|
return e.cfg
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// WasAOFRecovered возвращает флаг восстановления из AOF
|
|||
|
|
func (e *Engine) WasAOFRecovered() bool {
|
|||
|
|
return e.aofRecovered
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// replayAOF воспроизводит команды из AOF файла для восстановления состояния
|
|||
|
|
func replayAOF(aofMgr *replication.AOFManager) error {
|
|||
|
|
commands, err := aofMgr.ReadAll()
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Errorf("failed to read AOF: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаём временное хранилище для восстановления
|
|||
|
|
tempStorage := storage.NewStorage()
|
|||
|
|
|
|||
|
|
for i, cmd := range commands {
|
|||
|
|
// Пропускаем команды транзакций при восстановлении
|
|||
|
|
if cmd.Name == "begin" || cmd.Name == "commit" || cmd.Name == "rollback" {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Преобразуем аргументы в строки
|
|||
|
|
args := make([]string, len(cmd.Args))
|
|||
|
|
for j, arg := range cmd.Args {
|
|||
|
|
if str, ok := arg.(string); ok {
|
|||
|
|
args[j] = str
|
|||
|
|
} else {
|
|||
|
|
args[j] = fmt.Sprint(arg)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Выполняем команду на временном хранилище
|
|||
|
|
if err := executeRestoreCommand(tempStorage, cmd.Name, args); err != nil {
|
|||
|
|
utils.PrintWarning("Error replaying command #%d (%s): %v", i+1, cmd.Name, err)
|
|||
|
|
// Продолжаем восстановление, несмотря на ошибки
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// TODO: Перенести восстановленные данные в основное хранилище
|
|||
|
|
// Это упрощённая реализация, в реальности нужно синхронизировать состояния
|
|||
|
|
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// executeRestoreCommand выполняет команду при восстановлении из AOF
|
|||
|
|
func executeRestoreCommand(storage *storage.Storage, cmdName string, args []string) error {
|
|||
|
|
switch cmdName {
|
|||
|
|
case "create":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
switch args[0] {
|
|||
|
|
case "tapple":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
_, err := storage.GetTappleManager().CreateTapple(args[1])
|
|||
|
|
return err
|
|||
|
|
case "slice":
|
|||
|
|
if len(args) < 3 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
tapple, err := storage.GetTappleManager().GetTapple(args[1])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
_, err = storage.GetTappleManager().GetSliceManager().CreateSlice(tapple, args[2])
|
|||
|
|
return err
|
|||
|
|
case "tuple":
|
|||
|
|
if len(args) < 4 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
tapple, err := storage.GetTappleManager().GetTapple(args[1])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
slice, err := storage.GetTappleManager().GetSliceManager().GetSlice(tapple, args[2])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
fields := make(map[string]interface{})
|
|||
|
|
for i := 4; i < len(args); i++ {
|
|||
|
|
parts := strings.SplitN(args[i], "=", 2)
|
|||
|
|
if len(parts) == 2 {
|
|||
|
|
fields[parts[0]] = parts[1]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_, err = storage.GetTappleManager().GetSliceManager().GetTupleManager().CreateTuple(slice, args[3], fields)
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
case "update":
|
|||
|
|
if len(args) < 4 || args[0] != "tuple" {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
tapple, err := storage.GetTappleManager().GetTapple(args[1])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
slice, err := storage.GetTappleManager().GetSliceManager().GetSlice(tapple, args[2])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
fields := make(map[string]interface{})
|
|||
|
|
for i := 4; i < len(args); i++ {
|
|||
|
|
parts := strings.SplitN(args[i], "=", 2)
|
|||
|
|
if len(parts) == 2 {
|
|||
|
|
fields[parts[0]] = parts[1]
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
_, err = storage.GetTappleManager().GetSliceManager().GetTupleManager().UpdateTuple(slice, args[3], fields)
|
|||
|
|
return err
|
|||
|
|
case "delete":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
switch args[0] {
|
|||
|
|
case "tapple":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
return storage.GetTappleManager().DeleteTapple(args[1])
|
|||
|
|
case "slice":
|
|||
|
|
if len(args) < 3 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
tapple, err := storage.GetTappleManager().GetTapple(args[1])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
return storage.GetTappleManager().GetSliceManager().DeleteSlice(tapple, args[2])
|
|||
|
|
case "tuple":
|
|||
|
|
if len(args) < 4 {
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
tapple, err := storage.GetTappleManager().GetTapple(args[1])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
slice, err := storage.GetTappleManager().GetSliceManager().GetSlice(tapple, args[2])
|
|||
|
|
if err != nil {
|
|||
|
|
return err
|
|||
|
|
}
|
|||
|
|
return storage.GetTappleManager().GetSliceManager().GetTupleManager().DeleteTuple(slice, args[3])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 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 && command != "begin" && command != "commit" && command != "rollback" &&
|
|||
|
|
command != "cluster.status" && command != "help" && command != "exit" && command != "quit" &&
|
|||
|
|
command != "aof.recover" && command != "aof.info" &&
|
|||
|
|
!strings.HasPrefix(command, "add.prime.index") && !strings.HasPrefix(command, "delete.prime.index") &&
|
|||
|
|
!strings.HasPrefix(command, "add.secondary.index") && !strings.HasPrefix(command, "delete.secondary.index") &&
|
|||
|
|
command != "cluster.rebalance" {
|
|||
|
|
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 "cluster.rebalance":
|
|||
|
|
return e.handleClusterRebalance(args)
|
|||
|
|
|
|||
|
|
case "add.node":
|
|||
|
|
return e.handleAddNode(args)
|
|||
|
|
|
|||
|
|
case "evict.node":
|
|||
|
|
return e.handleEvictNode(args)
|
|||
|
|
|
|||
|
|
case "lua":
|
|||
|
|
return e.handleLua(args)
|
|||
|
|
|
|||
|
|
case "aof.recover":
|
|||
|
|
return e.handleAOFRecover(args)
|
|||
|
|
|
|||
|
|
case "aof.info":
|
|||
|
|
return e.handleAOFInfo()
|
|||
|
|
|
|||
|
|
case "add.prime.index":
|
|||
|
|
return e.handleAddPrimaryIndex(args)
|
|||
|
|
|
|||
|
|
case "delete.prime.index":
|
|||
|
|
return e.handleDeletePrimaryIndex(args)
|
|||
|
|
|
|||
|
|
case "add.secondary.index":
|
|||
|
|
return e.handleAddSecondaryIndex(args)
|
|||
|
|
|
|||
|
|
case "delete.secondary.index":
|
|||
|
|
return e.handleDeleteSecondaryIndex(args)
|
|||
|
|
|
|||
|
|
case "compression.stats":
|
|||
|
|
return e.handleCompressionStats(args)
|
|||
|
|
|
|||
|
|
case "sharding.status":
|
|||
|
|
return e.handleShardingStatus()
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
return "", fmt.Errorf("unknown command: %s", command)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleAOFRecover восстанавливает данные из AOF файла
|
|||
|
|
func (e *Engine) handleAOFRecover(args []string) (string, error) {
|
|||
|
|
if e.aofMgr == nil {
|
|||
|
|
return "", fmt.Errorf("AOF manager not initialized")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверяем, указан ли путь к файлу
|
|||
|
|
filePath := e.cfg.Node.AOFFile
|
|||
|
|
if len(args) > 0 {
|
|||
|
|
filePath = args[0]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаём временный AOF менеджер для чтения указанного файла
|
|||
|
|
tmpAOF, err := replication.NewAOFManager(filePath, true)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", fmt.Errorf("failed to open AOF file: %v", err)
|
|||
|
|
}
|
|||
|
|
defer tmpAOF.Close()
|
|||
|
|
|
|||
|
|
// Читаем все команды
|
|||
|
|
commands, err := tmpAOF.ReadAll()
|
|||
|
|
if err != nil {
|
|||
|
|
return "", fmt.Errorf("failed to read AOF file: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if len(commands) == 0 {
|
|||
|
|
return utils.ColorYellow + "AOF file is empty" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаём временное хранилище для проверки
|
|||
|
|
tempStorage := storage.NewStorage()
|
|||
|
|
successCount := 0
|
|||
|
|
errorCount := 0
|
|||
|
|
|
|||
|
|
for i, cmd := range commands {
|
|||
|
|
if cmd.Name == "begin" || cmd.Name == "commit" || cmd.Name == "rollback" {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
args := make([]string, len(cmd.Args))
|
|||
|
|
for j, arg := range cmd.Args {
|
|||
|
|
if str, ok := arg.(string); ok {
|
|||
|
|
args[j] = str
|
|||
|
|
} else {
|
|||
|
|
args[j] = fmt.Sprint(arg)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if err := executeRestoreCommand(tempStorage, cmd.Name, args); err != nil {
|
|||
|
|
utils.PrintWarning("Error in command #%d: %v", i+1, err)
|
|||
|
|
errorCount++
|
|||
|
|
} else {
|
|||
|
|
successCount++
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return fmt.Sprintf(utils.ColorGreen+"Recovery completed. Successful: %d, Errors: %d"+utils.ColorReset,
|
|||
|
|
successCount, errorCount), nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleAOFInfo показывает информацию о AOF файле
|
|||
|
|
func (e *Engine) handleAOFInfo() (string, error) {
|
|||
|
|
if e.aofMgr == nil {
|
|||
|
|
return "", fmt.Errorf("AOF manager not initialized")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получаем информацию о файле напрямую
|
|||
|
|
filePath := e.cfg.Node.AOFFile
|
|||
|
|
commands, err := e.aofMgr.ReadAll()
|
|||
|
|
if err != nil {
|
|||
|
|
return "", fmt.Errorf("failed to read AOF file: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получаем размер файла
|
|||
|
|
fileInfo, err := e.aofMgr.GetFileInfo()
|
|||
|
|
if err != nil {
|
|||
|
|
fileInfo = "unavailable"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result := utils.ColorCyan + "AOF Information:" + utils.ColorReset + "\n"
|
|||
|
|
result += fmt.Sprintf(" File: %s\n", filePath)
|
|||
|
|
result += fmt.Sprintf(" Size: %v\n", fileInfo)
|
|||
|
|
result += fmt.Sprintf(" Commands: %d\n", len(commands))
|
|||
|
|
if len(commands) > 0 {
|
|||
|
|
lastCmd := commands[len(commands)-1]
|
|||
|
|
result += fmt.Sprintf(" Last write: %d\n", lastCmd.Time)
|
|||
|
|
} else {
|
|||
|
|
result += " Last write: no records\n"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleClusterRebalance выполняет ребалансировку кластера
|
|||
|
|
func (e *Engine) handleClusterRebalance(args []string) (string, error) {
|
|||
|
|
clusterName := "futriis-cluster"
|
|||
|
|
if len(args) > 0 {
|
|||
|
|
clusterName = args[0]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
err := e.clusterMgr.RebalanceCluster()
|
|||
|
|
if err != nil {
|
|||
|
|
return "", fmt.Errorf("cluster rebalance failed: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Cluster '" + clusterName + "' rebalanced successfully" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleCreate обрабатывает команды создания
|
|||
|
|
func (e *Engine) handleCreate(args []string) (string, error) {
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for create command")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch args[0] {
|
|||
|
|
case "tapple":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("tapple name not specified")
|
|||
|
|
}
|
|||
|
|
return e.createTapple(args[1])
|
|||
|
|
case "slice":
|
|||
|
|
if len(args) < 3 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for slice creation")
|
|||
|
|
}
|
|||
|
|
return e.createSlice(args[1], args[2])
|
|||
|
|
case "tuple":
|
|||
|
|
if len(args) < 4 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for tuple creation")
|
|||
|
|
}
|
|||
|
|
return e.createTuple(args[1], args[2], args[3], args[4:])
|
|||
|
|
default:
|
|||
|
|
return "", fmt.Errorf("unknown creation type: %s", args[0])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleDelete обрабатывает команды удаления
|
|||
|
|
func (e *Engine) handleDelete(args []string) (string, error) {
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for delete command")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch args[0] {
|
|||
|
|
case "tapple":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("tapple name not specified")
|
|||
|
|
}
|
|||
|
|
return e.deleteTapple(args[1])
|
|||
|
|
case "slice":
|
|||
|
|
if len(args) < 3 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for slice deletion")
|
|||
|
|
}
|
|||
|
|
return e.deleteSlice(args[1], args[2])
|
|||
|
|
case "tuple":
|
|||
|
|
if len(args) < 4 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for tuple deletion")
|
|||
|
|
}
|
|||
|
|
return e.deleteTuple(args[1], args[2], args[3])
|
|||
|
|
default:
|
|||
|
|
return "", fmt.Errorf("unknown deletion type: %s", args[0])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleUpdate обрабатывает команды обновления
|
|||
|
|
func (e *Engine) handleUpdate(args []string) (string, error) {
|
|||
|
|
if len(args) < 4 || args[0] != "tuple" {
|
|||
|
|
return "", fmt.Errorf("invalid update command")
|
|||
|
|
}
|
|||
|
|
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("insufficient arguments for list command")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch args[0] {
|
|||
|
|
case "tapples":
|
|||
|
|
return e.listTapples()
|
|||
|
|
case "slices":
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("tapple name not specified")
|
|||
|
|
}
|
|||
|
|
return e.listSlices(args[1])
|
|||
|
|
default:
|
|||
|
|
return "", fmt.Errorf("unknown list type: %s", args[0])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleShow обрабатывает команды показа
|
|||
|
|
func (e *Engine) handleShow(args []string) (string, error) {
|
|||
|
|
if len(args) < 2 || args[0] != "tuples" {
|
|||
|
|
return "", fmt.Errorf("invalid show command")
|
|||
|
|
}
|
|||
|
|
if len(args) < 3 {
|
|||
|
|
return "", fmt.Errorf("insufficient arguments for 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 + "Transaction started. ID: " + id + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleCommit фиксирует транзакцию
|
|||
|
|
func (e *Engine) handleCommit() (string, error) {
|
|||
|
|
err := e.txMgr.Commit()
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
return utils.ColorGreen + "Transaction committed" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleRollback откатывает транзакцию
|
|||
|
|
func (e *Engine) handleRollback() (string, error) {
|
|||
|
|
err := e.txMgr.Rollback()
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
return utils.ColorGreen + "Transaction rolled back" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleClusterStatus показывает статус кластера
|
|||
|
|
func (e *Engine) handleClusterStatus() (string, error) {
|
|||
|
|
status := e.clusterMgr.GetClusterStatus()
|
|||
|
|
|
|||
|
|
result := utils.ColorCyan + "Cluster Status:" + utils.ColorReset + "\n"
|
|||
|
|
result += fmt.Sprintf(" Total nodes: %d\n", status["total_nodes"])
|
|||
|
|
result += fmt.Sprintf(" Active nodes: %d\n", status["active_nodes"])
|
|||
|
|
result += fmt.Sprintf(" Coordinator: %v\n", status["coordinator"])
|
|||
|
|
result += fmt.Sprintf(" Master-master replication: %v\n", status["master_master"])
|
|||
|
|
|
|||
|
|
nodes, _ := status["nodes"].([]map[string]interface{})
|
|||
|
|
if len(nodes) > 0 {
|
|||
|
|
result += utils.ColorCyan + "\nCluster Nodes:" + 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("specify node address")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
address := args[0]
|
|||
|
|
err := e.clusterMgr.AddNode(address)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Node " + address + " added to cluster" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleEvictNode удаляет узел из кластера
|
|||
|
|
func (e *Engine) handleEvictNode(args []string) (string, error) {
|
|||
|
|
if len(args) < 1 {
|
|||
|
|
return "", fmt.Errorf("specify node ID or address")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
nodeID := args[0]
|
|||
|
|
err := e.clusterMgr.RemoveNode(nodeID)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Node " + nodeID + " removed from cluster" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleLua выполняет Lua скрипт
|
|||
|
|
func (e *Engine) handleLua(args []string) (string, error) {
|
|||
|
|
if len(args) < 1 {
|
|||
|
|
return "", fmt.Errorf("specify plugin name")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pluginName := args[0]
|
|||
|
|
err := e.luaMgr.ExecutePlugin(pluginName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Plugin executed" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleAddPrimaryIndex обрабатывает создание первичного индекса
|
|||
|
|
func (e *Engine) handleAddPrimaryIndex(args []string) (string, error) {
|
|||
|
|
if len(args) < 1 {
|
|||
|
|
return "", fmt.Errorf("specify tapple name")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tappleName := args[0]
|
|||
|
|
|
|||
|
|
// Получаем таппл
|
|||
|
|
_, err := e.storage.GetTappleManager().GetTapple(tappleName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", fmt.Errorf("tapple not found: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаём первичный индекс
|
|||
|
|
indexManager := e.storage.GetTappleManager().GetIndexManager()
|
|||
|
|
err = indexManager.CreatePrimaryIndex(tappleName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Primary index for tapple '" + tappleName + "' created successfully" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleDeletePrimaryIndex обрабатывает удаление первичного индекса
|
|||
|
|
func (e *Engine) handleDeletePrimaryIndex(args []string) (string, error) {
|
|||
|
|
if len(args) < 1 {
|
|||
|
|
return "", fmt.Errorf("specify tapple name")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tappleName := args[0]
|
|||
|
|
|
|||
|
|
indexManager := e.storage.GetTappleManager().GetIndexManager()
|
|||
|
|
err := indexManager.DeletePrimaryIndex(tappleName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Primary index for tapple '" + tappleName + "' deleted successfully" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleAddSecondaryIndex обрабатывает создание вторичного индекса
|
|||
|
|
func (e *Engine) handleAddSecondaryIndex(args []string) (string, error) {
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("specify tapple name and field name")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tappleName := args[0]
|
|||
|
|
fieldName := args[1]
|
|||
|
|
|
|||
|
|
// Получаем таппл
|
|||
|
|
_, err := e.storage.GetTappleManager().GetTapple(tappleName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", fmt.Errorf("tapple not found: %v", err)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Создаём вторичный индекс
|
|||
|
|
indexManager := e.storage.GetTappleManager().GetIndexManager()
|
|||
|
|
err = indexManager.CreateSecondaryIndex(tappleName, fieldName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Secondary index for tapple '" + tappleName + "' on field '" + fieldName + "' created successfully" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleDeleteSecondaryIndex обрабатывает удаление вторичного индекса
|
|||
|
|
func (e *Engine) handleDeleteSecondaryIndex(args []string) (string, error) {
|
|||
|
|
if len(args) < 2 {
|
|||
|
|
return "", fmt.Errorf("specify tapple name and field name")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
tappleName := args[0]
|
|||
|
|
fieldName := args[1]
|
|||
|
|
|
|||
|
|
indexManager := e.storage.GetTappleManager().GetIndexManager()
|
|||
|
|
err := indexManager.DeleteSecondaryIndex(tappleName, fieldName)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return utils.ColorGreen + "Secondary index for tapple '" + tappleName + "' on field '" + fieldName + "' deleted successfully" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleCompressionStats показывает статистику сжатия
|
|||
|
|
func (e *Engine) handleCompressionStats(args []string) (string, error) {
|
|||
|
|
result := utils.ColorCyan + "Compression Statistics:" + utils.ColorReset + "\n"
|
|||
|
|
result += " Compression statistics available at slice level\n"
|
|||
|
|
result += " Use 'show compression <tapple> <slice>' for detailed information"
|
|||
|
|
|
|||
|
|
return result, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// handleShardingStatus показывает статус шардинга
|
|||
|
|
func (e *Engine) handleShardingStatus() (string, error) {
|
|||
|
|
status := e.clusterMgr.GetClusterStatus()
|
|||
|
|
|
|||
|
|
shardingInfo, exists := status["sharding"]
|
|||
|
|
if !exists {
|
|||
|
|
return utils.ColorYellow + "Sharding is not activated" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
shardStats := shardingInfo.(map[string]interface{})
|
|||
|
|
|
|||
|
|
result := utils.ColorCyan + "Sharding Status:" + utils.ColorReset + "\n"
|
|||
|
|
result += fmt.Sprintf(" Enabled: %v\n", shardStats["enabled"])
|
|||
|
|
result += fmt.Sprintf(" Strategy: %s\n", shardStats["strategy"])
|
|||
|
|
result += fmt.Sprintf(" Total shards: %d\n", shardStats["total_shards"])
|
|||
|
|
|
|||
|
|
shards, _ := shardStats["shards"].([]map[string]interface{})
|
|||
|
|
if len(shards) > 0 {
|
|||
|
|
result += utils.ColorCyan + "\nShards:" + utils.ColorReset + "\n"
|
|||
|
|
for _, shard := range shards {
|
|||
|
|
result += fmt.Sprintf(" %s: nodes=%v, reads=%d, writes=%d\n",
|
|||
|
|
shard["id"], shard["nodes"], shard["reads"], shard["writes"])
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result, 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 '" + tapple.Name + "' created successfully" + 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 + "Tapple '" + name + "' deleted successfully" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (e *Engine) listTapples() (string, error) {
|
|||
|
|
tapples := e.storage.GetTappleManager().ListTapples()
|
|||
|
|
if len(tapples) == 0 {
|
|||
|
|
return utils.ColorYellow + "No tapples found" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result := utils.ColorCyan + "List of tapples:" + 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 '" + slice.Name + "' in tapple '" + tappleName + "' created successfully" + 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 + "Slice '" + sliceName + "' in tapple '" + tappleName + "' deleted successfully" + 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 + "No slices found in tapple '" + tappleName + "'" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result := utils.ColorCyan + "List of slices in tapple '" + 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 '" + tuple.ID + "' in slice '" + sliceName + "' created successfully" + 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 + "Tuple '" + tupleID + "' in slice '" + sliceName + "' deleted successfully" + 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 '" + tuple.ID + "' in slice '" + sliceName + "' updated successfully" + 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
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Получаем все кортежи из слайса через рефлексию
|
|||
|
|
tuples, err := e.getAllTuplesFromSlice(slice)
|
|||
|
|
if err != nil {
|
|||
|
|
return "", err
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if len(tuples) == 0 {
|
|||
|
|
return utils.ColorYellow + "No tuples found in slice '" + sliceName + "'" + utils.ColorReset, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result := utils.ColorCyan + "List of tuples in slice '" + sliceName + "':" + utils.ColorReset + "\n"
|
|||
|
|
for id, tuple := range tuples {
|
|||
|
|
result += " " + utils.ColorGreen + "ID: " + id + utils.ColorReset + "\n"
|
|||
|
|
for k, v := range tuple.Fields {
|
|||
|
|
result += " " + utils.ColorYellow + k + utils.ColorReset + ": " + utils.ColorPromptCode + fmt.Sprint(v) + utils.ColorReset + "\n"
|
|||
|
|
}
|
|||
|
|
result += "\n"
|
|||
|
|
}
|
|||
|
|
return result, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// вспомогательная функция для получения всех кортежей из слайса через рефлексию
|
|||
|
|
func (e *Engine) getAllTuplesFromSlice(slice *types.Slice) (map[string]*types.Tuple, error) {
|
|||
|
|
if slice == nil {
|
|||
|
|
return nil, fmt.Errorf("slice is nil")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Используем рефлексию для доступа к неэкспортируемому полю tuples
|
|||
|
|
v := reflect.ValueOf(slice).Elem()
|
|||
|
|
field := v.FieldByName("tuples")
|
|||
|
|
|
|||
|
|
if !field.IsValid() || field.Kind() != reflect.Map {
|
|||
|
|
return nil, fmt.Errorf("cannot access tuples field in slice")
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
result := make(map[string]*types.Tuple)
|
|||
|
|
iter := field.MapRange()
|
|||
|
|
for iter.Next() {
|
|||
|
|
key := iter.Key().String()
|
|||
|
|
value := iter.Value().Interface()
|
|||
|
|
if tuple, ok := value.(*types.Tuple); ok {
|
|||
|
|
result[key] = tuple
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return result, nil
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// help возвращает справку по командам
|
|||
|
|
func (e *Engine) help() string {
|
|||
|
|
help := utils.ColorCyan + "Available commands:" + utils.ColorReset + "\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Basic commands:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "create tapple <name>" + utils.ColorReset + " - create a new tapple (database)\n"
|
|||
|
|
help += " " + utils.ColorGreen + "create slice <tapple> <name>" + utils.ColorReset + " - create a new slice (table)\n"
|
|||
|
|
help += " " + utils.ColorGreen + "create tuple <tapple> <slice> <id> [key=value...]" + utils.ColorReset + " - create a new tuple (record)\n"
|
|||
|
|
help += " " + utils.ColorGreen + "delete tapple <name>" + utils.ColorReset + " - delete a tapple\n"
|
|||
|
|
help += " " + utils.ColorGreen + "delete slice <tapple> <name>" + utils.ColorReset + " - delete a slice\n"
|
|||
|
|
help += " " + utils.ColorGreen + "delete tuple <tapple> <slice> <id>" + utils.ColorReset + " - delete a tuple\n"
|
|||
|
|
help += " " + utils.ColorGreen + "update tuple <tapple> <slice> <id> [key=value...]" + utils.ColorReset + " - update a tuple\n"
|
|||
|
|
help += " " + utils.ColorGreen + "list tapples" + utils.ColorReset + " - show all tapples\n"
|
|||
|
|
help += " " + utils.ColorGreen + "list slices <tapple>" + utils.ColorReset + " - show all slices in a tapple\n"
|
|||
|
|
help += " " + utils.ColorGreen + "show tuples <tapple> <slice>" + utils.ColorReset + " - show all tuples in a slice\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Index management:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "add.prime.index <tapple>" + utils.ColorReset + " - create primary index for tapple\n"
|
|||
|
|
help += " " + utils.ColorGreen + "delete.prime.index <tapple>" + utils.ColorReset + " - delete primary index\n"
|
|||
|
|
help += " " + utils.ColorGreen + "add.secondary.index <tapple> <field>" + utils.ColorReset + " - create secondary index on field\n"
|
|||
|
|
help += " " + utils.ColorGreen + "delete.secondary.index <tapple> <field>" + utils.ColorReset + " - delete secondary index\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Transactions:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "begin" + utils.ColorReset + " - start a transaction\n"
|
|||
|
|
help += " " + utils.ColorGreen + "commit" + utils.ColorReset + " - commit a transaction\n"
|
|||
|
|
help += " " + utils.ColorGreen + "rollback" + utils.ColorReset + " - rollback a transaction\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Cluster and sharding management:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "cluster.status" + utils.ColorReset + " - show cluster status\n"
|
|||
|
|
help += " " + utils.ColorGreen + "cluster.rebalance [cluster_name]" + utils.ColorReset + " - rebalance the cluster\n"
|
|||
|
|
help += " " + utils.ColorGreen + "sharding.status" + utils.ColorReset + " - show sharding status\n"
|
|||
|
|
help += " " + utils.ColorGreen + "add.node <address>" + utils.ColorReset + " - add a node to the cluster\n"
|
|||
|
|
help += " " + utils.ColorGreen + "evict.node <node_id>" + utils.ColorReset + " - remove a node from the cluster\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Compression:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "compression.stats" + utils.ColorReset + " - show compression statistics\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "AOF management:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "aof.recover [file]" + utils.ColorReset + " - recover data from AOF file\n"
|
|||
|
|
help += " " + utils.ColorGreen + "aof.info" + utils.ColorReset + " - show AOF file information\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Lua plugins:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "lua <plugin_name>" + utils.ColorReset + " - execute Lua plugin\n"
|
|||
|
|
|
|||
|
|
help += "\n" + utils.ColorYellow + "Other:" + utils.ColorReset + "\n"
|
|||
|
|
help += " " + utils.ColorGreen + "exit/quit" + utils.ColorReset + " - exit the DBMS\n"
|
|||
|
|
|
|||
|
|
return help
|
|||
|
|
}
|