From d9b5f85c3d5d239ee24677c326e9b1e5361c8989 Mon Sep 17 00:00:00 2001 From: Trey t Date: Sat, 25 Apr 2026 16:36:47 -0500 Subject: [PATCH] Thread ctx through auth middleware DB lookups MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The auth middleware's m.db.Preload + m.db.First calls were running without ctx, so on cache miss the resulting SQL queries appeared as orphan gorm.Query / gorm.Row spans in Jaeger. Now they nest under the parent HTTP request span like every other repo call. This was the last orphaned-SQL source on the request hot path. Combined with the seven service migrations, every authenticated API call now produces a fully-nested flame graph: HTTP → auth-token-lookup (cache hit) or HTTP → auth-token-SQL (cache miss) → service → service-SQL. Co-Authored-By: Claude Opus 4.7 (1M context) --- internal/middleware/auth.go | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/internal/middleware/auth.go b/internal/middleware/auth.go index dca1987..3cd361a 100644 --- a/internal/middleware/auth.go +++ b/internal/middleware/auth.go @@ -104,7 +104,7 @@ func (m *AuthMiddleware) TokenAuth() echo.MiddlewareFunc { } // Cache miss - look up token in database - user, authToken, err := m.getUserFromDatabaseWithToken(token) + user, authToken, err := m.getUserFromDatabaseWithToken(c.Request().Context(), token) if err != nil { log.Debug().Err(err).Str("token", truncateToken(token)).Msg("Token authentication failed") return apperrors.Unauthorized("error.invalid_token") @@ -148,7 +148,7 @@ func (m *AuthMiddleware) OptionalTokenAuth() echo.MiddlewareFunc { } // Try database - user, authToken, err := m.getUserFromDatabaseWithToken(token) + user, authToken, err := m.getUserFromDatabaseWithToken(c.Request().Context(), token) if err == nil && !m.isTokenExpired(authToken.Created) { m.cacheTokenInfo(c.Request().Context(), token, user.ID, authToken.Created) c.Set(AuthUserKey, user) @@ -224,7 +224,7 @@ func (m *AuthMiddleware) getUserFromCache(ctx context.Context, token string) (*m // In-memory cache miss — fetch from database var user models.User - if err := m.db.First(&user, userID).Error; err != nil { + if err := m.db.WithContext(ctx).First(&user, userID).Error; err != nil { // User was deleted - invalidate caches m.cache.InvalidateAuthToken(ctx, token) return nil, err @@ -242,10 +242,11 @@ func (m *AuthMiddleware) getUserFromCache(ctx context.Context, token string) (*m } // getUserFromDatabaseWithToken looks up the token in the database and returns -// both the user and the auth token record (for expiry checking). -func (m *AuthMiddleware) getUserFromDatabaseWithToken(token string) (*models.User, *models.AuthToken, error) { +// both the user and the auth token record (for expiry checking). The ctx is +// threaded into the GORM session so the SQL span attaches to the request trace. +func (m *AuthMiddleware) getUserFromDatabaseWithToken(ctx context.Context, token string) (*models.User, *models.AuthToken, error) { var authToken models.AuthToken - if err := m.db.Preload("User").Where("key = ?", token).First(&authToken).Error; err != nil { + if err := m.db.WithContext(ctx).Preload("User").Where("key = ?", token).First(&authToken).Error; err != nil { return nil, nil, fmt.Errorf("token not found") } @@ -262,8 +263,8 @@ func (m *AuthMiddleware) getUserFromDatabaseWithToken(token string) (*models.Use // getUserFromDatabase looks up the token in the database and caches the // resulting user record in memory. // Deprecated: Use getUserFromDatabaseWithToken for new code paths that need expiry checking. -func (m *AuthMiddleware) getUserFromDatabase(token string) (*models.User, error) { - user, _, err := m.getUserFromDatabaseWithToken(token) +func (m *AuthMiddleware) getUserFromDatabase(ctx context.Context, token string) (*models.User, error) { + user, _, err := m.getUserFromDatabaseWithToken(ctx, token) return user, err }