@@ -10,8 +10,9 @@ import (
1010 "fmt"
1111 "strings"
1212 "sync"
13+ "time"
1314
14- "github.com/google/go-github/v43 /github"
15+ "github.com/google/go-github/v59 /github"
1516 goversion "github.com/hashicorp/go-version"
1617 "github.com/nrkno/terraform-registry/pkg/core"
1718 "github.com/prometheus/client_golang/prometheus"
@@ -27,9 +28,10 @@ type GitHubStore struct {
2728 // Topic to filter repositories by. Leave empty for all.
2829 topicFilter string
2930
30- client * github.Client
31- cache map [string ][]* core.ModuleVersion
32- mut sync.RWMutex
31+ client * github.Client
32+ cache map [string ][]* core.ModuleVersion
33+ mut sync.RWMutex
34+ metrics metrics
3335
3436 logger * zap.Logger
3537}
@@ -50,6 +52,17 @@ func NewGitHubStore(ownerFilter, topicFilter, accessToken string, logger *zap.Lo
5052 client : github .NewClient (c ),
5153 cache : make (map [string ][]* core.ModuleVersion ),
5254 logger : logger ,
55+ metrics : metrics {
56+ rateLimitCoreLimit : newGauge ("rate_limit_core_limit" ),
57+ rateLimitCoreRemaining : newGauge ("rate_limit_core_remaining" ),
58+ rateLimitCoreResetTimestamp : newGauge ("rate_limit_core_reset_timestamp" ),
59+ rateLimitSearchLimit : newGauge ("rate_limit_search_limit" ),
60+ rateLimitSearchRemaining : newGauge ("rate_limit_search_remaining" ),
61+ rateLimitSearchResetTimestamp : newGauge ("rate_limit_search_reset_timestamp" ),
62+ rateLimitGraphQLLimit : newGauge ("rate_limit_graphql_limit" ),
63+ rateLimitGraphQLRemaining : newGauge ("rate_limit_graphql_remaining" ),
64+ rateLimitGraphQLResetTimestamp : newGauge ("rate_limit_graphql_reset_timestamp" ),
65+ },
5366 }
5467}
5568
@@ -88,7 +101,18 @@ func (s *GitHubStore) GetModuleVersion(ctx context.Context, namespace, name, pro
88101// ReloadCache queries the GitHub API and reloads the local cache of module versions.
89102// Should be called at least once after initialisation and proably on regular
90103// intervals afterwards to keep cache up-to-date.
104+ // This method also starts the background worker for polling rate limit status of the GitHub API on first invocation.
91105func (s * GitHubStore ) ReloadCache (ctx context.Context ) error {
106+ // Start rate limit worker on first invocation
107+ sync .OnceFunc (func () {
108+ go func () {
109+ for {
110+ s .updateMetrics (context .Background ())
111+ time .Sleep (15 * time .Second )
112+ }
113+ }()
114+ })()
115+
92116 repos , err := s .searchRepositories (ctx )
93117 if err != nil {
94118 return err
@@ -202,6 +226,56 @@ func (s *GitHubStore) searchRepositories(ctx context.Context) ([]*github.Reposit
202226 return allRepos , nil
203227}
204228
229+ type metrics struct {
230+ rateLimitCoreLimit prometheus.Gauge
231+ rateLimitCoreRemaining prometheus.Gauge
232+ rateLimitCoreResetTimestamp prometheus.Gauge
233+ rateLimitSearchLimit prometheus.Gauge
234+ rateLimitSearchRemaining prometheus.Gauge
235+ rateLimitSearchResetTimestamp prometheus.Gauge
236+ rateLimitGraphQLLimit prometheus.Gauge
237+ rateLimitGraphQLRemaining prometheus.Gauge
238+ rateLimitGraphQLResetTimestamp prometheus.Gauge
239+ }
240+
241+ // newGauge is a helper function for creating gauges with a common namespace and subsystem.
242+ func newGauge (name string ) prometheus.Gauge {
243+ return prometheus .NewGauge (prometheus.GaugeOpts {
244+ Namespace : "terraform_registry" ,
245+ Subsystem : "store_github" ,
246+ Name : name ,
247+ })
248+ }
249+
250+ // updateMetrics updates all metrics that needs polling.
251+ func (s * GitHubStore ) updateMetrics (ctx context.Context ) {
252+ ratel , _ , err := s .client .RateLimits (ctx )
253+ if err != nil {
254+ s .logger .Warn ("failed to get rate limit status" , zap .Errors ("err" , []error {err }))
255+ } else {
256+ s .metrics .rateLimitCoreLimit .Set (float64 (ratel .Core .Limit ))
257+ s .metrics .rateLimitCoreRemaining .Set (float64 (ratel .Core .Remaining ))
258+ s .metrics .rateLimitCoreResetTimestamp .Set (float64 (ratel .Core .Reset .Unix ()))
259+ s .metrics .rateLimitSearchLimit .Set (float64 (ratel .Search .Limit ))
260+ s .metrics .rateLimitSearchRemaining .Set (float64 (ratel .Search .Remaining ))
261+ s .metrics .rateLimitSearchResetTimestamp .Set (float64 (ratel .Search .Reset .Unix ()))
262+ s .metrics .rateLimitGraphQLLimit .Set (float64 (ratel .GraphQL .Limit ))
263+ s .metrics .rateLimitGraphQLRemaining .Set (float64 (ratel .GraphQL .Remaining ))
264+ s .metrics .rateLimitGraphQLResetTimestamp .Set (float64 (ratel .GraphQL .Reset .Unix ()))
265+ }
266+ }
267+
268+ // Metrics returns a registry with metrics for this store.
205269func (s * GitHubStore ) Metrics () []prometheus.Collector {
206- return nil
270+ return []prometheus.Collector {
271+ s .metrics .rateLimitCoreLimit ,
272+ s .metrics .rateLimitCoreRemaining ,
273+ s .metrics .rateLimitCoreResetTimestamp ,
274+ s .metrics .rateLimitSearchLimit ,
275+ s .metrics .rateLimitSearchRemaining ,
276+ s .metrics .rateLimitSearchResetTimestamp ,
277+ s .metrics .rateLimitGraphQLLimit ,
278+ s .metrics .rateLimitGraphQLRemaining ,
279+ s .metrics .rateLimitGraphQLResetTimestamp ,
280+ }
207281}
0 commit comments