fix(coderd): use insights for DAUs, simplify metricscache (#12775)

Fixes #12134
Fixes https://github.com/coder/customers/issues/384
Refs #12122
This commit is contained in:
Mathias Fredriksson
2024-03-27 18:10:14 +02:00
committed by GitHub
parent 5d82a78d4c
commit 421bf7e785
7 changed files with 90 additions and 657 deletions

View File

@ -19,241 +19,10 @@ import (
"github.com/coder/coder/v2/testutil"
)
func dateH(year, month, day, hour int) time.Time {
return time.Date(year, time.Month(month), day, hour, 0, 0, 0, time.UTC)
}
func date(year, month, day int) time.Time {
return time.Date(year, time.Month(month), day, 0, 0, 0, 0, time.UTC)
}
func TestCache_TemplateUsers(t *testing.T) {
t.Parallel()
statRow := func(user uuid.UUID, date time.Time) database.InsertWorkspaceAgentStatParams {
return database.InsertWorkspaceAgentStatParams{
CreatedAt: date,
UserID: user,
}
}
var (
zebra = uuid.UUID{1}
tiger = uuid.UUID{2}
)
type args struct {
rows []database.InsertWorkspaceAgentStatParams
}
type want struct {
entries []codersdk.DAUEntry
uniqueUsers int
}
tests := []struct {
name string
args args
tplWant want
// dauWant is optional
dauWant []codersdk.DAUEntry
tzOffset int
}{
{name: "empty", args: args{}, tplWant: want{nil, 0}},
{
name: "one hole",
args: args{
rows: []database.InsertWorkspaceAgentStatParams{
statRow(zebra, dateH(2022, 8, 27, 0)),
statRow(zebra, dateH(2022, 8, 30, 0)),
},
},
tplWant: want{[]codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 8, 27)),
Amount: 1,
},
{
Date: metricscache.OnlyDate(date(2022, 8, 28)),
Amount: 0,
},
{
Date: metricscache.OnlyDate(date(2022, 8, 29)),
Amount: 0,
},
{
Date: metricscache.OnlyDate(date(2022, 8, 30)),
Amount: 1,
},
}, 1},
},
{
name: "no holes",
args: args{
rows: []database.InsertWorkspaceAgentStatParams{
statRow(zebra, dateH(2022, 8, 27, 0)),
statRow(zebra, dateH(2022, 8, 28, 0)),
statRow(zebra, dateH(2022, 8, 29, 0)),
},
},
tplWant: want{[]codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 8, 27)),
Amount: 1,
},
{
Date: metricscache.OnlyDate(date(2022, 8, 28)),
Amount: 1,
},
{
Date: metricscache.OnlyDate(date(2022, 8, 29)),
Amount: 1,
},
}, 1},
},
{
name: "holes",
args: args{
rows: []database.InsertWorkspaceAgentStatParams{
statRow(zebra, dateH(2022, 1, 1, 0)),
statRow(tiger, dateH(2022, 1, 1, 0)),
statRow(zebra, dateH(2022, 1, 4, 0)),
statRow(zebra, dateH(2022, 1, 7, 0)),
statRow(tiger, dateH(2022, 1, 7, 0)),
},
},
tplWant: want{[]codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 1, 1)),
Amount: 2,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 2)),
Amount: 0,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 3)),
Amount: 0,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 4)),
Amount: 1,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 5)),
Amount: 0,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 6)),
Amount: 0,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 7)),
Amount: 2,
},
}, 2},
},
{
name: "tzOffset",
tzOffset: 3,
args: args{
rows: []database.InsertWorkspaceAgentStatParams{
statRow(zebra, dateH(2022, 1, 2, 3)),
statRow(tiger, dateH(2022, 1, 2, 3)),
// With offset these should be in the previous day
statRow(zebra, dateH(2022, 1, 2, 0)),
statRow(tiger, dateH(2022, 1, 2, 0)),
},
},
tplWant: want{[]codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 1, 2)),
Amount: 2,
},
}, 2},
dauWant: []codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 1, 1)),
Amount: 2,
},
{
Date: metricscache.OnlyDate(date(2022, 1, 2)),
Amount: 2,
},
},
},
{
name: "tzOffsetPreviousDay",
tzOffset: 6,
args: args{
rows: []database.InsertWorkspaceAgentStatParams{
statRow(zebra, dateH(2022, 1, 2, 1)),
statRow(tiger, dateH(2022, 1, 2, 1)),
statRow(zebra, dateH(2022, 1, 2, 0)),
statRow(tiger, dateH(2022, 1, 2, 0)),
},
},
dauWant: []codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 1, 1)),
Amount: 2,
},
},
tplWant: want{[]codersdk.DAUEntry{
{
Date: metricscache.OnlyDate(date(2022, 1, 2)),
Amount: 2,
},
}, 2},
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
var (
db = dbmem.New()
cache = metricscache.New(db, slogtest.Make(t, nil), metricscache.Intervals{
TemplateDAUs: testutil.IntervalFast,
})
)
defer cache.Close()
template := dbgen.Template(t, db, database.Template{
Provisioner: database.ProvisionerTypeEcho,
})
for _, row := range tt.args.rows {
row.TemplateID = template.ID
row.ConnectionCount = 1
db.InsertWorkspaceAgentStat(context.Background(), row)
}
require.Eventuallyf(t, func() bool {
_, _, ok := cache.TemplateDAUs(template.ID, tt.tzOffset)
return ok
}, testutil.WaitShort, testutil.IntervalMedium,
"TemplateDAUs never populated",
)
gotUniqueUsers, ok := cache.TemplateUniqueUsers(template.ID)
require.True(t, ok)
if tt.dauWant != nil {
_, dauResponse, ok := cache.DeploymentDAUs(tt.tzOffset)
require.True(t, ok)
require.Equal(t, tt.dauWant, dauResponse.Entries)
}
offset, gotEntries, ok := cache.TemplateDAUs(template.ID, tt.tzOffset)
require.True(t, ok)
// Template only supports 0 offset.
require.Equal(t, 0, offset)
require.Equal(t, tt.tplWant.entries, gotEntries.Entries)
require.Equal(t, tt.tplWant.uniqueUsers, gotUniqueUsers)
})
}
}
func TestCache_TemplateWorkspaceOwners(t *testing.T) {
t.Parallel()
var ()
@ -261,7 +30,7 @@ func TestCache_TemplateWorkspaceOwners(t *testing.T) {
var (
db = dbmem.New()
cache = metricscache.New(db, slogtest.Make(t, nil), metricscache.Intervals{
TemplateDAUs: testutil.IntervalFast,
TemplateBuildTimes: testutil.IntervalFast,
})
)
@ -412,7 +181,7 @@ func TestCache_BuildTime(t *testing.T) {
var (
db = dbmem.New()
cache = metricscache.New(db, slogtest.Make(t, nil), metricscache.Intervals{
TemplateDAUs: testutil.IntervalFast,
TemplateBuildTimes: testutil.IntervalFast,
})
)