197 lines
5.7 KiB
Go
Raw Normal View History

2026-02-27 22:04:04 +03:00
// /futriis/internal/storage/slice.go
// Пакет storage реализует операции со слайсами (таблицами) - контейнерами для кортежей.
// SliceManager управляет созданием, получением, удалением и списком слайсов внутри тапплов.
// Интегрируется с TupleManager для операций с кортежами на более низком уровне.
// Обеспечивает wait-free доступ к слайсам через атомарные операции.
package storage
import (
"errors"
"reflect"
"sync"
"sync/atomic"
"time"
"futriis/pkg/types"
"futriis/pkg/utils"
)
// SliceManager управляет операциями со слайсами
type SliceManager struct {
tupleManager *TupleManager
stats struct {
created int64
deleted int64
}
// Добавляем собственную блокировку для управления доступом к тапплам
mu sync.RWMutex
}
// NewSliceManager создаёт новый менеджер слайсов
func NewSliceManager() *SliceManager {
return &SliceManager{
tupleManager: NewTupleManager(),
}
}
// вспомогательная функция для доступа к неэкспортируемому полю slices
func getSlicesFromTapple(tapple *types.Tapple) map[string]*types.Slice {
// Используем рефлексию для доступа к неэкспортируемому полю
v := reflect.ValueOf(tapple).Elem()
field := v.FieldByName("slices")
if field.IsValid() && field.Kind() == reflect.Map {
// Преобразуем в map[string]*types.Slice
result := make(map[string]*types.Slice)
iter := field.MapRange()
for iter.Next() {
key := iter.Key().String()
value := iter.Value().Interface()
if slice, ok := value.(*types.Slice); ok {
result[key] = slice
}
}
return result
}
return make(map[string]*types.Slice)
}
// вспомогательная функция для добавления слайса в неэкспортируемое поле slices
func addSliceToTapple(tapple *types.Tapple, name string, slice *types.Slice) error {
v := reflect.ValueOf(tapple).Elem()
field := v.FieldByName("slices")
if field.IsValid() && field.Kind() == reflect.Map {
// Создаём новую map, если она nil
if field.IsNil() {
newMap := reflect.MakeMap(reflect.TypeOf(map[string]*types.Slice{}))
field.Set(newMap)
}
// Устанавливаем значение
key := reflect.ValueOf(name)
value := reflect.ValueOf(slice)
field.SetMapIndex(key, value)
return nil
}
return errors.New("cannot access slices field in tapple")
}
// вспомогательная функция для удаления слайса из неэкспортируемого поля slices
func removeSliceFromTapple(tapple *types.Tapple, name string) error {
v := reflect.ValueOf(tapple).Elem()
field := v.FieldByName("slices")
if field.IsValid() && field.Kind() == reflect.Map {
key := reflect.ValueOf(name)
field.SetMapIndex(key, reflect.Value{})
return nil
}
return errors.New("cannot access slices field in tapple")
}
// CreateSlice создаёт новый слайс в указанном таппле с временной меткой
func (sm *SliceManager) CreateSlice(tapple *types.Tapple, name string) (*types.Slice, error) {
if tapple == nil {
return nil, errors.New("tapple is nil")
}
// Используем блокировку менеджера для доступа к тапплу
sm.mu.Lock()
defer sm.mu.Unlock()
// Получаем слайсы через рефлексию
slices := getSlicesFromTapple(tapple)
_, exists := slices[name]
if exists {
return nil, errors.New("slice already exists")
}
slice := types.NewSlice(name)
slice.CreatedAt = time.Now()
slice.UpdatedAt = time.Now()
// Добавляем слайс через рефлексию
err := addSliceToTapple(tapple, name, slice)
if err != nil {
return nil, err
}
atomic.AddInt64(&sm.stats.created, 1)
logger := utils.GetLogger()
if logger != nil {
logger.Log("INFO", "Created slice: "+name+" at "+slice.CreatedAt.Format(time.RFC3339))
}
return slice, nil
}
// GetSlice возвращает слайс по имени
func (sm *SliceManager) GetSlice(tapple *types.Tapple, name string) (*types.Slice, error) {
if tapple == nil {
return nil, errors.New("tapple is nil")
}
sm.mu.RLock()
defer sm.mu.RUnlock()
slices := getSlicesFromTapple(tapple)
slice, exists := slices[name]
if !exists {
return nil, errors.New("slice not found")
}
return slice, nil
}
// DeleteSlice удаляет слайс
func (sm *SliceManager) DeleteSlice(tapple *types.Tapple, name string) error {
if tapple == nil {
return errors.New("tapple is nil")
}
sm.mu.Lock()
defer sm.mu.Unlock()
slices := getSlicesFromTapple(tapple)
_, exists := slices[name]
if !exists {
return errors.New("slice not found")
}
err := removeSliceFromTapple(tapple, name)
if err != nil {
return err
}
atomic.AddInt64(&sm.stats.deleted, 1)
logger := utils.GetLogger()
if logger != nil {
logger.Log("INFO", "Deleted slice: "+name)
}
return nil
}
// ListSlices возвращает список всех слайсов в таппле
func (sm *SliceManager) ListSlices(tapple *types.Tapple) []string {
if tapple == nil {
return nil
}
sm.mu.RLock()
defer sm.mu.RUnlock()
slices := getSlicesFromTapple(tapple)
result := make([]string, 0, len(slices))
for name := range slices {
result = append(result, name)
}
return result
}
// GetTupleManager возвращает менеджер кортежей
func (sm *SliceManager) GetTupleManager() *TupleManager {
return sm.tupleManager
}