Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit0a95dcd

Browse files
committed
report html first served at with a TelemetryItem
1 parent6554aa3 commit0a95dcd

File tree

4 files changed

+152
-71
lines changed

4 files changed

+152
-71
lines changed

‎coderd/database/dbgen/dbgen.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,23 @@ func ProvisionerJobTimings(t testing.TB, db database.Store, build database.Works
10931093
returntimings
10941094
}
10951095

1096+
funcTelemetryItem(t testing.TB,db database.Store,seed database.TelemetryItem) database.TelemetryItem {
1097+
ifseed.Key=="" {
1098+
seed.Key=testutil.GetRandomName(t)
1099+
}
1100+
ifseed.Value=="" {
1101+
seed.Value=time.Now().Format(time.RFC3339)
1102+
}
1103+
err:=db.UpsertTelemetryItem(genCtx, database.UpsertTelemetryItemParams{
1104+
Key:seed.Key,
1105+
Value:seed.Value,
1106+
})
1107+
require.NoError(t,err,"upsert telemetry item")
1108+
item,err:=db.GetTelemetryItem(genCtx,seed.Key)
1109+
require.NoError(t,err,"get telemetry item")
1110+
returnitem
1111+
}
1112+
10961113
funcprovisionerJobTiming(t testing.TB,db database.Store,seed database.ProvisionerJobTiming) database.ProvisionerJobTiming {
10971114
timing,err:=db.InsertProvisionerJobTimings(genCtx, database.InsertProvisionerJobTimingsParams{
10981115
JobID:takeFirst(seed.JobID,uuid.New()),

‎coderd/telemetry/telemetry.go

Lines changed: 55 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -258,32 +258,26 @@ func (r *remoteReporter) deployment() error {
258258
r.options.Logger.Debug(r.ctx,"check IDP org sync",slog.Error(err))
259259
}
260260

261-
htmlFirstServedAt,err:=getHTMLFirstServedAt(r.ctx,r.options.Database)
262-
iferr!=nil&&!errors.Is(err,sql.ErrNoRows) {
263-
r.options.Logger.Debug(r.ctx,"get telemetry html first served at",slog.Error(err))
264-
}
265-
266261
data,err:=json.Marshal(&Deployment{
267-
ID:r.options.DeploymentID,
268-
Architecture:sysInfo.Architecture,
269-
BuiltinPostgres:r.options.BuiltinPostgres,
270-
Containerized:containerized,
271-
Config:r.options.DeploymentConfig,
272-
Kubernetes:os.Getenv("KUBERNETES_SERVICE_HOST")!="",
273-
InstallSource:installSource,
274-
Tunnel:r.options.Tunnel,
275-
OSType:sysInfo.OS.Type,
276-
OSFamily:sysInfo.OS.Family,
277-
OSPlatform:sysInfo.OS.Platform,
278-
OSName:sysInfo.OS.Name,
279-
OSVersion:sysInfo.OS.Version,
280-
CPUCores:runtime.NumCPU(),
281-
MemoryTotal:mem.Total,
282-
MachineID:sysInfo.UniqueID,
283-
StartedAt:r.startedAt,
284-
ShutdownAt:r.shutdownAt,
285-
IDPOrgSync:&idpOrgSync,
286-
HTMLFirstServedAt:htmlFirstServedAt,
262+
ID:r.options.DeploymentID,
263+
Architecture:sysInfo.Architecture,
264+
BuiltinPostgres:r.options.BuiltinPostgres,
265+
Containerized:containerized,
266+
Config:r.options.DeploymentConfig,
267+
Kubernetes:os.Getenv("KUBERNETES_SERVICE_HOST")!="",
268+
InstallSource:installSource,
269+
Tunnel:r.options.Tunnel,
270+
OSType:sysInfo.OS.Type,
271+
OSFamily:sysInfo.OS.Family,
272+
OSPlatform:sysInfo.OS.Platform,
273+
OSName:sysInfo.OS.Name,
274+
OSVersion:sysInfo.OS.Version,
275+
CPUCores:runtime.NumCPU(),
276+
MemoryTotal:mem.Total,
277+
MachineID:sysInfo.UniqueID,
278+
StartedAt:r.startedAt,
279+
ShutdownAt:r.shutdownAt,
280+
IDPOrgSync:&idpOrgSync,
287281
})
288282
iferr!=nil {
289283
returnxerrors.Errorf("marshal deployment: %w",err)
@@ -605,6 +599,17 @@ func (r *remoteReporter) createSnapshot() (*Snapshot, error) {
605599
}
606600
returnnil
607601
})
602+
eg.Go(func()error {
603+
items,err:=r.options.Database.GetTelemetryItems(ctx)
604+
iferr!=nil {
605+
returnxerrors.Errorf("get telemetry items: %w",err)
606+
}
607+
snapshot.TelemetryItems=make([]TelemetryItem,0,len(items))
608+
for_,item:=rangeitems {
609+
snapshot.TelemetryItems=append(snapshot.TelemetryItems,ConvertTelemetryItem(item))
610+
}
611+
returnnil
612+
})
608613

609614
err:=eg.Wait()
610615
iferr!=nil {
@@ -1011,6 +1016,15 @@ func ConvertOrganization(org database.Organization) Organization {
10111016
}
10121017
}
10131018

1019+
funcConvertTelemetryItem(item database.TelemetryItem)TelemetryItem {
1020+
returnTelemetryItem{
1021+
Key:item.Key,
1022+
Value:item.Value,
1023+
CreatedAt:item.CreatedAt,
1024+
UpdatedAt:item.UpdatedAt,
1025+
}
1026+
}
1027+
10141028
// Snapshot represents a point-in-time anonymized database dump.
10151029
// Data is aggregated by latest on the server-side, so partial data
10161030
// can be sent without issue.
@@ -1038,6 +1052,7 @@ type Snapshot struct {
10381052
Workspaces []Workspace`json:"workspaces"`
10391053
NetworkEvents []NetworkEvent`json:"network_events"`
10401054
Organizations []Organization`json:"organizations"`
1055+
TelemetryItems []TelemetryItem`json:"telemetry_items"`
10411056
}
10421057

10431058
// Deployment contains information about the host running Coder.
@@ -1062,8 +1077,7 @@ type Deployment struct {
10621077
ShutdownAt*time.Time`json:"shutdown_at"`
10631078
// While IDPOrgSync will always be set, it's nullable to make
10641079
// the struct backwards compatible with older coder versions.
1065-
IDPOrgSync*bool`json:"idp_org_sync"`
1066-
HTMLFirstServedAt*time.Time`json:"html_first_served_at"`
1080+
IDPOrgSync*bool`json:"idp_org_sync"`
10671081
}
10681082

10691083
typeAPIKeystruct {
@@ -1563,6 +1577,20 @@ type Organization struct {
15631577
CreatedAt time.Time`json:"created_at"`
15641578
}
15651579

1580+
//revive:disable:exported
1581+
typeTelemetryItemKeystring
1582+
1583+
const (
1584+
TelemetryItemKeyHTMLFirstServedAtTelemetryItemKey="html_first_served_at"
1585+
)
1586+
1587+
typeTelemetryItemstruct {
1588+
Keystring`json:"key"`
1589+
Valuestring`json:"value"`
1590+
CreatedAt time.Time`json:"created_at"`
1591+
UpdatedAt time.Time`json:"updated_at"`
1592+
}
1593+
15661594
typenoopReporterstruct{}
15671595

15681596
func (*noopReporter)Report(_*Snapshot) {}

‎coderd/telemetry/telemetry_test.go

Lines changed: 46 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,10 @@ func TestTelemetry(t *testing.T) {
7575
Health:database.WorkspaceAppHealthDisabled,
7676
OpenIn:database.WorkspaceAppOpenInSlimWindow,
7777
})
78+
_=dbgen.TelemetryItem(t,db, database.TelemetryItem{
79+
Key:string(telemetry.TelemetryItemKeyHTMLFirstServedAt),
80+
Value:time.Now().Format(time.RFC3339),
81+
})
7882
group:=dbgen.Group(t,db, database.Group{})
7983
_=dbgen.GroupMember(t,db, database.GroupMemberTable{UserID:user.ID,GroupID:group.ID})
8084
wsagent:=dbgen.WorkspaceAgent(t,db, database.WorkspaceAgent{})
@@ -127,7 +131,7 @@ func TestTelemetry(t *testing.T) {
127131
require.Len(t,snapshot.WorkspaceProxies,1)
128132
require.Len(t,snapshot.WorkspaceModules,1)
129133
require.Len(t,snapshot.Organizations,1)
130-
134+
require.Len(t,snapshot.TelemetryItems,1)
131135
wsa:=snapshot.WorkspaceAgents[0]
132136
require.Len(t,wsa.Subsystems,2)
133137
require.Equal(t,string(database.WorkspaceAgentSubsystemEnvbox),wsa.Subsystems[0])
@@ -306,25 +310,6 @@ func TestTelemetry(t *testing.T) {
306310
deployment,_=collectSnapshot(t,db,nil)
307311
require.True(t,*deployment.IDPOrgSync)
308312
})
309-
t.Run("HTMLFirstServedAt",func(t*testing.T) {
310-
t.Parallel()
311-
db,_:=dbtestutil.NewDB(t)
312-
deployment,_:=collectSnapshot(t,db,nil)
313-
require.Nil(t,deployment.HTMLFirstServedAt)
314-
315-
ctx:=testutil.Context(t,testutil.WaitMedium)
316-
now:=time.Now().Format(time.RFC3339)
317-
parsedNow,err:=time.Parse(time.RFC3339,now)
318-
require.NoError(t,err)
319-
require.NoError(t,db.SetTelemetryHTMLFirstServedAt(ctx,now))
320-
deployment,_=collectSnapshot(t,db,nil)
321-
require.Equal(t,*deployment.HTMLFirstServedAt,parsedNow)
322-
323-
// Test idempotency
324-
require.NoError(t,db.SetTelemetryHTMLFirstServedAt(ctx,time.Now().Add(time.Hour).Format(time.RFC3339)))
325-
deployment,_=collectSnapshot(t,db,nil)
326-
require.Equal(t,*deployment.HTMLFirstServedAt,parsedNow)
327-
})
328313
}
329314

330315
// nolint:paralleltest
@@ -335,6 +320,47 @@ func TestTelemetryInstallSource(t *testing.T) {
335320
require.Equal(t,"aws_marketplace",deployment.InstallSource)
336321
}
337322

323+
funcTestTelemetryItem(t*testing.T) {
324+
t.Parallel()
325+
ctx:=testutil.Context(t,testutil.WaitMedium)
326+
db,_:=dbtestutil.NewDB(t)
327+
key:=testutil.GetRandomName(t)
328+
value:=time.Now().Format(time.RFC3339)
329+
330+
err:=db.InsertTelemetryItemIfNotExists(ctx, database.InsertTelemetryItemIfNotExistsParams{
331+
Key:key,
332+
Value:value,
333+
})
334+
require.NoError(t,err)
335+
336+
item,err:=db.GetTelemetryItem(ctx,key)
337+
require.NoError(t,err)
338+
require.Equal(t,item.Key,key)
339+
require.Equal(t,item.Value,value)
340+
341+
// Inserting a new value should not update the existing value
342+
err=db.InsertTelemetryItemIfNotExists(ctx, database.InsertTelemetryItemIfNotExistsParams{
343+
Key:key,
344+
Value:"new_value",
345+
})
346+
require.NoError(t,err)
347+
348+
item,err=db.GetTelemetryItem(ctx,key)
349+
require.NoError(t,err)
350+
require.Equal(t,item.Value,value)
351+
352+
// Upserting a new value should update the existing value
353+
err=db.UpsertTelemetryItem(ctx, database.UpsertTelemetryItemParams{
354+
Key:key,
355+
Value:"new_value",
356+
})
357+
require.NoError(t,err)
358+
359+
item,err=db.GetTelemetryItem(ctx,key)
360+
require.NoError(t,err)
361+
require.Equal(t,item.Value,"new_value")
362+
}
363+
338364
funccollectSnapshot(t*testing.T,db database.Store,addOptionsFnfunc(opts telemetry.Options) telemetry.Options) (*telemetry.Deployment,*telemetry.Snapshot) {
339365
t.Helper()
340366
deployment:=make(chan*telemetry.Deployment,64)

‎site/site.go

Lines changed: 34 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -330,29 +330,35 @@ func ShouldCacheFile(reqFile string) bool {
330330
// reportHTMLFirstServedAt sends a telemetry report when the first HTML is ever served.
331331
// The purpose is to track the first time the first user opens the site.
332332
func (h*Handler)reportHTMLFirstServedAt() {
333-
// `Once` is used to reduce the volume of db calls and telemetry reports.
334-
// It's fine to run this multiple times, but it's unnecessary.
335-
h.TelemetryHTMLServedOnce.Do(func() {
336-
ctx:=context.Background()
337-
// nolint:gocritic // Only used for telemetry, so AsSystemRestricted is fine.
338-
_,err:=h.opts.Database.GetTelemetryHTMLFirstServedAt(dbauthz.AsSystemRestricted(ctx))
339-
iferr==nil {
340-
// If the value is already set, then we reported it before.
341-
// We don't need to report it again.
342-
return
343-
}
344-
if!errors.Is(err,sql.ErrNoRows) {
345-
h.opts.Logger.Debug(ctx,"failed to get telemetry html first served at",slog.Error(err))
346-
return
347-
}
348-
// SetTelemetryHTMLFirstServedAt is idempotent, so there's no harm in calling it multiple times,
349-
// even across restarts. Once it's set for the first time, it will never be changed.
350-
// nolint:gocritic // Only used for telemetry, so AsSystemRestricted is fine.
351-
iferr:=h.opts.Database.SetTelemetryHTMLFirstServedAt(dbauthz.AsSystemRestricted(ctx),time.Now().Format(time.RFC3339));err!=nil {
352-
h.opts.Logger.Debug(ctx,"failed to set telemetry html first served at",slog.Error(err))
353-
return
354-
}
355-
h.opts.Telemetry.ReportDeployment()
333+
ctx:=context.Background()
334+
itemKey:=string(telemetry.TelemetryItemKeyHTMLFirstServedAt)
335+
// nolint:gocritic // Only used for telemetry, so AsSystemRestricted is fine.
336+
_,err:=h.opts.Database.GetTelemetryItem(dbauthz.AsSystemRestricted(ctx),itemKey)
337+
iferr==nil {
338+
// If the value is already set, then we reported it before.
339+
// We don't need to report it again.
340+
return
341+
}
342+
if!errors.Is(err,sql.ErrNoRows) {
343+
h.opts.Logger.Debug(ctx,"failed to get telemetry html first served at",slog.Error(err))
344+
return
345+
}
346+
// nolint:gocritic // Only used for telemetry, so AsSystemRestricted is fine.
347+
iferr:=h.opts.Database.InsertTelemetryItemIfNotExists(dbauthz.AsSystemRestricted(ctx), database.InsertTelemetryItemIfNotExistsParams{
348+
Key:string(telemetry.TelemetryItemKeyHTMLFirstServedAt),
349+
Value:time.Now().Format(time.RFC3339),
350+
});err!=nil {
351+
h.opts.Logger.Debug(ctx,"failed to set telemetry html first served at",slog.Error(err))
352+
return
353+
}
354+
// nolint:gocritic // Only used for telemetry, so AsSystemRestricted is fine.
355+
item,err:=h.opts.Database.GetTelemetryItem(dbauthz.AsSystemRestricted(ctx),itemKey)
356+
iferr!=nil {
357+
h.opts.Logger.Debug(ctx,"failed to get telemetry html first served at",slog.Error(err))
358+
return
359+
}
360+
h.opts.Telemetry.Report(&telemetry.Snapshot{
361+
TelemetryItems: []telemetry.TelemetryItem{telemetry.ConvertTelemetryItem(item)},
356362
})
357363
}
358364

@@ -362,7 +368,11 @@ func (h *Handler) serveHTML(resp http.ResponseWriter, request *http.Request, req
362368
// Pass "index.html" to the ServeContent so the ServeContent sets the right content headers.
363369
reqPath="index.html"
364370
}
365-
goh.reportHTMLFirstServedAt()
371+
// `Once` is used to reduce the volume of db calls and telemetry reports.
372+
// It's fine to run the enclosed function multiple times, but it's unnecessary.
373+
h.TelemetryHTMLServedOnce.Do(func() {
374+
goh.reportHTMLFirstServedAt()
375+
})
366376
http.ServeContent(resp,request,reqPath, time.Time{},bytes.NewReader(data))
367377
returntrue
368378
}

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp