-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrate_limiter.go
More file actions
76 lines (65 loc) · 2.22 KB
/
rate_limiter.go
File metadata and controls
76 lines (65 loc) · 2.22 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
package ghratelimit
import (
"context"
"io"
"net/http"
)
// Each installer of a GitHub Application is rate limited with a Primary rate
// limit of 5000 GitHub API requests/hour.
//
// "Secondary" rate limit per installation: 100 concurrent requests.
//
// This does not affect any other applications that the customer may have
// installed, which will have their own separate resource allowances.
//
// Nor does it affect other installers of the same application.
// RateLimitTransport contains two rate limiting objects, to solve both the Primary
// and Secondary rate limit questions.
type RateLimitTransport[T http.RoundTripper] struct {
semaphore semaphore
headerLimiter *githubHeaderRateLimiter
BaseTransport T
}
// RoundTrip implements http.RoundTripper.
func (r *RateLimitTransport[T]) RoundTrip(req *http.Request) (resp *http.Response, err error) {
if err = r.Acquire(req.Context()); err != nil {
return
}
defer r.Release(resp)
resp, err = r.BaseTransport.RoundTrip(req)
return
}
var (
_ http.RoundTripper = (*RateLimitTransport[http.RoundTripper])(nil)
_ io.Closer = (*RateLimitTransport[http.RoundTripper])(nil)
)
// newRateLimiter creates a new rate limiter with both concurrency control and
// GitHub header-based rate limiting.
func newRateLimiter[T http.RoundTripper](rt T, maxConcurrent int64) *RateLimitTransport[T] {
return &RateLimitTransport[T]{
semaphore: newSemaphore(int(maxConcurrent)),
headerLimiter: newGitHubHeaderRateLimiter(maxConcurrent),
BaseTransport: rt,
}
}
func NewRateLimitTransport[T http.RoundTripper](rt T, maxConcurrent int64) *RateLimitTransport[T] {
return newRateLimiter(rt, maxConcurrent)
}
// Acquire blocks based on both throttling and concurrency limits.
func (r *RateLimitTransport[T]) Acquire(ctx context.Context) error {
if err := r.headerLimiter.Acquire(ctx); err != nil {
return err
}
return r.semaphore.Acquire(ctx)
}
// Release a slot and process the GitHub API response for potential throttling.
func (r *RateLimitTransport[T]) Release(resp *http.Response) {
r.semaphore.Release()
r.headerLimiter.AddResponse(resp)
}
// Close releases all resources.
func (r *RateLimitTransport[T]) Close() error {
r.semaphore.Close()
r.headerLimiter.Close()
return nil
}