|
|
|
|
package logstorage
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"os"
|
|
|
|
|
"path/filepath"
|
|
|
|
|
|
|
|
|
|
"tera-clone/services/tera-api/internal/storage/atomicfile"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
type systemClearState struct {
|
|
|
|
|
Version int `json:"version"`
|
|
|
|
|
Revision uint64 `json:"revision"`
|
|
|
|
|
NextSequence uint64 `json:"next_sequence"`
|
|
|
|
|
AuditNextSequence uint64 `json:"audit_next_sequence,omitempty"`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) systemClearPath() string {
|
|
|
|
|
return filepath.Join(s.root, "system", "cleared-system-v1.json")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) loadSystemClearLocked() error {
|
|
|
|
|
info, err := os.Lstat(s.systemClearPath())
|
|
|
|
|
if os.IsNotExist(err) {
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if !info.Mode().IsRegular() || info.Size() > 1024 {
|
|
|
|
|
return errors.New("invalid system log deletion checkpoint")
|
|
|
|
|
}
|
|
|
|
|
data, err := os.ReadFile(s.systemClearPath())
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var state systemClearState
|
|
|
|
|
if err := json.Unmarshal(data, &state); err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
if state.Version != 1 || state.NextSequence == 0 || state.Revision == 0 {
|
|
|
|
|
return errors.New("invalid system log deletion checkpoint")
|
|
|
|
|
}
|
|
|
|
|
s.systemClear = state
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) systemRecordCleared(event Envelope) bool {
|
|
|
|
|
return event.Class == ClassSystem && event.Sequence < s.systemClear.NextSequence ||
|
|
|
|
|
event.Class == ClassAudit && event.Sequence < s.systemClear.AuditNextSequence
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) recordCleared(event Envelope) bool {
|
|
|
|
|
return s.portRecordCleared(event) || s.systemRecordCleared(event) || s.linkRecordCleared(event)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s *Store) SystemClearRevision() uint64 {
|
|
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
return s.systemClear.Revision
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ClearSystem clears system and audit records. Port and link records are preserved.
|
|
|
|
|
func (s *Store) ClearSystem() (int, error) {
|
|
|
|
|
s.mu.Lock()
|
|
|
|
|
defer s.mu.Unlock()
|
|
|
|
|
deleted := 0
|
|
|
|
|
if err := s.walkClassLocked(ClassSystem, false, func(Envelope) error { deleted++; return nil }); err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
if err := s.walkClassLocked(ClassAudit, false, func(Envelope) error { deleted++; return nil }); err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
next := systemClearState{Version: 1, Revision: s.systemClear.Revision + 1, NextSequence: s.classes[ClassSystem].NextSequence, AuditNextSequence: s.classes[ClassAudit].NextSequence}
|
|
|
|
|
data, err := json.Marshal(next)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
if err := atomicfile.Write(s.systemClearPath(), append(data, '\n'), 0o600); err != nil {
|
|
|
|
|
_ = s.loadSystemClearLocked()
|
|
|
|
|
return 0, err
|
|
|
|
|
}
|
|
|
|
|
s.systemClear = next
|
|
|
|
|
if err := s.compactClearedClassLocked(ClassSystem, s.systemRecordCleared); err != nil {
|
|
|
|
|
return deleted, err
|
|
|
|
|
}
|
|
|
|
|
return deleted, s.compactClearedClassLocked(ClassAudit, s.systemRecordCleared)
|
|
|
|
|
}
|