package store import ( "strings" "sync" "time" ) func (m *Memory) GetModulePrefixSnapshot(tenantID, moduleID string) (*ModulePrefixSnapshot, bool, error) { m.mu.RLock() defer m.mu.RUnlock() key := moduleSnapshotKey(tenantID, moduleID) snap, ok := m.moduleSnapshots[key] if !ok || snap == nil { return nil, false, nil } cp := make([]PrefixRow, len(snap.Prefixes)) copy(cp, snap.Prefixes) return &ModulePrefixSnapshot{ InputHash: snap.InputHash, CollectedAt: snap.CollectedAt, Prefixes: cp, }, true, nil } func (m *Memory) SetModulePrefixSnapshot(tenantID, moduleID, inputHash string, prefixes []PrefixRow) error { if strings.TrimSpace(tenantID) == "" || strings.TrimSpace(moduleID) == "" || strings.TrimSpace(inputHash) == "" { return ErrInvalidInput } m.mu.Lock() defer m.mu.Unlock() if m.moduleSnapshots == nil { m.moduleSnapshots = make(map[string]*moduleSnapshotRec) } cp := make([]PrefixRow, len(prefixes)) copy(cp, prefixes) key := moduleSnapshotKey(tenantID, moduleID) m.moduleSnapshots[key] = &moduleSnapshotRec{ InputHash: inputHash, CollectedAt: time.Now().UTC(), Prefixes: cp, } return nil } func (m *Memory) DeleteModulePrefixSnapshot(tenantID, moduleID string) error { m.mu.Lock() defer m.mu.Unlock() if m.moduleSnapshots != nil { delete(m.moduleSnapshots, moduleSnapshotKey(tenantID, moduleID)) } return nil } func (m *Memory) SetModuleInputHash(tenantID, moduleID, hash string) error { m.mu.Lock() defer m.mu.Unlock() mod, ok := m.modules[moduleID] if !ok || mod.DeletedAt != nil { return ErrNotFound } if mod.TenantID != tenantID { return ErrTenantScope } mod.InputHash = strings.TrimSpace(hash) return nil } func (m *Memory) LockModuleSnapshot(tenantID, moduleID string) func() { key := moduleSnapshotKey(tenantID, moduleID) v, _ := m.snapshotLocks.LoadOrStore(key, &sync.Mutex{}) mu := v.(*sync.Mutex) mu.Lock() return func() { mu.Unlock() } } type moduleSnapshotRec struct { InputHash string CollectedAt time.Time Prefixes []PrefixRow } func moduleSnapshotKey(tenantID, moduleID string) string { return strings.TrimSpace(tenantID) + "\x00" + strings.TrimSpace(moduleID) }