Skip to content

feat/throttled-dispatcher#8

Closed
mborders wants to merge 2 commits into
masterfrom
feature/throttled-dispatcher
Closed

feat/throttled-dispatcher#8
mborders wants to merge 2 commits into
masterfrom
feature/throttled-dispatcher

Conversation

@mborders

Copy link
Copy Markdown
Owner

No description provided.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new “throttled dispatcher” wrapper to rate-limit job dispatching (requested in Issue #6: Throttling) so callers can constrain how many jobs are queued over a time interval.

Changes:

  • Introduces ThrottleDispatcher with a token-bucket style throttling mechanism around the existing Dispatcher.
  • Adds a unit test validating that dispatching is limited to a configured number of jobs per time window.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 6 comments.

File Description
throttled_dispatcher.go New throttling wrapper around Dispatcher using a ticker-based token bucket and stop channel.
throttled_dispatcher_test.go Adds a timing-based test to verify throttling behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread throttled_dispatcher.go
Comment on lines +5 to +12
// ThrottleDispatcher wraps a Dispatcher with rate-limiting behavior,
// allowing at most maxJobs to be dispatched per interval.
type ThrottleDispatcher struct {
*Dispatcher
maxJobs int
interval time.Duration
tokens chan struct{}
quit chan bool
Comment thread throttled_dispatcher.go
Comment on lines +38 to +42
// Stop halts the throttled dispatcher and stops token replenishment.
func (td *ThrottleDispatcher) Stop() {
td.quit <- true
td.Dispatcher.Stop()
}
Comment thread throttled_dispatcher.go
Comment on lines +65 to +69
// Dispatch waits for a throttle token before queuing the job,
// ensuring no more than maxJobs are dispatched per interval.
func (td *ThrottleDispatcher) Dispatch(run func()) error {
<-td.tokens // Block until a token is available
return td.Dispatcher.Dispatch(run)
Comment thread throttled_dispatcher.go
Comment on lines +15 to +22
// NewThrottledDispatcher creates a new dispatcher that limits job execution
// to at most maxJobs per interval (e.g., 5 jobs per time.Minute).
func NewThrottledDispatcher(maxWorkers int, maxQueue int, maxJobs int, interval time.Duration) *ThrottleDispatcher {
tokens := make(chan struct{}, maxJobs)
// Pre-fill the token bucket
for i := 0; i < maxJobs; i++ {
tokens <- struct{}{}
}
Comment thread throttled_dispatcher.go
Comment on lines +44 to +67
// replenish refills the token bucket at the configured rate.
// Tokens are added in batches of maxJobs every interval.
func (td *ThrottleDispatcher) replenish() {
ticker := time.NewTicker(td.interval)
defer ticker.Stop()
for {
select {
case <-ticker.C:
for i := 0; i < td.maxJobs; i++ {
select {
case td.tokens <- struct{}{}:
default:
// Bucket is full; discard extra tokens
}
}
case <-td.quit:
return
}
}
}

// Dispatch waits for a throttle token before queuing the job,
// ensuring no more than maxJobs are dispatched per interval.
func (td *ThrottleDispatcher) Dispatch(run func()) error {
Comment on lines +33 to +37
// Wait for second window to open
time.Sleep(600 * time.Millisecond)
total := atomic.LoadInt32(&count)
if total != 6 {
t.Errorf("expected 6 total jobs, got %d", total)
@mborders mborders closed this May 20, 2026
@mborders
mborders deleted the feature/throttled-dispatcher branch May 20, 2026 01:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants