Skip to content

Commit a2db880

Browse files
committed
feat(store/github): expose github store metrics
Currently exposes rate limit status towards the GitHub API. These metrics are polled from the GitHub API on regular intervals. These requests are exempted from the general rate limits. The worker for polling this status is not started until the first call to `ReloadCache`. https://docs.github.com/en/rest/rate-limit/rate-limit?apiVersion=2022-11-28#get-rate-limit-status-for-the-authenticated-user
1 parent 699e523 commit a2db880

File tree

4 files changed

+81
-13
lines changed

4 files changed

+81
-13
lines changed

go.mod

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ toolchain go1.21.4
1212
require (
1313
github.com/aws/aws-sdk-go v1.50.25
1414
github.com/go-chi/chi/v5 v5.0.12
15-
github.com/google/go-github/v43 v43.0.0
15+
github.com/google/go-github/v59 v59.0.0
1616
github.com/hashicorp/go-version v1.6.0
1717
github.com/matryer/is v1.4.1
1818
github.com/migueleliasweb/go-github-mock v0.0.23
@@ -27,7 +27,6 @@ require (
2727
github.com/cespare/xxhash/v2 v2.2.0 // indirect
2828
github.com/davecgh/go-spew v1.1.1 // indirect
2929
github.com/golang/protobuf v1.5.3 // indirect
30-
github.com/google/go-github/v59 v59.0.0 // indirect
3130
github.com/google/go-querystring v1.1.0 // indirect
3231
github.com/gorilla/mux v1.8.0 // indirect
3332
github.com/jmespath/go-jmespath v0.4.0 // indirect
@@ -38,7 +37,6 @@ require (
3837
github.com/prometheus/procfs v0.12.0 // indirect
3938
github.com/stretchr/objx v0.5.0 // indirect
4039
go.uber.org/multierr v1.10.0 // indirect
41-
golang.org/x/crypto v0.19.0 // indirect
4240
golang.org/x/net v0.21.0 // indirect
4341
golang.org/x/sys v0.17.0 // indirect
4442
golang.org/x/time v0.3.0 // indirect

go.sum

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
1717
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
1818
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
1919
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
20-
github.com/google/go-github/v43 v43.0.0 h1:y+GL7LIsAIF2NZlJ46ZoC/D1W1ivZasT0lnWHMYPZ+U=
21-
github.com/google/go-github/v43 v43.0.0/go.mod h1:ZkTvvmCXBvsfPpTHXnH/d2hP9Y0cTbvN9kr5xqyXOIc=
2220
github.com/google/go-github/v59 v59.0.0 h1:7h6bgpF5as0YQLLkEiVqpgtJqjimMYhBkD4jT5aN3VA=
2321
github.com/google/go-github/v59 v59.0.0/go.mod h1:rJU4R0rQHFVFDOkqGWxfLNo6vEk4dv40oDjhV/gH6wM=
2422
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
@@ -68,8 +66,6 @@ go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8
6866
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
6967
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
7068
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
71-
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
72-
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
7369
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
7470
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
7571
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=

pkg/store/github/github.go

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -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.
91105
func (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.
205269
func (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
}

pkg/store/github/github_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"net/http"
1111
"testing"
1212

13-
"github.com/google/go-github/v43/github"
13+
"github.com/google/go-github/v59/github"
1414
"github.com/matryer/is"
1515
"github.com/migueleliasweb/go-github-mock/src/mock"
1616
"github.com/nrkno/terraform-registry/pkg/core"

0 commit comments

Comments
 (0)