Delete internal/storage/engine.go
This commit is contained in:
@@ -1,319 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright 2026 Safronov Grigorii
|
|
||||||
*
|
|
||||||
* Licensed under the CDDL, Version 1.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
*
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
* https://opensource.org/licenses/CDDL-1.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Файл: internal/storage/engine.go
|
|
||||||
// Назначение: In-memory движок хранения документов с поддержкой коллекций,
|
|
||||||
// слайсов (аналог БД), тапплов (аналог таблиц), полей и кортежей.
|
|
||||||
// Полностью wait-free с использованием sync.Map и атомарных операций.
|
|
||||||
|
|
||||||
package storage
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"sync"
|
|
||||||
"sync/atomic"
|
|
||||||
|
|
||||||
"futriis/internal/log"
|
|
||||||
"futriis/internal/serializer"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Storage представляет основное хранилище баз данных
|
|
||||||
type Storage struct {
|
|
||||||
databases sync.Map // map[string]*Database
|
|
||||||
pageSize int64
|
|
||||||
logger *log.Logger
|
|
||||||
totalDocs atomic.Int64
|
|
||||||
}
|
|
||||||
|
|
||||||
// Database представляет базу данных (аналог слайса в реляционных СУБД)
|
|
||||||
type Database struct {
|
|
||||||
name string
|
|
||||||
collections sync.Map // map[string]*Collection
|
|
||||||
}
|
|
||||||
|
|
||||||
// NewStorage создаёт новый экземпляр хранилища
|
|
||||||
func NewStorage(pageSizeMB int, logger *log.Logger) *Storage {
|
|
||||||
return &Storage{
|
|
||||||
pageSize: int64(pageSizeMB) * 1024 * 1024,
|
|
||||||
logger: logger,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateDatabase создаёт новую базу данных
|
|
||||||
func (s *Storage) CreateDatabase(name string) error {
|
|
||||||
if _, exists := s.databases.LoadOrStore(name, &Database{name: name}); exists {
|
|
||||||
return fmt.Errorf("database already exists")
|
|
||||||
}
|
|
||||||
AuditDatabaseOperation("CREATE", name)
|
|
||||||
s.logger.Info("Database created: " + name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDatabase возвращает базу данных по имени
|
|
||||||
func (s *Storage) GetDatabase(name string) (*Database, error) {
|
|
||||||
if val, ok := s.databases.Load(name); ok {
|
|
||||||
return val.(*Database), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("database not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropDatabase удаляет базу данных
|
|
||||||
func (s *Storage) DropDatabase(name string) error {
|
|
||||||
if _, ok := s.databases.LoadAndDelete(name); !ok {
|
|
||||||
return fmt.Errorf("database not found")
|
|
||||||
}
|
|
||||||
AuditDatabaseOperation("DROP", name)
|
|
||||||
s.logger.Info("Database dropped: " + name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListDatabases возвращает список всех баз данных
|
|
||||||
func (s *Storage) ListDatabases() []string {
|
|
||||||
databases := make([]string, 0)
|
|
||||||
s.databases.Range(func(key, value interface{}) bool {
|
|
||||||
databases = append(databases, key.(string))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return databases
|
|
||||||
}
|
|
||||||
|
|
||||||
// Name возвращает имя базы данных
|
|
||||||
func (db *Database) Name() string {
|
|
||||||
return db.name
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateCollection создаёт новую коллекцию в базе данных
|
|
||||||
func (db *Database) CreateCollection(name string) error {
|
|
||||||
// Исправлено: добавлен параметр dbName
|
|
||||||
if _, exists := db.collections.LoadOrStore(name, NewCollection(db.name, name, nil)); exists {
|
|
||||||
return fmt.Errorf("collection already exists")
|
|
||||||
}
|
|
||||||
AuditCollectionOperation("CREATE", db.name, name, nil)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// CreateCollectionWithSettings создаёт коллекцию с настройками
|
|
||||||
func (db *Database) CreateCollectionWithSettings(name string, settings *CollectionSettings) error {
|
|
||||||
// Исправлено: добавлен параметр dbName
|
|
||||||
if _, exists := db.collections.LoadOrStore(name, NewCollection(db.name, name, settings)); exists {
|
|
||||||
return fmt.Errorf("collection already exists")
|
|
||||||
}
|
|
||||||
AuditCollectionOperation("CREATE", db.name, name, settings)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetCollection возвращает коллекцию по имени
|
|
||||||
func (db *Database) GetCollection(name string) (*Collection, error) {
|
|
||||||
if val, ok := db.collections.Load(name); ok {
|
|
||||||
return val.(*Collection), nil
|
|
||||||
}
|
|
||||||
return nil, fmt.Errorf("collection not found")
|
|
||||||
}
|
|
||||||
|
|
||||||
// DropCollection удаляет коллекцию
|
|
||||||
func (db *Database) DropCollection(name string) error {
|
|
||||||
if _, ok := db.collections.LoadAndDelete(name); !ok {
|
|
||||||
return fmt.Errorf("collection not found")
|
|
||||||
}
|
|
||||||
AuditCollectionOperation("DROP", db.name, name, nil)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// ListCollections возвращает список всех коллекций в базе данных
|
|
||||||
func (db *Database) ListCollections() []string {
|
|
||||||
collections := make([]string, 0)
|
|
||||||
db.collections.Range(func(key, value interface{}) bool {
|
|
||||||
collections = append(collections, key.(string))
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return collections
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetTotalDocuments возвращает общее количество документов во всех коллекциях
|
|
||||||
func (s *Storage) GetTotalDocuments() int64 {
|
|
||||||
return s.totalDocs.Load()
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetPageSize возвращает размер страницы памяти
|
|
||||||
func (s *Storage) GetPageSize() int64 {
|
|
||||||
return s.pageSize
|
|
||||||
}
|
|
||||||
|
|
||||||
// SerializeDatabase сериализует всю базу данных в MessagePack
|
|
||||||
func (db *Database) SerializeDatabase() ([]byte, error) {
|
|
||||||
dbData := make(map[string]interface{})
|
|
||||||
|
|
||||||
db.collections.Range(func(key, value interface{}) bool {
|
|
||||||
coll := value.(*Collection)
|
|
||||||
collData := make(map[string]interface{})
|
|
||||||
|
|
||||||
// Собираем все документы коллекции
|
|
||||||
docs := coll.GetAllDocuments()
|
|
||||||
collDocs := make([]*Document, 0, len(docs))
|
|
||||||
for _, doc := range docs {
|
|
||||||
collDocs = append(collDocs, doc)
|
|
||||||
}
|
|
||||||
collData["documents"] = collDocs
|
|
||||||
collData["metadata"] = coll.GetMetadata()
|
|
||||||
|
|
||||||
dbData[key.(string)] = collData
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
|
|
||||||
return serializer.Marshal(dbData)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DeserializeDatabase десериализует базу данных из MessagePack
|
|
||||||
func (db *Database) DeserializeDatabase(data []byte) error {
|
|
||||||
var dbData map[string]interface{}
|
|
||||||
if err := serializer.Unmarshal(data, &dbData); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for collName, collDataRaw := range dbData {
|
|
||||||
collData, ok := collDataRaw.(map[string]interface{})
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// Создаём коллекцию
|
|
||||||
settings := &CollectionSettings{
|
|
||||||
MaxDocuments: 0,
|
|
||||||
ValidateSchema: false,
|
|
||||||
AutoIndexID: true,
|
|
||||||
TTLSeconds: 0,
|
|
||||||
}
|
|
||||||
|
|
||||||
if metaRaw, ok := collData["metadata"]; ok {
|
|
||||||
// Пробуем разные варианты десериализации метаданных
|
|
||||||
if meta, ok := metaRaw.(map[string]interface{}); ok {
|
|
||||||
if settingsRaw, ok := meta["settings"]; ok {
|
|
||||||
if settingsMap, ok := settingsRaw.(map[string]interface{}); ok {
|
|
||||||
if maxDocs, ok := settingsMap["max_documents"]; ok {
|
|
||||||
if v, ok := maxDocs.(int); ok {
|
|
||||||
settings.MaxDocuments = v
|
|
||||||
} else if v, ok := maxDocs.(int64); ok {
|
|
||||||
settings.MaxDocuments = int(v)
|
|
||||||
} else if v, ok := maxDocs.(float64); ok {
|
|
||||||
settings.MaxDocuments = int(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if validateSchema, ok := settingsMap["validate_schema"]; ok {
|
|
||||||
if v, ok := validateSchema.(bool); ok {
|
|
||||||
settings.ValidateSchema = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if autoIndexID, ok := settingsMap["auto_index_id"]; ok {
|
|
||||||
if v, ok := autoIndexID.(bool); ok {
|
|
||||||
settings.AutoIndexID = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ttlSeconds, ok := settingsMap["ttl_seconds"]; ok {
|
|
||||||
if v, ok := ttlSeconds.(int); ok {
|
|
||||||
settings.TTLSeconds = v
|
|
||||||
} else if v, ok := ttlSeconds.(int64); ok {
|
|
||||||
settings.TTLSeconds = int(v)
|
|
||||||
} else if v, ok := ttlSeconds.(float64); ok {
|
|
||||||
settings.TTLSeconds = int(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if meta, ok := metaRaw.(*CollectionMetadata); ok {
|
|
||||||
if meta.Settings != nil {
|
|
||||||
settings = meta.Settings
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Исправлено: добавлен параметр dbName
|
|
||||||
coll := NewCollection(db.name, collName, settings)
|
|
||||||
|
|
||||||
// Восстанавливаем документы
|
|
||||||
if docsRaw, ok := collData["documents"]; ok {
|
|
||||||
if docs, ok := docsRaw.([]interface{}); ok {
|
|
||||||
for _, docRaw := range docs {
|
|
||||||
if doc, ok := docRaw.(*Document); ok {
|
|
||||||
coll.Insert(doc)
|
|
||||||
} else if docMap, ok := docRaw.(map[string]interface{}); ok {
|
|
||||||
// Создаём документ из map
|
|
||||||
doc := NewDocument()
|
|
||||||
if id, ok := docMap["ID"].(string); ok {
|
|
||||||
doc.ID = id
|
|
||||||
} else if id, ok := docMap["id"].(string); ok {
|
|
||||||
doc.ID = id
|
|
||||||
}
|
|
||||||
if fields, ok := docMap["fields"]; ok {
|
|
||||||
if fieldsMap, ok := fields.(map[string]interface{}); ok {
|
|
||||||
for k, v := range fieldsMap {
|
|
||||||
doc.SetField(k, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if createdAt, ok := docMap["created_at"]; ok {
|
|
||||||
if v, ok := createdAt.(int64); ok {
|
|
||||||
doc.CreatedAt = v
|
|
||||||
} else if v, ok := createdAt.(float64); ok {
|
|
||||||
doc.CreatedAt = int64(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if updatedAt, ok := docMap["updated_at"]; ok {
|
|
||||||
if v, ok := updatedAt.(int64); ok {
|
|
||||||
doc.UpdatedAt = v
|
|
||||||
} else if v, ok := updatedAt.(float64); ok {
|
|
||||||
doc.UpdatedAt = int64(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if version, ok := docMap["version"]; ok {
|
|
||||||
if v, ok := version.(uint64); ok {
|
|
||||||
doc.Version = v
|
|
||||||
} else if v, ok := version.(int64); ok {
|
|
||||||
doc.Version = uint64(v)
|
|
||||||
} else if v, ok := version.(float64); ok {
|
|
||||||
doc.Version = uint64(v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
coll.Insert(doc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if docs, ok := docsRaw.([]*Document); ok {
|
|
||||||
for _, doc := range docs {
|
|
||||||
coll.Insert(doc)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
db.collections.Store(collName, coll)
|
|
||||||
AuditCollectionOperation("RESTORE", db.name, collName, settings)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDatabaseNames возвращает имена всех баз данных
|
|
||||||
func (s *Storage) GetDatabaseNames() []string {
|
|
||||||
return s.ListDatabases()
|
|
||||||
}
|
|
||||||
|
|
||||||
// ExistsDatabase проверяет существование базы данных
|
|
||||||
func (s *Storage) ExistsDatabase(name string) bool {
|
|
||||||
_, ok := s.databases.Load(name)
|
|
||||||
return ok
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetDatabaseCount возвращает количество баз данных
|
|
||||||
func (s *Storage) GetDatabaseCount() int {
|
|
||||||
count := 0
|
|
||||||
s.databases.Range(func(key, value interface{}) bool {
|
|
||||||
count++
|
|
||||||
return true
|
|
||||||
})
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user