|
1 | 1 | package org.opensearch.dataprepper.plugins.source.source_crawler.coordination.scheduler; |
2 | 2 |
|
| 3 | +import org.junit.jupiter.api.DisplayName; |
3 | 4 | import org.junit.jupiter.api.Test; |
4 | 5 | import org.junit.jupiter.api.extension.ExtendWith; |
5 | 6 | import org.junit.jupiter.params.ParameterizedTest; |
|
18 | 19 | import java.util.concurrent.ExecutorService; |
19 | 20 | import java.util.concurrent.Executors; |
20 | 21 |
|
| 22 | +import static org.junit.jupiter.api.Assertions.assertThrows; |
21 | 23 | import static org.mockito.ArgumentMatchers.any; |
22 | 24 | import static org.mockito.ArgumentMatchers.eq; |
23 | 25 | import static org.mockito.BDDMockito.given; |
|
27 | 29 | import static org.mockito.Mockito.verify; |
28 | 30 | import static org.mockito.Mockito.verifyNoInteractions; |
29 | 31 | import static org.mockito.Mockito.when; |
30 | | -import static org.junit.jupiter.api.Assertions.assertThrows; |
31 | 32 |
|
32 | 33 | @ExtendWith(MockitoExtension.class) |
33 | 34 | public class LeaderSchedulerTest { |
@@ -169,13 +170,56 @@ void testWhileLoopRunnningAfterTheSleep() throws InterruptedException { |
169 | 170 | void testSetLeaderProgressState_throwsExceptionOnStateMismatch() { |
170 | 171 | // Create a LeaderPartition with TokenPaginationCrawlerLeaderProgressState |
171 | 172 | LeaderPartition leaderPartition = new LeaderPartition(new TokenPaginationCrawlerLeaderProgressState("")); |
172 | | - |
| 173 | + |
173 | 174 | // Try to set a different type of state (PaginationCrawlerLeaderProgressState) |
174 | 175 | PaginationCrawlerLeaderProgressState incompatibleState = new PaginationCrawlerLeaderProgressState(Instant.now()); |
175 | | - |
| 176 | + |
176 | 177 | // Verify that RuntimeException is thrown due to state type mismatch |
177 | 178 | RuntimeException exception = assertThrows(RuntimeException.class, () -> { |
178 | 179 | leaderPartition.setLeaderProgressState(incompatibleState); |
179 | 180 | }); |
180 | 181 | } |
| 182 | + |
| 183 | + @Test |
| 184 | + @DisplayName("Ensure that if DynamoDB becomes unreachable, the leader gives up the partition and retries acquisition") |
| 185 | + void testLeaderPartitionGivenUpOnSaveFailure_andRetryAcquire() throws InterruptedException { |
| 186 | + // This test verifies the fix for line 72: leaderPartition = null when saveProgressStateForPartition fails |
| 187 | + // This ensures that if DynamoDB becomes unreachable, the leader gives up the partition and retries acquisition |
| 188 | + |
| 189 | + LeaderScheduler leaderScheduler = new LeaderScheduler(coordinator, crawler); |
| 190 | + leaderScheduler.setLeaseInterval(Duration.ofMillis(10)); |
| 191 | + |
| 192 | + LeaderPartition leaderPartition = new LeaderPartition(new TokenPaginationCrawlerLeaderProgressState("")); |
| 193 | + TokenPaginationCrawlerLeaderProgressState state = (TokenPaginationCrawlerLeaderProgressState) leaderPartition.getProgressState().get(); |
| 194 | + state.setInitialized(true); |
| 195 | + state.setLastToken(LAST_TOKEN); |
| 196 | + |
| 197 | + // First acquisition succeeds, but subsequent saveProgressStateForPartition fails (simulating DynamoDB outage) |
| 198 | + // Then second acquisition should be attempted after giving up the partition |
| 199 | + when(coordinator.acquireAvailablePartition(LeaderPartition.PARTITION_TYPE)) |
| 200 | + .thenReturn(Optional.of(leaderPartition)) // First acquisition succeeds |
| 201 | + .thenReturn(Optional.of(leaderPartition)); // Second acquisition after giving up partition |
| 202 | + |
| 203 | + // saveProgressStateForPartition throws exception to simulate DynamoDB outage |
| 204 | + doThrow(new RuntimeException("DynamoDB unreachable")) |
| 205 | + .doNothing() // Second time succeeds after recovery |
| 206 | + .when(coordinator).saveProgressStateForPartition(any(LeaderPartition.class), any(Duration.class)); |
| 207 | + |
| 208 | + ExecutorService executorService = Executors.newSingleThreadExecutor(); |
| 209 | + executorService.submit(leaderScheduler); |
| 210 | + |
| 211 | + // Wait long enough for multiple iterations |
| 212 | + Thread.sleep(100); |
| 213 | + executorService.shutdownNow(); |
| 214 | + |
| 215 | + // Verify that crawler was called multiple times (showing the scheduler continued to work) |
| 216 | + verify(crawler, atLeast(2)).crawl(any(LeaderPartition.class), any(EnhancedSourceCoordinator.class)); |
| 217 | + |
| 218 | + // Verify that acquireAvailablePartition was called at least twice: |
| 219 | + // once for initial acquisition, and again after giving up due to save failure |
| 220 | + verify(coordinator, atLeast(2)).acquireAvailablePartition(LeaderPartition.PARTITION_TYPE); |
| 221 | + |
| 222 | + // Verify that saveProgressStateForPartition was attempted multiple times |
| 223 | + verify(coordinator, atLeast(2)).saveProgressStateForPartition(any(LeaderPartition.class), any(Duration.class)); |
| 224 | + } |
181 | 225 | } |
0 commit comments