197 lines
5.7 KiB
Go
197 lines
5.7 KiB
Go
|
|
// /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
|
|||
|
|
}
|