diff --git a/src/config/config.go.example b/src/config/config.go.example index ac25c0d2..2cde3d34 100644 --- a/src/config/config.go.example +++ b/src/config/config.go.example @@ -23,6 +23,7 @@ var Config = HMNConfig{ LogLevel: tracelog.LogLevelError, // LogLevelWarn is recommended for production MinConn: 2, // Keep these low for dev, high for production MaxConn: 10, + SlowQueryThresholdMs: 200, }, Auth: AuthConfig{ CookieDomain: ".handmade.local", diff --git a/src/config/types.go b/src/config/types.go index c983ac05..14403ff6 100644 --- a/src/config/types.go +++ b/src/config/types.go @@ -37,14 +37,15 @@ type HMNConfig struct { } type PostgresConfig struct { - User string - Password string - Hostname string - Port int - DbName string - LogLevel tracelog.LogLevel - MinConn int32 - MaxConn int32 + User string + Password string + Hostname string + Port int + DbName string + LogLevel tracelog.LogLevel + MinConn int32 + MaxConn int32 + SlowQueryThresholdMs int } type AuthConfig struct { diff --git a/src/db/db.go b/src/db/db.go index 1cf682ff..5f1a42f4 100644 --- a/src/db/db.go +++ b/src/db/db.go @@ -7,6 +7,7 @@ import ( "reflect" "regexp" "strings" + "time" "git.handmade.network/hmn/hmn/src/config" "git.handmade.network/hmn/hmn/src/logging" @@ -298,6 +299,7 @@ func QueryIterator[T any]( compiled := compileQuery(query, destType) + queryStart := time.Now() rows, err := conn.Query(ctx, compiled.query, args...) if err != nil { if errors.Is(err, context.DeadlineExceeded) { @@ -305,6 +307,14 @@ func QueryIterator[T any]( } return nil, err } + duration := time.Now().Sub(queryStart) + if config.Config.Postgres.SlowQueryThresholdMs > 0 && duration > time.Duration(config.Config.Postgres.SlowQueryThresholdMs)*time.Millisecond { + logging.Warn(). + Interface("Duration", duration.String()). + Interface("Query", strings.ReplaceAll(strings.ReplaceAll(compiled.query, "\n", " "), "\t", " ")). + Interface("Args", args). + Msg("Slow query") + } it := &Iterator[T]{ fieldPaths: compiled.fieldPaths,