// Файл: 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 }