Skip to content

Commit c6b087a

Browse files
committed
Implement in metricscache
1 parent 1a5d3ea commit c6b087a

File tree

6 files changed

+67
-44
lines changed

6 files changed

+67
-44
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ site/out/
3939
*.lock.hcl
4040
.terraform/
4141

42-
.vscode/*.log
42+
.vscode/*
4343
**/*.swp
4444
.coderv2/*
4545
**/__debug_bin

coderd/database/databasefake/databasefake.go

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -163,34 +163,37 @@ func (q *fakeQuerier) GetTemplateDAUs(_ context.Context, templateID uuid.UUID) (
163163
q.mutex.Lock()
164164
defer q.mutex.Unlock()
165165

166-
counts := make(map[time.Time]map[string]struct{})
166+
seens := make(map[time.Time]map[uuid.UUID]struct{})
167167

168168
for _, as := range q.agentStats {
169169
if as.TemplateID != templateID {
170170
continue
171171
}
172172

173173
date := as.CreatedAt.Truncate(time.Hour * 24)
174-
dateEntry := counts[date]
174+
175+
dateEntry := seens[date]
175176
if dateEntry == nil {
176-
dateEntry = make(map[string]struct{})
177+
dateEntry = make(map[uuid.UUID]struct{})
177178
}
178-
counts[date] = dateEntry
179-
180-
dateEntry[as.UserID.String()] = struct{}{}
179+
dateEntry[as.ID] = struct{}{}
180+
seens[date] = dateEntry
181181
}
182182

183-
countKeys := maps.Keys(counts)
183+
countKeys := maps.Keys(seens)
184184
sort.Slice(countKeys, func(i, j int) bool {
185185
return countKeys[i].Before(countKeys[j])
186186
})
187187

188188
var rs []database.GetTemplateDAUsRow
189189
for _, key := range countKeys {
190-
rs = append(rs, database.GetTemplateDAUsRow{
191-
Date: key,
192-
Amount: int64(len(counts[key])),
193-
})
190+
ids := seens[key]
191+
for id := range ids {
192+
rs = append(rs, database.GetTemplateDAUsRow{
193+
Date: key,
194+
UserID: id,
195+
})
196+
}
194197
}
195198

196199
return rs, nil

coderd/database/queries.sql.go

Lines changed: 4 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

coderd/database/queries/agentstats.sql

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,12 @@ VALUES
1515
-- name: GetTemplateDAUs :many
1616
select
1717
(created_at at TIME ZONE 'UTC')::date as date,
18-
count(distinct(user_id)) as amount
18+
user_id
1919
from
2020
agent_stats
2121
where template_id = $1
2222
group by
23-
date
23+
date, user_id
2424
order by
2525
date asc;
2626

coderd/metricscache/metricscache.go

Lines changed: 44 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"sync/atomic"
66
"time"
77

8+
"golang.org/x/exp/maps"
9+
"golang.org/x/exp/slices"
810
"golang.org/x/xerrors"
911

1012
"github.com/google/uuid"
@@ -24,7 +26,7 @@ type Cache struct {
2426
database database.Store
2527
log slog.Logger
2628

27-
templateDAUResponses atomic.Pointer[map[string]codersdk.TemplateDAUsResponse]
29+
templateDAUResponses atomic.Pointer[map[uuid.UUID]codersdk.TemplateDAUsResponse]
2830

2931
doneCh chan struct{}
3032
cancel func()
@@ -49,34 +51,60 @@ func New(db database.Store, log slog.Logger, interval time.Duration) *Cache {
4951
return c
5052
}
5153

52-
func fillEmptyDays(rows []database.GetTemplateDAUsRow) []database.GetTemplateDAUsRow {
53-
var newRows []database.GetTemplateDAUsRow
54+
func fillEmptyDays(sortedDates []time.Time) []time.Time {
55+
var newDates []time.Time
5456

55-
for i, row := range rows {
57+
for i, ti := range sortedDates {
5658
if i == 0 {
57-
newRows = append(newRows, row)
59+
newDates = append(newDates, ti)
5860
continue
5961
}
6062

61-
last := rows[i-1]
63+
last := sortedDates[i-1]
6264

6365
const day = time.Hour * 24
64-
diff := row.Date.Sub(last.Date)
66+
diff := ti.Sub(last)
6567
for diff > day {
6668
if diff <= day {
6769
break
6870
}
69-
last.Date = last.Date.Add(day)
70-
last.Amount = 0
71-
newRows = append(newRows, last)
71+
last = last.Add(day)
72+
newDates = append(newDates, last)
7273
diff -= day
7374
}
7475

75-
newRows = append(newRows, row)
76+
newDates = append(newDates, ti)
7677
continue
7778
}
7879

79-
return newRows
80+
return newDates
81+
}
82+
83+
func convertDAUResponse(rows []database.GetTemplateDAUsRow) codersdk.TemplateDAUsResponse {
84+
respMap := make(map[time.Time][]uuid.UUID)
85+
for _, row := range rows {
86+
uuids := respMap[row.Date]
87+
if uuids == nil {
88+
uuids = make([]uuid.UUID, 0, 8)
89+
}
90+
uuids = append(uuids, row.UserID)
91+
respMap[row.Date] = uuids
92+
}
93+
94+
dates := maps.Keys(respMap)
95+
slices.SortFunc(dates, func(a, b time.Time) bool {
96+
return a.Before(b)
97+
})
98+
99+
var resp codersdk.TemplateDAUsResponse
100+
for _, date := range fillEmptyDays(dates) {
101+
resp.Entries = append(resp.Entries, codersdk.DAUEntry{
102+
Date: date,
103+
Amount: len(respMap[date]),
104+
})
105+
}
106+
107+
return resp
80108
}
81109

82110
func (c *Cache) refresh(ctx context.Context) error {
@@ -90,22 +118,14 @@ func (c *Cache) refresh(ctx context.Context) error {
90118
return err
91119
}
92120

93-
templateDAUs := make(map[string]codersdk.TemplateDAUsResponse, len(templates))
121+
templateDAUs := make(map[uuid.UUID]codersdk.TemplateDAUsResponse, len(templates))
94122

95123
for _, template := range templates {
96-
daus, err := c.database.GetTemplateDAUs(ctx, template.ID)
124+
rows, err := c.database.GetTemplateDAUs(ctx, template.ID)
97125
if err != nil {
98126
return err
99127
}
100-
101-
var resp codersdk.TemplateDAUsResponse
102-
for _, ent := range fillEmptyDays(daus) {
103-
resp.Entries = append(resp.Entries, codersdk.DAUEntry{
104-
Date: ent.Date,
105-
Amount: int(ent.Amount),
106-
})
107-
}
108-
templateDAUs[template.ID.String()] = resp
128+
templateDAUs[template.ID] = convertDAUResponse(rows)
109129
}
110130

111131
c.templateDAUResponses.Store(&templateDAUs)
@@ -163,7 +183,7 @@ func (c *Cache) TemplateDAUs(id uuid.UUID) codersdk.TemplateDAUsResponse {
163183
return codersdk.TemplateDAUsResponse{}
164184
}
165185

166-
resp, ok := (*m)[id.String()]
186+
resp, ok := (*m)[id]
167187
if !ok {
168188
// Probably no data.
169189
return codersdk.TemplateDAUsResponse{}

coderd/metricscache/metricscache_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ func TestCache(t *testing.T) {
157157
t.Parallel()
158158
var (
159159
db = databasefake.New()
160-
cache = metricscache.New(db, slogtest.Make(t, nil), time.Millisecond*100)
160+
cache = metricscache.New(db, slogtest.Make(t, nil), testutil.IntervalFast)
161161
)
162162

163163
defer cache.Close()
@@ -177,7 +177,7 @@ func TestCache(t *testing.T) {
177177
require.Eventuallyf(t, func() bool {
178178
got = cache.TemplateDAUs(templateID)
179179
return reflect.DeepEqual(got.Entries, tt.want)
180-
}, testutil.WaitShort, testutil.IntervalFast,
180+
}, testutil.WaitShort, testutil.IntervalMedium,
181181
"GetDAUs() = %v, want %v", got, tt.want,
182182
)
183183
})

0 commit comments

Comments
 (0)