Harden production workflows and agent admission
This commit is contained in:
@@ -24,10 +24,19 @@ type Spool struct {
|
||||
maxEvents int
|
||||
maxBytes int64
|
||||
|
||||
mu sync.Mutex
|
||||
nextSeq uint64
|
||||
files []entry // sorted ascending by seq
|
||||
bytes int64
|
||||
mu sync.Mutex
|
||||
nextSeq uint64
|
||||
files []entry // sorted ascending by seq
|
||||
bytes int64
|
||||
dropEvents int
|
||||
dropBytes int
|
||||
}
|
||||
|
||||
// DropStats reports events dropped due to bounded-spool overflow since the
|
||||
// previous call to Drops. Counters are reset on read.
|
||||
type DropStats struct {
|
||||
OverflowEvents int
|
||||
OverflowBytes int
|
||||
}
|
||||
|
||||
type entry struct {
|
||||
@@ -135,13 +144,31 @@ func (s *Spool) dropOldestLocked() {
|
||||
if len(s.files) == 0 {
|
||||
return
|
||||
}
|
||||
byCount := len(s.files) > s.maxEvents
|
||||
old := s.files[0]
|
||||
_ = os.Remove(filepath.Join(s.dir, old.name))
|
||||
s.bytes -= old.size
|
||||
s.files = s.files[1:]
|
||||
if byCount {
|
||||
s.dropEvents++
|
||||
} else {
|
||||
s.dropBytes++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Drops returns and resets the count of events dropped due to overflow since
|
||||
// the previous call. Callers use this to feed bounded-cardinality metrics and
|
||||
// throttled logs without spamming on every Enqueue.
|
||||
func (s *Spool) Drops() DropStats {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
d := DropStats{OverflowEvents: s.dropEvents, OverflowBytes: s.dropBytes}
|
||||
s.dropEvents = 0
|
||||
s.dropBytes = 0
|
||||
return d
|
||||
}
|
||||
|
||||
// Peek returns up to limit oldest items. Does not remove them.
|
||||
func (s *Spool) Peek(limit int) ([]Item, error) {
|
||||
s.mu.Lock()
|
||||
|
||||
@@ -55,6 +55,13 @@ func TestDropOldestByCount(t *testing.T) {
|
||||
if items[0].Event.EventID != "e2" {
|
||||
t.Fatalf("expected oldest dropped, got %q", items[0].Event.EventID)
|
||||
}
|
||||
d := s.Drops()
|
||||
if d.OverflowEvents != 2 || d.OverflowBytes != 0 {
|
||||
t.Fatalf("drop stats: %+v", d)
|
||||
}
|
||||
if again := s.Drops(); again.OverflowEvents != 0 || again.OverflowBytes != 0 {
|
||||
t.Fatalf("Drops must reset, got %+v", again)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDropOldestByBytes(t *testing.T) {
|
||||
@@ -71,6 +78,10 @@ func TestDropOldestByBytes(t *testing.T) {
|
||||
if s.Bytes() > 3000 {
|
||||
t.Fatalf("bytes=%d > 3000", s.Bytes())
|
||||
}
|
||||
d := s.Drops()
|
||||
if d.OverflowBytes == 0 {
|
||||
t.Fatalf("expected bytes-overflow drops to be counted, got %+v", d)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReopenAppliesLimits(t *testing.T) {
|
||||
|
||||
Reference in New Issue
Block a user