|
19 | 19 | import com.amazonaws.services.lambda.runtime.Context; |
20 | 20 | import com.amazonaws.services.lambda.runtime.events.DynamodbEvent; |
21 | 21 | import com.amazonaws.services.lambda.runtime.events.StreamsEventResponse; |
| 22 | +import com.amazonaws.services.lambda.runtime.events.models.dynamodb.StreamRecord; |
22 | 23 | import com.amazonaws.services.lambda.runtime.tests.annotations.Event; |
| 24 | + |
| 25 | +import java.time.Duration; |
23 | 26 | import java.util.ArrayList; |
24 | 27 | import java.util.Collections; |
25 | 28 | import java.util.List; |
|
28 | 31 | import java.util.concurrent.Executors; |
29 | 32 | import java.util.concurrent.atomic.AtomicBoolean; |
30 | 33 | import java.util.function.BiConsumer; |
| 34 | +import java.util.stream.Collectors; |
31 | 35 |
|
32 | 36 | import org.junit.jupiter.api.AfterEach; |
33 | 37 | import org.junit.jupiter.params.ParameterizedTest; |
34 | 38 | import org.mockito.Mock; |
35 | 39 | import software.amazon.lambda.powertools.batch.handler.BatchMessageHandler; |
| 40 | +import software.amazon.lambda.powertools.batch.testutils.BatchMessageHandlerTestUtils; |
36 | 41 |
|
37 | 42 | class DdbBatchProcessorTest { |
38 | 43 |
|
@@ -88,9 +93,17 @@ private void processMessageInParallelFailsForFixedMessage(DynamodbEvent.Dynamodb |
88 | 93 | private StreamsEventResponse testParallelBatchExecution(DynamodbEvent event, |
89 | 94 | BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> messageHandler, |
90 | 95 | Executor executor) { |
| 96 | + return testParallelBatchExecution(event, messageHandler, null, executor); |
| 97 | + } |
| 98 | + |
| 99 | + private StreamsEventResponse testParallelBatchExecution(DynamodbEvent event, |
| 100 | + BiConsumer<DynamodbEvent.DynamodbStreamRecord, Context> messageHandler, |
| 101 | + BiConsumer<DynamodbEvent.DynamodbStreamRecord, Throwable> failureHandler, |
| 102 | + Executor executor) { |
91 | 103 | // Arrange |
92 | 104 | BatchMessageHandler<DynamodbEvent, StreamsEventResponse> handler = new BatchMessageHandlerBuilder() |
93 | 105 | .withDynamoDbBatchHandler() |
| 106 | + .withFailureHandler(failureHandler) |
94 | 107 | .buildWithRawMessageHandler(messageHandler); |
95 | 108 |
|
96 | 109 | // Act |
@@ -238,4 +251,23 @@ void parallelBatchProcessingWithExecutor_shouldAddMessageToBatchFailure_whenExce |
238 | 251 | assertThat(threadList).hasSizeGreaterThan(1); |
239 | 252 | } |
240 | 253 |
|
| 254 | + @ParameterizedTest |
| 255 | + @Event(value = "dynamo_event_big.json", type = DynamodbEvent.class) |
| 256 | + void parallelBatchProcessing_whenCollectingBatchItemFailures_shouldBeThreadSafe(DynamodbEvent event) { |
| 257 | + int nThreads = 8; |
| 258 | + List<String> batchItemFailureIds; |
| 259 | + ExecutorService executor = Executors.newFixedThreadPool(nThreads); |
| 260 | + try { |
| 261 | + StreamsEventResponse dynamodbBatchResponse = testParallelBatchExecution(event, (message, ctx) -> { |
| 262 | + throw new RuntimeException("fake exception"); |
| 263 | + }, BatchMessageHandlerTestUtils.spinGateFailureHandler(nThreads, Duration.ofSeconds(5)), executor); |
| 264 | + batchItemFailureIds = dynamodbBatchResponse.getBatchItemFailures().stream().map( |
| 265 | + StreamsEventResponse.BatchItemFailure::getItemIdentifier).collect(Collectors.toList()); |
| 266 | + } finally { |
| 267 | + executor.shutdown(); |
| 268 | + } |
| 269 | + List<String> sequenceNumbers = event.getRecords().stream().map(DynamodbEvent.DynamodbStreamRecord::getDynamodb) |
| 270 | + .map(StreamRecord::getSequenceNumber).collect(Collectors.toList()); |
| 271 | + assertThat(batchItemFailureIds).containsExactlyInAnyOrderElementsOf(sequenceNumbers); |
| 272 | + } |
241 | 273 | } |
0 commit comments