From de29d4194563ca1009d97ed03b40da9a6ee6d321 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Tue, 13 Jun 2023 13:39:58 +0000 Subject: [PATCH 1/2] chore: rename dbgen package files and remove small file --- .../database/dbgen/{generator.go => dbgen.go} | 37 +++++++++++++++++ .../{generator_test.go => dbgen_test.go} | 0 coderd/database/dbgen/take.go | 40 ------------------- 3 files changed, 37 insertions(+), 40 deletions(-) rename coderd/database/dbgen/{generator.go => dbgen.go} (96%) rename coderd/database/dbgen/{generator_test.go => dbgen_test.go} (100%) delete mode 100644 coderd/database/dbgen/take.go diff --git a/coderd/database/dbgen/generator.go b/coderd/database/dbgen/dbgen.go similarity index 96% rename from coderd/database/dbgen/generator.go rename to coderd/database/dbgen/dbgen.go index c21a7952906e4..604873fa2afb6 100644 --- a/coderd/database/dbgen/generator.go +++ b/coderd/database/dbgen/dbgen.go @@ -525,3 +525,40 @@ func must[V any](v V, err error) V { } return v } + +func takeFirstIP(values ...net.IPNet) net.IPNet { + return takeFirstF(values, func(v net.IPNet) bool { + return len(v.IP) != 0 && len(v.Mask) != 0 + }) +} + +// takeFirstSlice implements takeFirst for []any. +// []any is not a comparable type. +func takeFirstSlice[T any](values ...[]T) []T { + return takeFirstF(values, func(v []T) bool { + return len(v) != 0 + }) +} + +// takeFirstF takes the first value that returns true +func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { + for _, v := range values { + if take(v) { + return v + } + } + // If all empty, return the last element + if len(values) > 0 { + return values[len(values)-1] + } + var empty Value + return empty +} + +// takeFirst will take the first non-empty value. +func takeFirst[Value comparable](values ...Value) Value { + var empty Value + return takeFirstF(values, func(v Value) bool { + return v != empty + }) +} diff --git a/coderd/database/dbgen/generator_test.go b/coderd/database/dbgen/dbgen_test.go similarity index 100% rename from coderd/database/dbgen/generator_test.go rename to coderd/database/dbgen/dbgen_test.go diff --git a/coderd/database/dbgen/take.go b/coderd/database/dbgen/take.go deleted file mode 100644 index 6792e5bb83b4b..0000000000000 --- a/coderd/database/dbgen/take.go +++ /dev/null @@ -1,40 +0,0 @@ -package dbgen - -import "net" - -func takeFirstIP(values ...net.IPNet) net.IPNet { - return takeFirstF(values, func(v net.IPNet) bool { - return len(v.IP) != 0 && len(v.Mask) != 0 - }) -} - -// takeFirstSlice implements takeFirst for []any. -// []any is not a comparable type. -func takeFirstSlice[T any](values ...[]T) []T { - return takeFirstF(values, func(v []T) bool { - return len(v) != 0 - }) -} - -// takeFirstF takes the first value that returns true -func takeFirstF[Value any](values []Value, take func(v Value) bool) Value { - for _, v := range values { - if take(v) { - return v - } - } - // If all empty, return the last element - if len(values) > 0 { - return values[len(values)-1] - } - var empty Value - return empty -} - -// takeFirst will take the first non-empty value. -func takeFirst[Value comparable](values ...Value) Value { - var empty Value - return takeFirstF(values, func(v Value) bool { - return v != empty - }) -} From c63dea5ec911be93ca6d68531ca566329e2d9492 Mon Sep 17 00:00:00 2001 From: Kyle Carberry Date: Tue, 13 Jun 2023 14:12:21 +0000 Subject: [PATCH 2/2] chore: automatically generate dbmetrics when new queries are added --- coderd/database/dbmetrics/dbmetrics.go | 133 ++++++++-------- coderd/database/gen/metrics/main.go | 210 +++++++++++++++++++++++++ coderd/database/generate.sh | 3 + 3 files changed, 281 insertions(+), 65 deletions(-) create mode 100644 coderd/database/gen/metrics/main.go diff --git a/coderd/database/dbmetrics/dbmetrics.go b/coderd/database/dbmetrics/dbmetrics.go index 136d972f4510b..49cf5fc402c5e 100644 --- a/coderd/database/dbmetrics/dbmetrics.go +++ b/coderd/database/dbmetrics/dbmetrics.go @@ -1,3 +1,6 @@ +// Code generated by coderd/database/gen/metrics. +// Any function can be edited and will not be overwritten. +// New database functions are automatically generated! package dbmetrics import ( @@ -70,6 +73,41 @@ func (m metricsStore) InTx(f func(database.Store) error, options *sql.TxOptions) return err } +func (m metricsStore) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) { + start := time.Now() + templates, err := m.s.GetAuthorizedTemplates(ctx, arg, prepared) + m.queryLatencies.WithLabelValues("GetAuthorizedTemplates").Observe(time.Since(start).Seconds()) + return templates, err +} + +func (m metricsStore) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateGroup, error) { + start := time.Now() + roles, err := m.s.GetTemplateGroupRoles(ctx, id) + m.queryLatencies.WithLabelValues("GetTemplateGroupRoles").Observe(time.Since(start).Seconds()) + return roles, err +} + +func (m metricsStore) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateUser, error) { + start := time.Now() + roles, err := m.s.GetTemplateUserRoles(ctx, id) + m.queryLatencies.WithLabelValues("GetTemplateUserRoles").Observe(time.Since(start).Seconds()) + return roles, err +} + +func (m metricsStore) GetAuthorizedWorkspaces(ctx context.Context, arg database.GetWorkspacesParams, prepared rbac.PreparedAuthorized) ([]database.GetWorkspacesRow, error) { + start := time.Now() + workspaces, err := m.s.GetAuthorizedWorkspaces(ctx, arg, prepared) + m.queryLatencies.WithLabelValues("GetAuthorizedWorkspaces").Observe(time.Since(start).Seconds()) + return workspaces, err +} + +func (m metricsStore) GetAuthorizedUserCount(ctx context.Context, arg database.GetFilteredUserCountParams, prepared rbac.PreparedAuthorized) (int64, error) { + start := time.Now() + count, err := m.s.GetAuthorizedUserCount(ctx, arg, prepared) + m.queryLatencies.WithLabelValues("GetAuthorizedUserCount").Observe(time.Since(start).Seconds()) + return count, err +} + func (m metricsStore) AcquireLock(ctx context.Context, pgAdvisoryXactLock int64) error { start := time.Now() err := m.s.AcquireLock(ctx, pgAdvisoryXactLock) @@ -231,6 +269,13 @@ func (m metricsStore) GetDERPMeshKey(ctx context.Context) (string, error) { return key, err } +func (m metricsStore) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) { + start := time.Now() + resp, err := m.s.GetDefaultProxyConfig(ctx) + m.queryLatencies.WithLabelValues("GetDefaultProxyConfig").Observe(time.Since(start).Seconds()) + return resp, err +} + func (m metricsStore) GetDeploymentDAUs(ctx context.Context, tzOffset int32) ([]database.GetDeploymentDAUsRow, error) { start := time.Now() rows, err := m.s.GetDeploymentDAUs(ctx, tzOffset) @@ -1360,9 +1405,9 @@ func (m metricsStore) UpdateWorkspaceAgentConnectionByID(ctx context.Context, ar func (m metricsStore) UpdateWorkspaceAgentLifecycleStateByID(ctx context.Context, arg database.UpdateWorkspaceAgentLifecycleStateByIDParams) error { start := time.Now() - err := m.s.UpdateWorkspaceAgentLifecycleStateByID(ctx, arg) + r0 := m.s.UpdateWorkspaceAgentLifecycleStateByID(ctx, arg) m.queryLatencies.WithLabelValues("UpdateWorkspaceAgentLifecycleStateByID").Observe(time.Since(start).Seconds()) - return err + return r0 } func (m metricsStore) UpdateWorkspaceAgentMetadata(ctx context.Context, arg database.UpdateWorkspaceAgentMetadataParams) error { @@ -1437,98 +1482,56 @@ func (m metricsStore) UpdateWorkspaceProxy(ctx context.Context, arg database.Upd func (m metricsStore) UpdateWorkspaceProxyDeleted(ctx context.Context, arg database.UpdateWorkspaceProxyDeletedParams) error { start := time.Now() - err := m.s.UpdateWorkspaceProxyDeleted(ctx, arg) + r0 := m.s.UpdateWorkspaceProxyDeleted(ctx, arg) m.queryLatencies.WithLabelValues("UpdateWorkspaceProxyDeleted").Observe(time.Since(start).Seconds()) - return err + return r0 } func (m metricsStore) UpdateWorkspaceTTL(ctx context.Context, arg database.UpdateWorkspaceTTLParams) error { start := time.Now() - err := m.s.UpdateWorkspaceTTL(ctx, arg) + r0 := m.s.UpdateWorkspaceTTL(ctx, arg) m.queryLatencies.WithLabelValues("UpdateWorkspaceTTL").Observe(time.Since(start).Seconds()) - return err + return r0 } func (m metricsStore) UpdateWorkspaceTTLToBeWithinTemplateMax(ctx context.Context, arg database.UpdateWorkspaceTTLToBeWithinTemplateMaxParams) error { start := time.Now() - err := m.s.UpdateWorkspaceTTLToBeWithinTemplateMax(ctx, arg) + r0 := m.s.UpdateWorkspaceTTLToBeWithinTemplateMax(ctx, arg) m.queryLatencies.WithLabelValues("UpdateWorkspaceTTLToBeWithinTemplateMax").Observe(time.Since(start).Seconds()) - return err + return r0 } func (m metricsStore) UpsertAppSecurityKey(ctx context.Context, value string) error { start := time.Now() - err := m.s.UpsertAppSecurityKey(ctx, value) + r0 := m.s.UpsertAppSecurityKey(ctx, value) m.queryLatencies.WithLabelValues("UpsertAppSecurityKey").Observe(time.Since(start).Seconds()) - return err + return r0 +} + +func (m metricsStore) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error { + start := time.Now() + r0 := m.s.UpsertDefaultProxy(ctx, arg) + m.queryLatencies.WithLabelValues("UpsertDefaultProxy").Observe(time.Since(start).Seconds()) + return r0 } func (m metricsStore) UpsertLastUpdateCheck(ctx context.Context, value string) error { start := time.Now() - err := m.s.UpsertLastUpdateCheck(ctx, value) + r0 := m.s.UpsertLastUpdateCheck(ctx, value) m.queryLatencies.WithLabelValues("UpsertLastUpdateCheck").Observe(time.Since(start).Seconds()) - return err + return r0 } func (m metricsStore) UpsertLogoURL(ctx context.Context, value string) error { start := time.Now() - err := m.s.UpsertLogoURL(ctx, value) + r0 := m.s.UpsertLogoURL(ctx, value) m.queryLatencies.WithLabelValues("UpsertLogoURL").Observe(time.Since(start).Seconds()) - return err + return r0 } func (m metricsStore) UpsertServiceBanner(ctx context.Context, value string) error { start := time.Now() - err := m.s.UpsertServiceBanner(ctx, value) + r0 := m.s.UpsertServiceBanner(ctx, value) m.queryLatencies.WithLabelValues("UpsertServiceBanner").Observe(time.Since(start).Seconds()) - return err -} - -func (m metricsStore) GetAuthorizedTemplates(ctx context.Context, arg database.GetTemplatesWithFilterParams, prepared rbac.PreparedAuthorized) ([]database.Template, error) { - start := time.Now() - templates, err := m.s.GetAuthorizedTemplates(ctx, arg, prepared) - m.queryLatencies.WithLabelValues("GetAuthorizedTemplates").Observe(time.Since(start).Seconds()) - return templates, err -} - -func (m metricsStore) GetTemplateGroupRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateGroup, error) { - start := time.Now() - roles, err := m.s.GetTemplateGroupRoles(ctx, id) - m.queryLatencies.WithLabelValues("GetTemplateGroupRoles").Observe(time.Since(start).Seconds()) - return roles, err -} - -func (m metricsStore) GetTemplateUserRoles(ctx context.Context, id uuid.UUID) ([]database.TemplateUser, error) { - start := time.Now() - roles, err := m.s.GetTemplateUserRoles(ctx, id) - m.queryLatencies.WithLabelValues("GetTemplateUserRoles").Observe(time.Since(start).Seconds()) - return roles, err -} - -func (m metricsStore) GetAuthorizedWorkspaces(ctx context.Context, arg database.GetWorkspacesParams, prepared rbac.PreparedAuthorized) ([]database.GetWorkspacesRow, error) { - start := time.Now() - workspaces, err := m.s.GetAuthorizedWorkspaces(ctx, arg, prepared) - m.queryLatencies.WithLabelValues("GetAuthorizedWorkspaces").Observe(time.Since(start).Seconds()) - return workspaces, err -} - -func (m metricsStore) GetAuthorizedUserCount(ctx context.Context, arg database.GetFilteredUserCountParams, prepared rbac.PreparedAuthorized) (int64, error) { - start := time.Now() - count, err := m.s.GetAuthorizedUserCount(ctx, arg, prepared) - m.queryLatencies.WithLabelValues("GetAuthorizedUserCount").Observe(time.Since(start).Seconds()) - return count, err -} - -func (m metricsStore) UpsertDefaultProxy(ctx context.Context, arg database.UpsertDefaultProxyParams) error { - start := time.Now() - err := m.s.UpsertDefaultProxy(ctx, arg) - m.queryLatencies.WithLabelValues("UpsertDefaultProxy").Observe(time.Since(start).Seconds()) - return err -} - -func (m metricsStore) GetDefaultProxyConfig(ctx context.Context) (database.GetDefaultProxyConfigRow, error) { - start := time.Now() - resp, err := m.s.GetDefaultProxyConfig(ctx) - m.queryLatencies.WithLabelValues("GetDefaultProxyConfig").Observe(time.Since(start).Seconds()) - return resp, err + return r0 } diff --git a/coderd/database/gen/metrics/main.go b/coderd/database/gen/metrics/main.go new file mode 100644 index 0000000000000..ded12c0eae6b3 --- /dev/null +++ b/coderd/database/gen/metrics/main.go @@ -0,0 +1,210 @@ +package main + +import ( + "fmt" + "go/format" + "go/token" + "log" + "os" + "strings" + + "github.com/dave/dst" + "github.com/dave/dst/decorator" + "github.com/dave/dst/decorator/resolver/goast" + "github.com/dave/dst/decorator/resolver/guess" + "golang.org/x/xerrors" +) + +func main() { + err := run() + if err != nil { + log.Fatal(err) + } +} + +func run() error { + funcs, err := readStoreInterface() + if err != nil { + return err + } + funcByName := map[string]struct{}{} + for _, f := range funcs { + funcByName[f.Name] = struct{}{} + } + declByName := map[string]*dst.FuncDecl{} + + dbmetrics, err := os.ReadFile("./dbmetrics/dbmetrics.go") + if err != nil { + return xerrors.Errorf("read dbfake: %w", err) + } + + // Required to preserve imports! + f, err := decorator.NewDecoratorWithImports(token.NewFileSet(), "dbmetrics", goast.New()).Parse(dbmetrics) + if err != nil { + return xerrors.Errorf("parse dbfake: %w", err) + } + + for i := 0; i < len(f.Decls); i++ { + funcDecl, ok := f.Decls[i].(*dst.FuncDecl) + if !ok || funcDecl.Recv == nil || len(funcDecl.Recv.List) == 0 { + continue + } + // Check if the receiver is the struct we're interested in + _, ok = funcDecl.Recv.List[0].Type.(*dst.Ident) + if !ok { + continue + } + if _, ok := funcByName[funcDecl.Name.Name]; !ok { + continue + } + declByName[funcDecl.Name.Name] = funcDecl + f.Decls = append(f.Decls[:i], f.Decls[i+1:]...) + i-- + } + + for _, fn := range funcs { + decl, ok := declByName[fn.Name] + if !ok { + params := make([]string, 0) + if fn.Func.Params != nil { + for _, p := range fn.Func.Params.List { + for _, name := range p.Names { + params = append(params, name.Name) + } + } + } + returns := make([]string, 0) + if fn.Func.Results != nil { + for i := range fn.Func.Results.List { + returns = append(returns, fmt.Sprintf("r%d", i)) + } + } + + code := fmt.Sprintf(` +package stub + +func stub() { + start := time.Now() + %s := m.s.%s(%s) + m.queryLatencies.WithLabelValues("%s").Observe(time.Since(start).Seconds()) + return %s +} +`, strings.Join(returns, ","), fn.Name, strings.Join(params, ","), fn.Name, strings.Join(returns, ",")) + file, err := decorator.Parse(code) + if err != nil { + return xerrors.Errorf("parse code: %w", err) + } + stmt, ok := file.Decls[0].(*dst.FuncDecl) + if !ok { + return xerrors.Errorf("not ok %T", file.Decls[0]) + } + + // Not implemented! + // When a function isn't implemented, we automatically stub it! + decl = &dst.FuncDecl{ + Name: dst.NewIdent(fn.Name), + Type: fn.Func, + Recv: &dst.FieldList{ + List: []*dst.Field{{ + Names: []*dst.Ident{dst.NewIdent("m")}, + Type: dst.NewIdent("metricsStore"), + }}, + }, + Decs: dst.FuncDeclDecorations{ + NodeDecs: dst.NodeDecs{ + Before: dst.EmptyLine, + After: dst.EmptyLine, + }, + }, + Body: stmt.Body, + } + } + f.Decls = append(f.Decls, decl) + } + + file, err := os.OpenFile("./dbmetrics/dbmetrics.go", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o755) + if err != nil { + return xerrors.Errorf("open dbfake: %w", err) + } + defer file.Close() + + // Required to preserve imports! + restorer := decorator.NewRestorerWithImports("dbmetrics", guess.New()) + restored, err := restorer.RestoreFile(f) + if err != nil { + return xerrors.Errorf("restore dbfake: %w", err) + } + err = format.Node(file, restorer.Fset, restored) + return err +} + +type storeMethod struct { + Name string + Func *dst.FuncType +} + +func readStoreInterface() ([]storeMethod, error) { + querier, err := os.ReadFile("./querier.go") + if err != nil { + return nil, xerrors.Errorf("read querier: %w", err) + } + f, err := decorator.Parse(querier) + if err != nil { + return nil, err + } + + var sqlcQuerier *dst.InterfaceType + for _, decl := range f.Decls { + genDecl, ok := decl.(*dst.GenDecl) + if !ok { + continue + } + + for _, spec := range genDecl.Specs { + typeSpec, ok := spec.(*dst.TypeSpec) + if !ok { + continue + } + if typeSpec.Name.Name != "sqlcQuerier" { + continue + } + sqlcQuerier, ok = typeSpec.Type.(*dst.InterfaceType) + if !ok { + return nil, xerrors.Errorf("unexpected sqlcQuerier type: %T", typeSpec.Type) + } + break + } + } + if sqlcQuerier == nil { + return nil, xerrors.Errorf("sqlcQuerier not found") + } + funcs := []storeMethod{} + for _, method := range sqlcQuerier.Methods.List { + funcType, ok := method.Type.(*dst.FuncType) + if !ok { + continue + } + + for _, t := range []*dst.FieldList{funcType.Params, funcType.Results} { + if t == nil { + continue + } + for _, f := range t.List { + ident, ok := f.Type.(*dst.Ident) + if !ok { + continue + } + if !ident.IsExported() { + continue + } + ident.Path = "github.com/coder/coder/coderd/database" + } + } + + funcs = append(funcs, storeMethod{ + Name: method.Names[0].Name, + Func: funcType, + }) + } + return funcs, nil +} diff --git a/coderd/database/generate.sh b/coderd/database/generate.sh index 8eda82be03812..c17bf695461bf 100755 --- a/coderd/database/generate.sh +++ b/coderd/database/generate.sh @@ -62,4 +62,7 @@ SCRIPT_DIR=$(dirname "${BASH_SOURCE[0]}") # Generate the database fake! go run gen/fake/main.go go run golang.org/x/tools/cmd/goimports@latest -w ./dbfake/dbfake.go + + go run gen/metrics/main.go + go run golang.org/x/tools/cmd/goimports@latest -w ./dbmetrics/dbmetrics.go ) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy