You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
83 lines
2.2 KiB
83 lines
2.2 KiB
|
2 months ago
|
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"`
|
||
|
|
}
|
||
|
|
|
||
|
|
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
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Store) recordCleared(event Envelope) bool {
|
||
|
|
return s.portRecordCleared(event) || s.systemRecordCleared(event)
|
||
|
|
}
|
||
|
|
|
||
|
|
func (s *Store) SystemClearRevision() uint64 {
|
||
|
|
s.mu.Lock()
|
||
|
|
defer s.mu.Unlock()
|
||
|
|
return s.systemClear.Revision
|
||
|
|
}
|
||
|
|
|
||
|
|
// ClearSystem never accepts a caller-supplied class or filesystem path. Audit,
|
||
|
|
// port, and link records are outside this operation, even in the same bucket.
|
||
|
|
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
|
||
|
|
}
|
||
|
|
next := systemClearState{Version: 1, Revision: s.systemClear.Revision + 1, NextSequence: s.classes[ClassSystem].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
|
||
|
|
return deleted, s.compactClearedClassLocked(ClassSystem, s.systemRecordCleared)
|
||
|
|
}
|