4
4
Batch SQS utilities
5
5
"""
6
6
import logging
7
+ import math
7
8
import sys
8
- from typing import Callable , Dict , List , Optional , Tuple , cast
9
+ from concurrent .futures import ThreadPoolExecutor , as_completed
10
+ from typing import Any , Callable , Dict , List , Optional , Tuple , cast
9
11
10
12
import boto3
11
13
from botocore .config import Config
@@ -73,6 +75,7 @@ def __init__(
73
75
session = boto3_session or boto3 .session .Session ()
74
76
self .client = session .client ("sqs" , config = config )
75
77
self .suppress_exception = suppress_exception
78
+ self .max_message_batch = 10
76
79
77
80
super ().__init__ ()
78
81
@@ -120,28 +123,39 @@ def _prepare(self):
120
123
self .success_messages .clear ()
121
124
self .fail_messages .clear ()
122
125
123
- def _clean (self ):
126
+ def _clean (self ) -> Optional [ List ] :
124
127
"""
125
128
Delete messages from Queue in case of partial failure.
126
129
"""
130
+
127
131
# If all messages were successful, fall back to the default SQS -
128
- # Lambda behaviour which deletes messages if Lambda responds successfully
132
+ # Lambda behavior which deletes messages if Lambda responds successfully
129
133
if not self .fail_messages :
130
134
logger .debug (f"All { len (self .success_messages )} records successfully processed" )
131
- return
135
+ return None
132
136
133
137
queue_url = self ._get_queue_url ()
134
138
entries_to_remove = self ._get_entries_to_clean ()
135
-
136
- delete_message_response = None
137
- while entries_to_remove :
138
- # Batch delete up to 10 messages at a time (SQS limit)
139
- delete_message_response = self .client .delete_message_batch (
140
- QueueUrl = queue_url ,
141
- Entries = entries_to_remove [:10 ],
142
- )
143
- entries_to_remove = entries_to_remove [10 :]
144
-
139
+ # Batch delete up to 10 messages at a time (SQS limit)
140
+ max_workers = math .ceil (len (entries_to_remove ) / self .max_message_batch )
141
+
142
+ if entries_to_remove :
143
+ with ThreadPoolExecutor (max_workers = max_workers ) as executor :
144
+ futures , results = [], []
145
+ while entries_to_remove :
146
+ futures .append (
147
+ executor .submit (
148
+ self ._delete_messages , queue_url , entries_to_remove [: self .max_message_batch ], self .client
149
+ )
150
+ )
151
+ entries_to_remove = entries_to_remove [self .max_message_batch :]
152
+ for future in as_completed (futures ):
153
+ try :
154
+ logger .debug ("Deleted batch of processed messages from SQS" )
155
+ results .append (future .result ())
156
+ except Exception :
157
+ logger .exception ("Couldn't remove batch of processed messages from SQS" )
158
+ raise
145
159
if self .suppress_exception :
146
160
logger .debug (f"{ len (self .fail_messages )} records failed processing, but exceptions are suppressed" )
147
161
else :
@@ -152,6 +166,13 @@ def _clean(self):
152
166
child_exceptions = self .exceptions ,
153
167
)
154
168
169
+ return results
170
+
171
+ def _delete_messages (self , queue_url : str , entries_to_remove : List , sqs_client : Any ):
172
+ delete_message_response = sqs_client .delete_message_batch (
173
+ QueueUrl = queue_url ,
174
+ Entries = entries_to_remove ,
175
+ )
155
176
return delete_message_response
156
177
157
178
0 commit comments