18
18
import com .amazonaws .services .lambda .runtime .events .SQSBatchResponse ;
19
19
import com .amazonaws .services .lambda .runtime .events .SQSEvent ;
20
20
import java .util .ArrayList ;
21
+ import java .util .List ;
22
+ import java .util .Optional ;
23
+ import java .util .concurrent .atomic .AtomicBoolean ;
21
24
import java .util .function .BiConsumer ;
22
25
import java .util .function .Consumer ;
26
+ import java .util .stream .Collectors ;
23
27
import org .slf4j .Logger ;
24
28
import org .slf4j .LoggerFactory ;
29
+ import software .amazon .lambda .powertools .batch .internal .MultiThreadMDC ;
25
30
import software .amazon .lambda .powertools .utilities .EventDeserializer ;
26
31
27
32
/**
@@ -61,57 +66,27 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) {
61
66
// If we are working on a FIFO queue, when any message fails we should stop processing and return the
62
67
// rest of the batch as failed too. We use this variable to track when that has happened.
63
68
// https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting
64
- boolean failWholeBatch = false ;
69
+ AtomicBoolean failWholeBatch = new AtomicBoolean ( false ) ;
65
70
66
71
int messageCursor = 0 ;
67
- for (; messageCursor < event .getRecords ().size () && !failWholeBatch ; messageCursor ++) {
72
+ for (; messageCursor < event .getRecords ().size () && !failWholeBatch . get () ; messageCursor ++) {
68
73
SQSEvent .SQSMessage message = event .getRecords ().get (messageCursor );
69
74
70
75
String messageGroupId = message .getAttributes () != null ?
71
76
message .getAttributes ().get (MESSAGE_GROUP_ID_KEY ) : null ;
72
77
73
- try {
74
- if (this .rawMessageHandler != null ) {
75
- rawMessageHandler .accept (message , context );
76
- } else {
77
- M messageDeserialized = EventDeserializer .extractDataFrom (message ).as (messageClass );
78
- messageHandler .accept (messageDeserialized , context );
79
- }
80
-
81
- // Report success if we have a handler
82
- if (this .successHandler != null ) {
83
- this .successHandler .accept (message );
84
- }
85
-
86
- } catch (Throwable t ) {
87
- LOGGER .error ("Error while processing message with messageId {}: {}, adding it to batch item failures" ,
88
- message .getMessageId (), t .getMessage ());
89
- LOGGER .error ("Error was" , t );
90
-
91
- response .getBatchItemFailures ()
92
- .add (SQSBatchResponse .BatchItemFailure .builder ().withItemIdentifier (message .getMessageId ())
93
- .build ());
78
+ processBatchItem (message , context ).ifPresent (batchItemFailure -> {
79
+ response .getBatchItemFailures ().add (batchItemFailure );
94
80
if (messageGroupId != null ) {
95
- failWholeBatch = true ;
81
+ failWholeBatch . set ( true ) ;
96
82
LOGGER .info (
97
83
"A message in a batch with messageGroupId {} and messageId {} failed; failing the rest of the batch too"
98
84
, messageGroupId , message .getMessageId ());
99
85
}
100
-
101
- // Report failure if we have a handler
102
- if (this .failureHandler != null ) {
103
- // A failing failure handler is no reason to fail the batch
104
- try {
105
- this .failureHandler .accept (message , t );
106
- } catch (Throwable t2 ) {
107
- LOGGER .warn ("failureHandler threw handling failure" , t2 );
108
- }
109
- }
110
-
111
- }
86
+ });
112
87
}
113
88
114
- if (failWholeBatch ) {
89
+ if (failWholeBatch . get () ) {
115
90
// Add the remaining messages to the batch item failures
116
91
event .getRecords ()
117
92
.subList (messageCursor , event .getRecords ().size ())
@@ -121,4 +96,60 @@ public SQSBatchResponse processBatch(SQSEvent event, Context context) {
121
96
}
122
97
return response ;
123
98
}
99
+
100
+ @ Override
101
+ public SQSBatchResponse processBatchInParallel (SQSEvent event , Context context ) {
102
+ if (!event .getRecords ().isEmpty () && event .getRecords ().get (0 ).getAttributes ().get (MESSAGE_GROUP_ID_KEY ) != null ) {
103
+ LOGGER .warn ("FIFO queues are not supported in parallel mode, proceeding in sequence" );
104
+ return processBatch (event , context );
105
+ }
106
+
107
+ MultiThreadMDC multiThreadMDC = new MultiThreadMDC ();
108
+ List <SQSBatchResponse .BatchItemFailure > batchItemFailures = event .getRecords ()
109
+ .parallelStream () // Parallel processing
110
+ .map (sqsMessage -> {
111
+ multiThreadMDC .copyMDCToThread (Thread .currentThread ().getName ());
112
+ return processBatchItem (sqsMessage , context );
113
+ })
114
+ .filter (Optional ::isPresent )
115
+ .map (Optional ::get )
116
+ .collect (Collectors .toList ());
117
+
118
+ return SQSBatchResponse .builder ().withBatchItemFailures (batchItemFailures ).build ();
119
+ }
120
+
121
+ private Optional <SQSBatchResponse .BatchItemFailure > processBatchItem (SQSEvent .SQSMessage message , Context context ) {
122
+ try {
123
+ LOGGER .debug ("Processing message {}" , message .getMessageId ());
124
+
125
+ if (this .rawMessageHandler != null ) {
126
+ rawMessageHandler .accept (message , context );
127
+ } else {
128
+ M messageDeserialized = EventDeserializer .extractDataFrom (message ).as (messageClass );
129
+ messageHandler .accept (messageDeserialized , context );
130
+ }
131
+
132
+ // Report success if we have a handler
133
+ if (this .successHandler != null ) {
134
+ this .successHandler .accept (message );
135
+ }
136
+ return Optional .empty ();
137
+ } catch (Throwable t ) {
138
+ LOGGER .error ("Error while processing message with messageId {}: {}, adding it to batch item failures" ,
139
+ message .getMessageId (), t .getMessage ());
140
+ LOGGER .error ("Error was" , t );
141
+
142
+ // Report failure if we have a handler
143
+ if (this .failureHandler != null ) {
144
+ // A failing failure handler is no reason to fail the batch
145
+ try {
146
+ this .failureHandler .accept (message , t );
147
+ } catch (Throwable t2 ) {
148
+ LOGGER .warn ("failureHandler threw handling failure" , t2 );
149
+ }
150
+ }
151
+ return Optional .of (SQSBatchResponse .BatchItemFailure .builder ().withItemIdentifier (message .getMessageId ())
152
+ .build ());
153
+ }
154
+ }
124
155
}
0 commit comments