Files
EvoBGP/internal/store/memory_maintenance_policy.go
T
DenozordecandCursor 07c3de4939 feat(store): add MaintenancePolicy CRUD backend
Типы maintenance_policy, методы store.Backend и реализации для PostgreSQL и in-memory.

Co-authored-by: Cursor <[email protected]>
2026-06-12 13:20:27 +07:00

245 lines
5.8 KiB
Go

package store
import (
"sort"
"strconv"
"strings"
"time"
"github.com/google/uuid"
)
func (m *Memory) ListMaintenancePolicies(cursor string, limit int) ([]*MaintenancePolicy, string, bool, error) {
if limit <= 0 {
limit = 50
}
m.mu.RLock()
defer m.mu.RUnlock()
all := make([]*MaintenancePolicy, 0, len(m.maintenancePolicies))
for _, p := range m.maintenancePolicies {
all = append(all, p)
}
sort.Slice(all, func(i, j int) bool {
if all[i].CreatedAt.Equal(all[j].CreatedAt) {
return all[i].ID > all[j].ID
}
return all[i].CreatedAt.After(all[j].CreatedAt)
})
off := parseMaintCursor(cursor)
end := off + limit
next := ""
hasMore := false
if end > len(all) {
end = len(all)
} else if end < len(all) {
hasMore = true
next = formatMaintCursor(end)
}
if off >= len(all) {
return nil, "", false, nil
}
out := make([]*MaintenancePolicy, end-off)
copy(out, all[off:end])
return out, next, hasMore, nil
}
func (m *Memory) GetMaintenancePolicy(id string) (*MaintenancePolicy, error) {
m.mu.RLock()
defer m.mu.RUnlock()
p, ok := m.maintenancePolicies[id]
if !ok {
return nil, ErrNotFound
}
return cloneMaintenancePolicy(p), nil
}
func (m *Memory) CreateMaintenancePolicy(in *MaintenancePolicy) (*MaintenancePolicy, error) {
if in == nil {
return nil, ErrInvalidInput
}
vacuum := in.VacuumStrategy
if vacuum == "" {
vacuum = VacuumStrategyNone
}
if err := ValidateMaintenancePolicyInput(in.Name, in.TableName, vacuum, in.Schedule); err != nil {
return nil, err
}
m.mu.Lock()
defer m.mu.Unlock()
now := time.Now().UTC()
id := uuid.NewString()
p := &MaintenancePolicy{
ID: id,
Name: strings.TrimSpace(in.Name),
TableName: strings.TrimSpace(in.TableName),
Condition: NormalizeMaintenancePolicyCondition(in.Condition),
RetentionPeriodSec: in.RetentionPeriodSec,
MaxRows: in.MaxRows,
VacuumStrategy: vacuum,
Schedule: strings.TrimSpace(in.Schedule),
Enabled: in.Enabled,
DryRunEnabled: in.DryRunEnabled,
CreatedAt: now,
UpdatedAt: now,
}
m.maintenancePolicies[id] = p
return cloneMaintenancePolicy(p), nil
}
func (m *Memory) UpdateMaintenancePolicy(id string, patch *MaintenancePolicyPatch) (*MaintenancePolicy, error) {
if patch == nil {
return nil, ErrInvalidInput
}
m.mu.Lock()
defer m.mu.Unlock()
p, ok := m.maintenancePolicies[id]
if !ok {
return nil, ErrNotFound
}
if patch.Name != nil {
p.Name = strings.TrimSpace(*patch.Name)
}
if patch.TableName != nil {
p.TableName = strings.TrimSpace(*patch.TableName)
}
if patch.Condition != nil {
p.Condition = NormalizeMaintenancePolicyCondition(*patch.Condition)
}
if patch.RetentionPeriodSec != nil {
p.RetentionPeriodSec = patch.RetentionPeriodSec
}
if patch.MaxRows != nil {
p.MaxRows = patch.MaxRows
}
if patch.VacuumStrategy != nil {
if !ValidVacuumStrategy(*patch.VacuumStrategy) {
return nil, ErrInvalidInput
}
p.VacuumStrategy = strings.TrimSpace(*patch.VacuumStrategy)
}
if patch.Schedule != nil {
p.Schedule = strings.TrimSpace(*patch.Schedule)
}
if patch.Enabled != nil {
p.Enabled = *patch.Enabled
}
if patch.DryRunEnabled != nil {
p.DryRunEnabled = *patch.DryRunEnabled
}
if err := ValidateMaintenancePolicyInput(p.Name, p.TableName, p.VacuumStrategy, p.Schedule); err != nil {
return nil, err
}
p.UpdatedAt = time.Now().UTC()
return cloneMaintenancePolicy(p), nil
}
func (m *Memory) DeleteMaintenancePolicy(id string) error {
m.mu.Lock()
defer m.mu.Unlock()
if _, ok := m.maintenancePolicies[id]; !ok {
return ErrNotFound
}
delete(m.maintenancePolicies, id)
return nil
}
func (m *Memory) TouchMaintenancePolicyRun(id, status, errMsg string) error {
m.mu.Lock()
defer m.mu.Unlock()
p, ok := m.maintenancePolicies[id]
if !ok {
return ErrNotFound
}
now := time.Now().UTC()
p.LastRunAt = &now
p.LastStatus = status
p.LastError = errMsg
p.UpdatedAt = now
return nil
}
func (m *Memory) AppendMaintenancePolicyConfigAudit(actor, policyID, action string, before, after map[string]any) error {
m.mu.Lock()
defer m.mu.Unlock()
row := &MaintenancePolicyConfigAudit{
ID: uuid.NewString(),
PolicyID: policyID,
ActorPrefix: strings.TrimSpace(actor),
Action: action,
Before: before,
After: after,
CreatedAt: time.Now().UTC(),
}
m.maintConfigAudit = append(m.maintConfigAudit, row)
return nil
}
func (m *Memory) ListMaintenancePolicyConfigAudit(cursor string, limit int) ([]*MaintenancePolicyConfigAudit, string, bool, error) {
if limit <= 0 {
limit = 50
}
m.mu.RLock()
defer m.mu.RUnlock()
all := append([]*MaintenancePolicyConfigAudit(nil), m.maintConfigAudit...)
sort.Slice(all, func(i, j int) bool {
if all[i].CreatedAt.Equal(all[j].CreatedAt) {
return all[i].ID > all[j].ID
}
return all[i].CreatedAt.After(all[j].CreatedAt)
})
off := parseMaintCursor(cursor)
end := off + limit
next := ""
hasMore := false
if end > len(all) {
end = len(all)
} else if end < len(all) {
hasMore = true
next = formatMaintCursor(end)
}
if off >= len(all) {
return nil, "", false, nil
}
out := make([]*MaintenancePolicyConfigAudit, end-off)
copy(out, all[off:end])
return out, next, hasMore, nil
}
func cloneMaintenancePolicy(p *MaintenancePolicy) *MaintenancePolicy {
if p == nil {
return nil
}
cp := *p
if p.RetentionPeriodSec != nil {
v := *p.RetentionPeriodSec
cp.RetentionPeriodSec = &v
}
if p.MaxRows != nil {
v := *p.MaxRows
cp.MaxRows = &v
}
if p.LastRunAt != nil {
t := *p.LastRunAt
cp.LastRunAt = &t
}
return &cp
}
func parseMaintCursor(cursor string) int {
if cursor == "" {
return 0
}
var off int
for _, r := range cursor {
if r < '0' || r > '9' {
return 0
}
off = off*10 + int(r-'0')
}
return off
}
func formatMaintCursor(off int) string {
return strconv.Itoa(off)
}