33"""
44Batch SQS utilities
55"""
6- from typing import List , Optional , Tuple
6+ import logging
7+ from typing import Callable , Dict , List , Optional , Tuple
78
89import boto3
910from botocore .config import Config
1011
12+ from ...middleware_factory import lambda_handler_decorator
1113from .base import BasePartialProcessor
14+ from .exceptions import SQSBatchProcessingError
15+
16+ logger = logging .getLogger (__name__ )
1217
1318
1419class PartialSQSProcessor (BasePartialProcessor ):
1520 """
1621 Amazon SQS batch processor to delete successes from the Queue.
1722
18- Only the **special** case of partial failure is handled, thus a batch in
19- which all records failed is **not** going to be removed from the queue, and
20- the same is valid for a full success .
23+ The whole batch will be processed, even if failures occur. After all records are processed,
24+ SQSBatchProcessingError will be raised if there were any failures, causing messages to
25+ be returned to the SQS queue. This behaviour can be disabled by passing suppress_exception .
2126
2227 Parameters
2328 ----------
2429 config: Config
2530 botocore config object
31+ suppress_exception: bool, optional
32+ Supress exception raised if any messages fail processing, by default False
33+
2634
2735 Example
2836 -------
@@ -46,12 +54,13 @@ class PartialSQSProcessor(BasePartialProcessor):
4654 >>> return result
4755 """
4856
49- def __init__ (self , config : Optional [Config ] = None ):
57+ def __init__ (self , config : Optional [Config ] = None , suppress_exception : bool = False ):
5058 """
5159 Initializes sqs client.
5260 """
5361 config = config or Config ()
5462 self .client = boto3 .client ("sqs" , config = config )
63+ self .suppress_exception = suppress_exception
5564
5665 super ().__init__ ()
5766
@@ -97,10 +106,76 @@ def _clean(self):
97106 """
98107 Delete messages from Queue in case of partial failure.
99108 """
100- if not (self .fail_messages and self .success_messages ):
109+ # If all messages were successful, fall back to the default SQS -
110+ # Lambda behaviour which deletes messages if Lambda responds successfully
111+ if not self .fail_messages :
112+ logger .debug (f"All { len (self .success_messages )} records successfully processed" )
101113 return
102114
103115 queue_url = self ._get_queue_url ()
104116 entries_to_remove = self ._get_entries_to_clean ()
105117
106- return self .client .delete_message_batch (QueueUrl = queue_url , Entries = entries_to_remove )
118+ delete_message_response = self .client .delete_message_batch (QueueUrl = queue_url , Entries = entries_to_remove )
119+
120+ if self .suppress_exception :
121+ logger .debug (f"{ len (self .fail_messages )} records failed processing, but exceptions are suppressed" )
122+ else :
123+ logger .debug (f"{ len (self .fail_messages )} records failed processing, raising exception" )
124+ raise SQSBatchProcessingError (list (self .exceptions ))
125+
126+ return delete_message_response
127+
128+
129+ @lambda_handler_decorator
130+ def sqs_batch_processor (
131+ handler : Callable ,
132+ event : Dict ,
133+ context : Dict ,
134+ record_handler : Callable ,
135+ config : Optional [Config ] = None ,
136+ suppress_exception : bool = False ,
137+ ):
138+ """
139+ Middleware to handle SQS batch event processing
140+
141+ Parameters
142+ ----------
143+ handler: Callable
144+ Lambda's handler
145+ event: Dict
146+ Lambda's Event
147+ context: Dict
148+ Lambda's Context
149+ record_handler: Callable
150+ Callable to process each record from the batch
151+ config: Config
152+ botocore config object
153+ suppress_exception: bool, optional
154+ Supress exception raised if any messages fail processing, by default False
155+
156+ Examples
157+ --------
158+ **Processes Lambda's event with PartialSQSProcessor**
159+ >>> from aws_lambda_powertools.utilities.batch import sqs_batch_processor
160+ >>>
161+ >>> def record_handler(record):
162+ >>> return record["body"]
163+ >>>
164+ >>> @sqs_batch_processor(record_handler=record_handler)
165+ >>> def handler(event, context):
166+ >>> return {"StatusCode": 200}
167+
168+ Limitations
169+ -----------
170+ * Async batch processors
171+
172+ """
173+ config = config or Config ()
174+ processor = PartialSQSProcessor (config = config , suppress_exception = suppress_exception )
175+
176+ records = event ["Records" ]
177+
178+ with processor (records , record_handler ):
179+ processor .process ()
180+
181+ return handler (event , context )
0 commit comments