225 lines
7.4 KiB
Go
225 lines
7.4 KiB
Go
// Файл: 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 {
|
||
if _, exists := db.collections.LoadOrStore(name, NewCollection(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 {
|
||
if _, exists := db.collections.LoadOrStore(name, NewCollection(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 := collDataRaw.(map[string]interface{})
|
||
|
||
// Создаём коллекцию
|
||
settings := &CollectionSettings{
|
||
MaxDocuments: 0,
|
||
ValidateSchema: false,
|
||
AutoIndexID: true,
|
||
TTLSeconds: 0,
|
||
}
|
||
|
||
if metaRaw, ok := collData["metadata"]; ok {
|
||
if meta, ok := metaRaw.(*CollectionMetadata); ok {
|
||
if meta.Settings != nil {
|
||
settings = meta.Settings
|
||
}
|
||
}
|
||
}
|
||
|
||
coll := NewCollection(collName, settings)
|
||
|
||
// Восстанавливаем документы
|
||
if docsRaw, ok := collData["documents"]; ok {
|
||
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
|
||
}
|