-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgithub_rate_limiter.go
More file actions
79 lines (68 loc) · 2.17 KB
/
github_rate_limiter.go
File metadata and controls
79 lines (68 loc) · 2.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
package ghratelimit
import (
"context"
"net/http"
"time"
)
// githubHeaderRateLimiter manages rate limits based on GitHub rate limit headers.
type githubHeaderRateLimiter struct {
resetThreshold int64 // Throttle when remaining requests are below this threshold
responses chan *http.Response // Channel to process HTTP responses
lock chan struct{} // Channel that blocks when throttling
}
func (r *githubHeaderRateLimiter) Acquire(ctx context.Context) error {
select {
// Normal operation (Primary rate limit has NOT been reached):
// r.lock is CLOSED and therefore we fall through immediately
// to return successfully.
// LOCKED/BLOCKED (Primary rate limit has been reached):
// r.lock is an open channel, which will be closed when the
// rate limit resets, therefore allowing a successful return
// as long as the reset happens before the context expires.
case <-r.lock:
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func (r *githubHeaderRateLimiter) Close() {
close(r.responses)
}
// newGitHubHeaderRateLimiter creates a new rate limiter based on GitHub headers.
func newGitHubHeaderRateLimiter(threshold int64) *githubHeaderRateLimiter {
rateLimiter := &githubHeaderRateLimiter{
resetThreshold: threshold,
responses: make(chan *http.Response, threshold),
lock: make(chan struct{}),
}
close(rateLimiter.lock)
go rateLimiter.manageThrottle()
return rateLimiter
}
// manageThrottle manages rate limits based on incoming responses.
func (r *githubHeaderRateLimiter) manageThrottle() {
for {
select {
case resp, stillOpen := <-r.responses:
if !stillOpen {
return
}
if resp == nil {
continue
}
rateLimitInfo := NewGitHubRateLimitInfo(resp)
// If remaining requests drop below the threshold, start throttling
if rateLimitInfo.Remaining <= r.resetThreshold {
r.lock = make(chan struct{})
timer := time.NewTimer(rateLimitInfo.TimeToReset())
defer timer.Stop()
<-timer.C
close(r.lock)
}
}
}
}
// AddResponse processes the HTTP response headers for rate limiting.
func (r *githubHeaderRateLimiter) AddResponse(resp *http.Response) {
r.responses <- resp
}