diff --git a/coderd/insights.go b/coderd/insights.go index 99f87e65eb1d5..80865b9287ec6 100644 --- a/coderd/insights.go +++ b/coderd/insights.go @@ -8,6 +8,7 @@ import ( "github.com/google/uuid" "golang.org/x/exp/slices" + "golang.org/x/sync/errgroup" "golang.org/x/xerrors" "github.com/coder/coder/v2/coderd/database" @@ -190,14 +191,18 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) { var usage database.GetTemplateInsightsRow var appUsage []database.GetTemplateAppInsightsRow var dailyUsage []database.GetTemplateDailyInsightsRow + var parameterRows []database.GetTemplateParameterInsightsRow - // Use a transaction to ensure that we get consistent data between - // the full and interval report. - err := api.Database.InTx(func(tx database.Store) error { - var err error + eg, egCtx := errgroup.WithContext(ctx) + eg.SetLimit(4) + // The following insights data queries have a theoretical chance to be + // inconsistent between eachother when looking at "today", however, the + // overhead from a transaction is not worth it. + eg.Go(func() error { + var err error if interval != "" { - dailyUsage, err = tx.GetTemplateDailyInsights(ctx, database.GetTemplateDailyInsightsParams{ + dailyUsage, err = api.Database.GetTemplateDailyInsights(egCtx, database.GetTemplateDailyInsightsParams{ StartTime: startTime, EndTime: endTime, TemplateIDs: templateIDs, @@ -206,8 +211,11 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) { return xerrors.Errorf("get template daily insights: %w", err) } } - - usage, err = tx.GetTemplateInsights(ctx, database.GetTemplateInsightsParams{ + return nil + }) + eg.Go(func() error { + var err error + usage, err = api.Database.GetTemplateInsights(egCtx, database.GetTemplateInsightsParams{ StartTime: startTime, EndTime: endTime, TemplateIDs: templateIDs, @@ -215,8 +223,11 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) { if err != nil { return xerrors.Errorf("get template insights: %w", err) } - - appUsage, err = tx.GetTemplateAppInsights(ctx, database.GetTemplateAppInsightsParams{ + return nil + }) + eg.Go(func() error { + var err error + appUsage, err = api.Database.GetTemplateAppInsights(egCtx, database.GetTemplateAppInsightsParams{ StartTime: startTime, EndTime: endTime, TemplateIDs: templateIDs, @@ -224,9 +235,25 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) { if err != nil { return xerrors.Errorf("get template app insights: %w", err) } + return nil + }) + // Template parameter insights have no risk of inconsistency with the other + // insights. + eg.Go(func() error { + var err error + parameterRows, err = api.Database.GetTemplateParameterInsights(ctx, database.GetTemplateParameterInsightsParams{ + StartTime: startTime, + EndTime: endTime, + TemplateIDs: templateIDs, + }) + if err != nil { + return xerrors.Errorf("get template parameter insights: %w", err) + } return nil - }, nil) + }) + + err := eg.Wait() if httpapi.Is404Error(err) { httpapi.ResourceNotFound(rw) return @@ -239,21 +266,6 @@ func (api *API) insightsTemplates(rw http.ResponseWriter, r *http.Request) { return } - // Template parameter insights have no risk of inconsistency with the other - // insights, so we don't need to perform this in a transaction. - parameterRows, err := api.Database.GetTemplateParameterInsights(ctx, database.GetTemplateParameterInsightsParams{ - StartTime: startTime, - EndTime: endTime, - TemplateIDs: templateIDs, - }) - if err != nil { - httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{ - Message: "Internal error fetching template parameter insights.", - Detail: err.Error(), - }) - return - } - parametersUsage, err := db2sdk.TemplateInsightsParameters(parameterRows) if err != nil { httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{