watchdog/internal/aggregate/custom_events.go
NotAShelf c3b77696aa
internal: centralize size/length constants; better DoS protection
...also adds a bounded custom event registry for cardinality control but
I ran out of space in the commit message. Praise be to the long
descriptions...

Signed-off-by: NotAShelf <raf@notashelf.dev>
Change-Id: Ic205f69804c7fb24c39fa84abdd9546b6a6a6964
2026-03-02 22:38:00 +03:00

56 lines
1.3 KiB
Go

package aggregate
import "sync"
// Maintains a bounded set of allowed custom event names
type CustomEventRegistry struct {
mu sync.RWMutex
events map[string]struct{}
maxEvents int
overflowCount int
}
// Creates a registry with a maximum number of unique event names
func NewCustomEventRegistry(maxEvents int) *CustomEventRegistry {
return &CustomEventRegistry{
events: make(map[string]struct{}, maxEvents),
maxEvents: maxEvents,
}
}
// Add attempts to add an event name to the registry
// Returns the event name if accepted, "other" if rejected due to limit
func (r *CustomEventRegistry) Add(eventName string) string {
// Fast path: check with read lock first
r.mu.RLock()
if _, exists := r.events[eventName]; exists {
r.mu.RUnlock()
return eventName
}
r.mu.RUnlock()
// Slow path: acquire write lock to add
r.mu.Lock()
defer r.mu.Unlock()
// Double-check after acquiring write lock
if _, exists := r.events[eventName]; exists {
return eventName
}
// Check limit
if len(r.events) >= r.maxEvents {
r.overflowCount++
return "other"
}
r.events[eventName] = struct{}{}
return eventName
}
// Returns the number of events rejected due to limit
func (r *CustomEventRegistry) OverflowCount() int {
r.mu.RLock()
defer r.mu.RUnlock()
return r.overflowCount
}