package graph import ( "sync" "time" ) // Debouncer coalesces rapid calls with the same key, executing only the last // one after a configurable delay. This prevents redundant work when multiple // updates arrive in quick succession (e.g., rapid schema publishing). type Debouncer struct { mu sync.Mutex delay time.Duration timers map[string]*time.Timer } // NewDebouncer creates a Debouncer with the given delay window. func NewDebouncer(delay time.Duration) *Debouncer { return &Debouncer{ delay: delay, timers: make(map[string]*time.Timer), } } // Debounce resets the timer for key. When the timer fires (after delay with no // new calls for the same key), fn is executed in a new goroutine. func (d *Debouncer) Debounce(key string, fn func()) { d.mu.Lock() defer d.mu.Unlock() if t, ok := d.timers[key]; ok { t.Stop() } d.timers[key] = time.AfterFunc(d.delay, func() { d.mu.Lock() delete(d.timers, key) d.mu.Unlock() fn() }) }