43 lines
971 B
Go
43 lines
971 B
Go
|
|
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()
|
||
|
|
})
|
||
|
|
}
|