fix(coderd): parallelize queries to improve template insights performance (#9275)

This commit is contained in:
Mathias Fredriksson 2023-08-23 18:31:23 +03:00 committed by GitHub
parent d180df1fd5
commit 8bfa312905
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 38 additions and 26 deletions

View File

@ -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 {
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
}, 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
})
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{