type BoundedFrequencyRunner … type rateLimiter … type nullLimiter … func (nullLimiter) TryAccept() bool { … } func (nullLimiter) Stop() { … } var _ … type timer … type realTimer … func (rt *realTimer) C() <-chan time.Time { … } func (rt *realTimer) Reset(d time.Duration) bool { … } func (rt *realTimer) Stop() bool { … } func (rt *realTimer) Now() time.Time { … } func (rt *realTimer) Remaining() time.Duration { … } func (rt *realTimer) Since(t time.Time) time.Duration { … } func (rt *realTimer) Sleep(d time.Duration) { … } var _ … // NewBoundedFrequencyRunner creates a new BoundedFrequencyRunner instance, // which will manage runs of the specified function. // // All runs will be async to the caller of BoundedFrequencyRunner.Run, but // multiple runs are serialized. If the function needs to hold locks, it must // take them internally. // // Runs of the function will have at least minInterval between them (from // completion to next start), except that up to bursts may be allowed. Burst // runs are "accumulated" over time, one per minInterval up to burstRuns total. // This can be used, for example, to mitigate the impact of expensive operations // being called in response to user-initiated operations. Run requests that // would violate the minInterval are coalesced and run at the next opportunity. // // The function will be run at least once per maxInterval. For example, this can // force periodic refreshes of state in the absence of anyone calling Run. // // Examples: // // NewBoundedFrequencyRunner("name", fn, time.Second, 5*time.Second, 1) // - fn will have at least 1 second between runs // - fn will have no more than 5 seconds between runs // // NewBoundedFrequencyRunner("name", fn, 3*time.Second, 10*time.Second, 3) // - fn will have at least 3 seconds between runs, with up to 3 burst runs // - fn will have no more than 10 seconds between runs // // The maxInterval must be greater than or equal to the minInterval, If the // caller passes a maxInterval less than minInterval, this function will panic. func NewBoundedFrequencyRunner(name string, fn func(), minInterval, maxInterval time.Duration, burstRuns int) *BoundedFrequencyRunner { … } // Make an instance with dependencies injected. func construct(name string, fn func(), minInterval, maxInterval time.Duration, burstRuns int, timer timer) *BoundedFrequencyRunner { … } // Loop handles the periodic timer and run requests. This is expected to be // called as a goroutine. func (bfr *BoundedFrequencyRunner) Loop(stop <-chan struct{ … } // Run the function as soon as possible. If this is called while Loop is not // running, the call may be deferred indefinitely. // If there is already a queued request to call the underlying function, it // may be dropped - it is just guaranteed that we will try calling the // underlying function as soon as possible starting from now. func (bfr *BoundedFrequencyRunner) Run() { … } // RetryAfter ensures that the function will run again after no later than interval. This // can be called from inside a run of the BoundedFrequencyRunner's function, or // asynchronously. func (bfr *BoundedFrequencyRunner) RetryAfter(interval time.Duration) { … } // assumes the lock is not held func (bfr *BoundedFrequencyRunner) stop() { … } // assumes the lock is not held func (bfr *BoundedFrequencyRunner) doRetry() { … } // assumes the lock is not held func (bfr *BoundedFrequencyRunner) tryRun() { … }