diff --git a/changelog.d/20241122_105538_rra_DM_47716.md b/changelog.d/20241122_105538_rra_DM_47716.md new file mode 100644 index 00000000..d5bb2d7e --- /dev/null +++ b/changelog.d/20241122_105538_rra_DM_47716.md @@ -0,0 +1,3 @@ +### Bug fixes + +- Move metrics reporting for hot-path authentication events to a background task so that it happens in parallel with the HTTP response. diff --git a/src/gafaelfawr/handlers/ingress.py b/src/gafaelfawr/handlers/ingress.py index 23d10aa9..8c25bd38 100644 --- a/src/gafaelfawr/handlers/ingress.py +++ b/src/gafaelfawr/handlers/ingress.py @@ -12,8 +12,15 @@ from datetime import timedelta from typing import Annotated -import sentry_sdk -from fastapi import APIRouter, Depends, Header, HTTPException, Query, Response +from fastapi import ( + APIRouter, + BackgroundTasks, + Depends, + Header, + HTTPException, + Query, + Response, +) from safir.datetime import current_datetime from safir.models import ErrorModel from safir.slack.webhook import SlackRouteErrorHandler @@ -27,7 +34,7 @@ from ..constants import MINIMUM_LIFETIME from ..dependencies.auth import AuthenticateRead from ..dependencies.context import RequestContext, context_dependency -from ..events import AuthBotEvent, AuthUserEvent +from ..events import AuthBotEvent, AuthUserEvent, FrontendEvents from ..exceptions import ( ExternalUserInfoError, InsufficientScopeError, @@ -305,6 +312,30 @@ async def authenticate_with_type( return await authenticate(context=context) +async def publish_auth_event( + events: FrontendEvents, auth_config: AuthConfig, token_data: TokenData +) -> None: + """Publish metrics events for a successful authentication. + + Parameters + ---------- + events + Event manager. + auth_config + Requested authentication parameters. + token_data + Successful authentication data. + """ + username = token_data.username + service = auth_config.service + if is_bot_user(token_data.username): + bot_event = AuthBotEvent(username=username, service=service) + await events.auth_bot.publish(bot_event) + else: + user_event = AuthUserEvent(username=username, service=service) + await events.auth_user.publish(user_event) + + @router.get( "/ingress/auth", description="Meant to be used as an NGINX auth_request handler", @@ -322,6 +353,7 @@ async def get_auth( token_data: Annotated[TokenData, Depends(authenticate_with_type)], context: Annotated[RequestContext, Depends(context_dependency)], response: Response, + background_tasks: BackgroundTasks, ) -> dict[str, str]: check_lifetime(context, auth_config, token_data) @@ -360,18 +392,9 @@ async def get_auth( headers = await build_success_headers(context, auth_config, token_data) for key, value in headers: response.headers.append(key, value) - - with sentry_sdk.start_span(name="events.publish"): - if is_bot_user(token_data.username): - bot_event = AuthBotEvent( - username=token_data.username, service=auth_config.service - ) - await context.events.auth_bot.publish(bot_event) - else: - user_event = AuthUserEvent( - username=token_data.username, service=auth_config.service - ) - await context.events.auth_user.publish(user_event) + background_tasks.add_task( + publish_auth_event, context.events, auth_config, token_data + ) return {"status": "ok"}