diff --git a/internal/workbenchruntime/service.go b/internal/workbenchruntime/service.go index 830d48e8..a4c54063 100644 --- a/internal/workbenchruntime/service.go +++ b/internal/workbenchruntime/service.go @@ -34,6 +34,12 @@ type Server struct { mux *http.ServeMux } +var workbenchRuntimeReadIndexes = []string{ + "CREATE INDEX IF NOT EXISTS idx_workbench_sessions_updated ON workbench_sessions(updated_at DESC, session_id)", + "CREATE INDEX IF NOT EXISTS idx_workbench_sessions_owner_updated ON workbench_sessions(owner_user_id, updated_at DESC, session_id)", + "CREATE INDEX IF NOT EXISTS idx_workbench_sessions_status_updated ON workbench_sessions(status, updated_at DESC, session_id)", +} + type factQuery struct { Families []string `json:"families"` FactFamilies []string `json:"factFamilies"` @@ -120,6 +126,9 @@ func Run(ctx context.Context) error { db.SetMaxOpenConns(config.PoolMax) db.SetMaxIdleConns(config.PoolMax) } + if err := ensureWorkbenchRuntimeReadIndexes(ctx, db); err != nil { + return err + } server := NewServer(config, db) httpServer := &http.Server{Addr: config.Addr, Handler: server.otelHTTPHandler(server.mux), ReadHeaderTimeout: 5 * time.Second} errCh := make(chan error, 1) @@ -142,6 +151,17 @@ func Run(ctx context.Context) error { return httpServer.Shutdown(shutdownCtx) } +func ensureWorkbenchRuntimeReadIndexes(ctx context.Context, db *sql.DB) error { + ensureCtx, cancel := context.WithTimeout(ctx, 30*time.Second) + defer cancel() + for _, statement := range workbenchRuntimeReadIndexes { + if _, err := db.ExecContext(ensureCtx, statement); err != nil { + return fmt.Errorf("ensure workbench runtime read index: %w", err) + } + } + return nil +} + func NewServer(config Config, db *sql.DB) *Server { server := &Server{config: config, db: db, mux: http.NewServeMux()} server.routes() @@ -474,12 +494,7 @@ func (s *Server) queryFactRows(ctx context.Context, table string, jsonColumn str func (s *Server) queryJSONColumn(ctx context.Context, stage string, table string, sqlText string, args []any) ([]any, error) { result := []any{} - err := s.withOtelInternalSpan(ctx, stage, map[string]any{ - "db.system": "postgresql", - "db.operation.name": "SELECT", - "db.sql.table": table, - "db.query.arg_count": int64(len(args)), - }, func(spanCtx context.Context) error { + err := s.withOtelInternalSpan(ctx, stage, s.dbQueryAttrs(table, len(args), nil), func(spanCtx context.Context) error { rows, err := s.db.QueryContext(spanCtx, sqlText, args...) if err != nil { return err @@ -503,14 +518,31 @@ func (s *Server) queryJSONColumn(ctx context.Context, stage string, table string return result, nil } +func (s *Server) dbQueryAttrs(table string, argCount int, extra map[string]any) map[string]any { + stats := s.db.Stats() + attrs := map[string]any{ + "db.system": "postgresql", + "db.operation.name": "SELECT", + "db.sql.table": table, + "db.query.arg_count": int64(argCount), + "db.pool.max_open": int64(s.config.PoolMax), + "db.pool.open_connections": int64(stats.OpenConnections), + "db.pool.in_use": int64(stats.InUse), + "db.pool.idle": int64(stats.Idle), + "db.pool.wait_count": int64(stats.WaitCount), + "db.pool.wait_duration_ms": stats.WaitDuration.Milliseconds(), + "db.pool.max_idle_closed": int64(stats.MaxIdleClosed), + "db.pool.max_lifetime_closed": int64(stats.MaxLifetimeClosed), + } + for key, value := range extra { + attrs[key] = value + } + return attrs +} + func (s *Server) querySessionSummaries(ctx context.Context, stage string, sqlText string, args []any) ([]any, error) { result := []any{} - err := s.withOtelInternalSpan(ctx, stage, map[string]any{ - "db.system": "postgresql", - "db.operation.name": "SELECT", - "db.sql.table": "workbench_sessions", - "db.query.arg_count": int64(len(args)), - }, func(spanCtx context.Context) error { + err := s.withOtelInternalSpan(ctx, stage, s.dbQueryAttrs("workbench_sessions", len(args), map[string]any{"db.index.expected": "idx_workbench_sessions_owner_updated|idx_workbench_sessions_updated"}), func(spanCtx context.Context) error { rows, err := s.db.QueryContext(spanCtx, sqlText, args...) if err != nil { return err