Skip to content

Commit 56af0a2

Browse files
committed
Move metrics service into a cache
1 parent b917ef6 commit 56af0a2

File tree

7 files changed

+286
-154
lines changed

7 files changed

+286
-154
lines changed

coderd/coderd.go

+14
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package coderd
22

33
import (
4+
"context"
45
"crypto/x509"
56
"io"
67
"net/http"
@@ -26,6 +27,7 @@ import (
2627
"github.com/coder/coder/coderd/gitsshkey"
2728
"github.com/coder/coder/coderd/httpapi"
2829
"github.com/coder/coder/coderd/httpmw"
30+
"github.com/coder/coder/coderd/metricscache"
2931
"github.com/coder/coder/coderd/rbac"
3032
"github.com/coder/coder/coderd/telemetry"
3133
"github.com/coder/coder/coderd/tracing"
@@ -109,6 +111,13 @@ func New(options *Options) *API {
109111
panic(xerrors.Errorf("read site bin failed: %w", err))
110112
}
111113

114+
metricsCache := metricscache.New(
115+
options.Database,
116+
options.Logger.Named("metrics_cache"),
117+
)
118+
119+
metricsCache.Start(context.Background())
120+
112121
r := chi.NewRouter()
113122
api := &API{
114123
Options: options,
@@ -118,6 +127,7 @@ func New(options *Options) *API {
118127
Authorizer: options.Authorizer,
119128
Logger: options.Logger,
120129
},
130+
metricsCache: metricsCache,
121131
}
122132
api.workspaceAgentCache = wsconncache.New(api.dialWorkspaceAgent, 0)
123133
oauthConfigs := &httpmw.OAuth2Configs{
@@ -445,6 +455,8 @@ type API struct {
445455
websocketWaitGroup sync.WaitGroup
446456
workspaceAgentCache *wsconncache.Cache
447457
httpAuth *HTTPAuthorizer
458+
459+
metricsCache *metricscache.Cache
448460
}
449461

450462
// Close waits for all WebSocket connections to drain before returning.
@@ -453,6 +465,8 @@ func (api *API) Close() error {
453465
api.websocketWaitGroup.Wait()
454466
api.websocketWaitMutex.Unlock()
455467

468+
api.metricsCache.Close()
469+
456470
return api.workspaceAgentCache.Close()
457471
}
458472

coderd/coderdtest/authtest.go

+1
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,7 @@ func AGPLRoutes(a *AuthTester) (map[string]string, map[string]RouteCheck) {
198198
"POST:/api/v2/workspaceagents/me/keys": {NoAuthorize: true},
199199
"GET:/api/v2/workspaceagents/{workspaceagent}/iceservers": {NoAuthorize: true},
200200
"GET:/api/v2/workspaceagents/{workspaceagent}/derp": {NoAuthorize: true},
201+
"GET:/api/v2/metrics/report-agent-stats": {NoAuthorize: true},
201202

202203
// These endpoints have more assertions. This is good, add more endpoints to assert if you can!
203204
"GET:/api/v2/organizations/{organization}": {AssertObject: rbac.ResourceOrganization.InOrg(a.Admin.OrganizationID)},

coderd/metrics.go

+1-48
Original file line numberDiff line numberDiff line change
@@ -20,60 +20,13 @@ import (
2020
"github.com/coder/coder/codersdk"
2121
)
2222

23-
func FillEmptyDAUDays(rows []database.GetDAUsFromAgentStatsRow) []database.GetDAUsFromAgentStatsRow {
24-
var newRows []database.GetDAUsFromAgentStatsRow
25-
26-
for i, row := range rows {
27-
if i == 0 {
28-
newRows = append(newRows, row)
29-
continue
30-
}
31-
32-
last := rows[i-1]
33-
34-
const day = time.Hour * 24
35-
diff := row.Date.Sub(last.Date)
36-
for diff > day {
37-
if diff <= day {
38-
break
39-
}
40-
last.Date = last.Date.Add(day)
41-
last.Daus = 0
42-
newRows = append(newRows, last)
43-
diff -= day
44-
}
45-
46-
newRows = append(newRows, row)
47-
continue
48-
}
49-
50-
return newRows
51-
}
52-
5323
func (api *API) daus(rw http.ResponseWriter, r *http.Request) {
5424
if !api.Authorize(r, rbac.ActionRead, rbac.ResourceUser) {
5525
httpapi.Forbidden(rw)
5626
return
5727
}
5828

59-
daus, err := api.Database.GetDAUsFromAgentStats(r.Context())
60-
if err != nil {
61-
httpapi.Write(rw, http.StatusBadRequest, codersdk.Response{
62-
Message: "Failed to get DAUs.",
63-
Detail: err.Error(),
64-
})
65-
return
66-
}
67-
68-
var resp codersdk.GetDAUsResponse
69-
for _, ent := range FillEmptyDAUDays(daus) {
70-
resp.Entries = append(resp.Entries, codersdk.DAUEntry{
71-
Date: ent.Date,
72-
DAUs: int(ent.Daus),
73-
})
74-
}
75-
76-
httpapi.Write(rw, http.StatusOK, resp)
29+
httpapi.Write(rw, http.StatusOK, api.metricsCache.GetDAUs())
7730
}
7831

7932
const AgentStatIntervalEnv = "CODER_AGENT_STAT_INTERVAL_MS"

coderd/metrics_test.go

+4-106
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ package coderd_test
33
import (
44
"context"
55
"os"
6-
"reflect"
76
"testing"
87
"time"
98

@@ -14,7 +13,7 @@ import (
1413
"github.com/coder/coder/agent"
1514
"github.com/coder/coder/coderd"
1615
"github.com/coder/coder/coderd/coderdtest"
17-
"github.com/coder/coder/coderd/database"
16+
"github.com/coder/coder/coderd/metricscache"
1817
"github.com/coder/coder/codersdk"
1918
"github.com/coder/coder/peer"
2019
"github.com/coder/coder/provisioner/echo"
@@ -24,6 +23,7 @@ import (
2423

2524
func init() {
2625
os.Setenv(coderd.AgentStatIntervalEnv, "100")
26+
os.Setenv(metricscache.CacheRefreshIntervalEnv, "100")
2727
}
2828
func TestWorkspaceReportStats(t *testing.T) {
2929
t.Parallel()
@@ -92,7 +92,8 @@ func TestWorkspaceReportStats(t *testing.T) {
9292
require.NoError(t, err)
9393

9494
// Give enough time for stats to hit DB
95-
time.Sleep(time.Second * 1)
95+
// and metrics cache to refresh.
96+
time.Sleep(time.Second * 5)
9697

9798
daus, err := client.GetDAUsFromAgentStats(context.Background())
9899
require.NoError(t, err)
@@ -107,106 +108,3 @@ func TestWorkspaceReportStats(t *testing.T) {
107108
},
108109
}, daus)
109110
}
110-
111-
func date(year, month, day int) time.Time {
112-
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
113-
}
114-
115-
func TestFillEmptyDAUDays(t *testing.T) {
116-
t.Parallel()
117-
118-
type args struct {
119-
rows []database.GetDAUsFromAgentStatsRow
120-
}
121-
tests := []struct {
122-
name string
123-
args args
124-
want []database.GetDAUsFromAgentStatsRow
125-
}{
126-
{"empty", args{}, nil},
127-
{"no holes", args{
128-
rows: []database.GetDAUsFromAgentStatsRow{
129-
{
130-
Date: date(2022, 01, 01),
131-
Daus: 1,
132-
},
133-
{
134-
Date: date(2022, 01, 02),
135-
Daus: 1,
136-
},
137-
{
138-
Date: date(2022, 01, 03),
139-
Daus: 1,
140-
},
141-
},
142-
}, []database.GetDAUsFromAgentStatsRow{
143-
{
144-
Date: date(2022, 01, 01),
145-
Daus: 1,
146-
},
147-
{
148-
Date: date(2022, 01, 02),
149-
Daus: 1,
150-
},
151-
{
152-
Date: date(2022, 01, 03),
153-
Daus: 1,
154-
},
155-
}},
156-
{"holes", args{
157-
rows: []database.GetDAUsFromAgentStatsRow{
158-
{
159-
Date: date(2022, 1, 1),
160-
Daus: 3,
161-
},
162-
{
163-
Date: date(2022, 1, 4),
164-
Daus: 1,
165-
},
166-
{
167-
Date: date(2022, 1, 7),
168-
Daus: 3,
169-
},
170-
},
171-
}, []database.GetDAUsFromAgentStatsRow{
172-
{
173-
Date: date(2022, 1, 1),
174-
Daus: 3,
175-
},
176-
{
177-
Date: date(2022, 1, 2),
178-
Daus: 0,
179-
},
180-
{
181-
Date: date(2022, 1, 3),
182-
Daus: 0,
183-
},
184-
{
185-
Date: date(2022, 1, 4),
186-
Daus: 1,
187-
},
188-
{
189-
Date: date(2022, 1, 5),
190-
Daus: 0,
191-
},
192-
{
193-
Date: date(2022, 1, 6),
194-
Daus: 0,
195-
},
196-
{
197-
Date: date(2022, 1, 7),
198-
Daus: 3,
199-
},
200-
}},
201-
}
202-
for _, tt := range tests {
203-
tt := tt
204-
t.Run(tt.name, func(t *testing.T) {
205-
t.Parallel()
206-
207-
if got := coderd.FillEmptyDAUDays(tt.args.rows); !reflect.DeepEqual(got, tt.want) {
208-
t.Errorf("fillEmptyDAUDays() = %v, want %v", got, tt.want)
209-
}
210-
})
211-
}
212-
}

0 commit comments

Comments
 (0)