"""Lambda outbox processor function module.""" from __future__ import annotations from datetime import datetime from typing import Any, Mapping import sentry_sdk from lambdacommon.common_config import logger from sentry_sdk.integrations.aws_lambda import AwsLambdaIntegration import config from src.connectors import ( EventBridgeConnector, Repository, get_events_client, mysql_connection, ) from src.db_iterable import DBOutboxIterable from src.errors import TransientError from src.kafka_iterable import KafkaOutboxIterable from src.processor import OutboxProcessor from src.schemas import KafkaEvent if config.SENTRY_DSN: sentry_sdk.init( dsn=config.SENTRY_DSN, environment=config.ENVIRONMENT, integrations=[AwsLambdaIntegration(timeout_warning=True)], ) def handler(event: Mapping[str, Any] | None, context: Any) -> dict[str, Any]: """Lambda entry point. Handles both Kafka/MSK events (CDC-triggered) and direct invocations (testing/polling). Args: event: Event data (Kafka/MSK or direct invocation). context: Lambda runtime information. Returns: dict: Response with processed count. """ try: logger.info(f'Function ARN: {context.invoked_function_arn}') is_kafka_event = event and event.get('eventSource') == 'aws:kafka' event_source = 'Kafka/MSK CDC' if is_kafka_event else 'DB polling' logger.info(f'Event source: {event_source}') logger.info('Establishing connections') with mysql_connection(**config.MYSQL_CONFIG) as mysql_conn: repository = Repository(mysql_conn) eb_connector = EventBridgeConnector( config.EVENT_SOURCE, get_events_client() ) outbox_iterable = ( KafkaOutboxIterable(KafkaEvent(**(event or {}))) if is_kafka_event else DBOutboxIterable(repository, config.BATCH_SIZE) ) logger.info('Processing') start_time = datetime.now() processor = OutboxProcessor(repository, eb_connector) result = processor.process(outbox_iterable) end_time = datetime.now() # Log processing time diff = end_time - start_time logger.info(f'Finished processing in {diff}') # Return result return result.model_dump() except TransientError as e: # Retriable errors (DB connection, IntegrityError, etc) logger.warning(f'Transient error encountered: {e}') raise TransientError(str(e)) from e except Exception as e: # Unexpected errors logger.exception(f'Unexpected error encountered: {e}') raise