import itertools import json import sys import traceback import uuid from typing import Any, Dict, List import backoff from botocore.client import BaseClient from botocore.exceptions import BotoCoreError from structlog import BoundLogger from src.config import SQS_RETRY_COUNT, SQS_MAX_TIME, MAX_SQS_CHUNK_SIZE class SQSClient: """SQS for sending push receipts to SQS.""" def __init__(self, provider: BaseClient, logger: BoundLogger): self.provider = provider self.logger = logger def _handle_retry(self, retry_data: Dict[str, Any]): err = ( f"{retry_data['tries']}/{SQS_RETRY_COUNT} {self.__class__.__name__} " f"call got {retry_data['exception']} with args: {retry_data['args']} and kwargs {retry_data['kwargs']}. " ) self.logger.error(err + f"{traceback.format_exception(*sys.exc_info()) + traceback.format_stack()}") def send(self, queue: str, messages: List[Dict[str, Any]]): @backoff.on_exception( backoff.expo, BotoCoreError, max_tries=SQS_RETRY_COUNT, max_time=SQS_MAX_TIME, logger=self.logger, on_backoff=self._handle_retry, ) def _send(queue_, messages_): return self.provider.send_message_batch( QueueUrl=queue_, Entries=[ { "Id": str(message.get("id", uuid.uuid4())), "MessageBody": json.dumps(message, default=str)} for message in messages_ ], ) return list( itertools.chain(*[_send(queue, messages[i: i + MAX_SQS_CHUNK_SIZE]) for i in range(0, len(messages), MAX_SQS_CHUNK_SIZE)]) )