|
95 | 95 | import java.util.Optional; |
96 | 96 | import java.util.Set; |
97 | 97 | import java.util.UUID; |
| 98 | +import java.util.concurrent.CountDownLatch; |
98 | 99 | import java.util.concurrent.ExecutionException; |
99 | | -import java.util.concurrent.ExecutorService; |
100 | | -import java.util.concurrent.Future; |
101 | | -import java.util.concurrent.SynchronousQueue; |
102 | | -import java.util.concurrent.ThreadPoolExecutor; |
103 | 100 | import java.util.concurrent.TimeUnit; |
104 | 101 | import java.util.concurrent.atomic.AtomicInteger; |
105 | 102 | import java.util.function.BiFunction; |
@@ -1169,106 +1166,111 @@ private void uploadSnapshotDiffJobToDb(SnapshotInfo fromSnapshot, |
1169 | 1166 | } |
1170 | 1167 |
|
1171 | 1168 | private static Stream<Arguments> threadPoolFullScenarios() { |
| 1169 | + int fullThreadPoolSize = 2 * OZONE_OM_SNAPSHOT_DIFF_THREAD_POOL_SIZE_DEFAULT; |
1172 | 1170 | return Stream.of( |
1173 | | - Arguments.of("When there is a wait time between job batches", |
1174 | | - 500L, 45, 0), |
1175 | | - Arguments.of("When there is no wait time between job batches", |
1176 | | - 0L, 20, 25) |
| 1171 | + Arguments.of("When the pool drains between job batches", |
| 1172 | + true, 45, 0), |
| 1173 | + Arguments.of("When the pool does not drain between job batches", |
| 1174 | + false, fullThreadPoolSize, 45 - fullThreadPoolSize) |
1177 | 1175 | ); |
1178 | 1176 | } |
1179 | 1177 |
|
1180 | 1178 | @ParameterizedTest(name = "{0}") |
1181 | 1179 | @MethodSource("threadPoolFullScenarios") |
1182 | 1180 | public void testThreadPoolIsFull(String description, |
1183 | | - long waitBetweenBatches, |
| 1181 | + boolean drainBetweenBatches, |
1184 | 1182 | int expectInProgressJobsCount, |
1185 | 1183 | int expectRejectedJobsCount) |
1186 | 1184 | throws Exception { |
1187 | | - ExecutorService executorService = new ThreadPoolExecutor(100, 100, 0, |
1188 | | - TimeUnit.MILLISECONDS, new SynchronousQueue<>() |
1189 | | - ); |
1190 | | - |
1191 | | - List<SnapshotInfo> snapshotInfos = new ArrayList<>(); |
1192 | | - |
1193 | | - for (int i = 0; i < 10; i++) { |
1194 | | - UUID snapshotId = UUID.randomUUID(); |
1195 | | - String snapshotName = "snap-" + snapshotId; |
1196 | | - SnapshotInfo snapInfo = new SnapshotInfo.Builder() |
1197 | | - .setSnapshotId(snapshotId) |
1198 | | - .setVolumeName(VOLUME_NAME) |
1199 | | - .setBucketName(BUCKET_NAME) |
1200 | | - .setName(snapshotName) |
1201 | | - .setSnapshotPath("fromSnapshotPath") |
1202 | | - .build(); |
1203 | | - snapshotInfos.add(snapInfo); |
1204 | | - |
1205 | | - when(snapshotInfoTable.get(getTableKey(VOLUME_NAME, BUCKET_NAME, |
1206 | | - snapshotName))).thenReturn(snapInfo); |
1207 | | - } |
1208 | | - |
| 1185 | + List<SnapshotInfo> snapshotInfos = createTestSnapshots(10); |
1209 | 1186 | SnapshotDiffManager spy = spy(snapshotDiffManager); |
1210 | 1187 |
|
1211 | | - for (int i = 0; i < snapshotInfos.size(); i++) { |
1212 | | - for (int j = i + 1; j < snapshotInfos.size(); j++) { |
1213 | | - String fromSnapshotName = snapshotInfos.get(i).getName(); |
1214 | | - String toSnapshotName = snapshotInfos.get(j).getName(); |
| 1188 | + CountDownLatch blockWorkers = new CountDownLatch(1); |
| 1189 | + AtomicInteger completedJobs = new AtomicInteger(0); |
| 1190 | + doAnswer(invocation -> { |
| 1191 | + blockWorkers.await(); |
| 1192 | + completedJobs.incrementAndGet(); |
| 1193 | + return null; |
| 1194 | + }).when(spy).generateSnapshotDiffReport(anyString(), anyString(), |
| 1195 | + eq(VOLUME_NAME), eq(BUCKET_NAME), anyString(), anyString(), |
| 1196 | + eq(false), eq(false)); |
1215 | 1197 |
|
1216 | | - doAnswer(invocation -> { |
1217 | | - Thread.sleep(250L); |
1218 | | - return null; |
1219 | | - }).when(spy).generateSnapshotDiffReport(anyString(), anyString(), |
1220 | | - eq(VOLUME_NAME), eq(BUCKET_NAME), eq(fromSnapshotName), |
1221 | | - eq(toSnapshotName), eq(false), eq(false)); |
1222 | | - } |
| 1198 | + if (drainBetweenBatches) { |
| 1199 | + blockWorkers.countDown(); |
1223 | 1200 | } |
1224 | 1201 |
|
1225 | | - List<Future<SnapshotDiffResponse>> futures = new ArrayList<>(); |
1226 | | - for (int i = 0; i < snapshotInfos.size(); i++) { |
1227 | | - for (int j = i + 1; j < snapshotInfos.size(); j++) { |
1228 | | - String fromSnapshotName = snapshotInfos.get(i).getName(); |
1229 | | - String toSnapshotName = snapshotInfos.get(j).getName(); |
1230 | | - |
1231 | | - Future<SnapshotDiffResponse> future = executorService.submit( |
1232 | | - () -> submitJob(spy, fromSnapshotName, toSnapshotName)); |
1233 | | - futures.add(future); |
| 1202 | + try { |
| 1203 | + List<SnapshotDiffResponse> responses = new ArrayList<>(); |
| 1204 | + int totalSubmitted = 0; |
| 1205 | + for (int i = 0; i < snapshotInfos.size(); i++) { |
| 1206 | + for (int j = i + 1; j < snapshotInfos.size(); j++) { |
| 1207 | + String fromSnapshotName = snapshotInfos.get(i).getName(); |
| 1208 | + String toSnapshotName = snapshotInfos.get(j).getName(); |
| 1209 | + responses.add(submitJob(spy, fromSnapshotName, toSnapshotName)); |
| 1210 | + totalSubmitted++; |
| 1211 | + } |
| 1212 | + if (drainBetweenBatches) { |
| 1213 | + final int expected = totalSubmitted; |
| 1214 | + attempt(() -> { |
| 1215 | + if (completedJobs.get() < expected) { |
| 1216 | + throw new IllegalStateException("Waiting for jobs to complete"); |
| 1217 | + } |
| 1218 | + return null; |
| 1219 | + }, 50, TimeDuration.valueOf(100, TimeUnit.MILLISECONDS), null, null); |
| 1220 | + } |
1234 | 1221 | } |
1235 | | - Thread.sleep(waitBetweenBatches); |
1236 | | - } |
1237 | 1222 |
|
1238 | | - // Wait to make sure that all jobs finish before assertion. |
1239 | | - Thread.sleep(1000L); |
1240 | | - int inProgressJobsCount = 0; |
1241 | | - int rejectedJobsCount = 0; |
1242 | | - |
1243 | | - for (Future<SnapshotDiffResponse> future : futures) { |
1244 | | - SnapshotDiffResponse response = future.get(); |
1245 | | - if (response.getJobStatus() == IN_PROGRESS) { |
1246 | | - inProgressJobsCount++; |
1247 | | - } else if (response.getJobStatus() == REJECTED) { |
1248 | | - rejectedJobsCount++; |
1249 | | - } else { |
1250 | | - throw new IllegalStateException("Unexpected job status."); |
| 1223 | + int inProgressJobsCount = 0; |
| 1224 | + int rejectedJobsCount = 0; |
| 1225 | + for (SnapshotDiffResponse response : responses) { |
| 1226 | + if (response.getJobStatus() == IN_PROGRESS) { |
| 1227 | + inProgressJobsCount++; |
| 1228 | + } else if (response.getJobStatus() == REJECTED) { |
| 1229 | + rejectedJobsCount++; |
| 1230 | + } else { |
| 1231 | + throw new IllegalStateException("Unexpected job status."); |
| 1232 | + } |
1251 | 1233 | } |
1252 | | - } |
1253 | 1234 |
|
1254 | | - assertEquals(expectInProgressJobsCount, inProgressJobsCount); |
1255 | | - assertEquals(expectRejectedJobsCount, rejectedJobsCount); |
1256 | | - |
1257 | | - int notFoundJobs = 0; |
1258 | | - for (int i = 0; i < snapshotInfos.size(); i++) { |
1259 | | - for (int j = i + 1; j < snapshotInfos.size(); j++) { |
1260 | | - SnapshotDiffJob diffJob = |
1261 | | - getSnapshotDiffJobFromDb(snapshotInfos.get(i), |
1262 | | - snapshotInfos.get(j)); |
1263 | | - if (diffJob == null) { |
1264 | | - notFoundJobs++; |
| 1235 | + assertEquals(expectInProgressJobsCount, inProgressJobsCount); |
| 1236 | + assertEquals(expectRejectedJobsCount, rejectedJobsCount); |
| 1237 | + |
| 1238 | + int notFoundJobs = 0; |
| 1239 | + for (int i = 0; i < snapshotInfos.size(); i++) { |
| 1240 | + for (int j = i + 1; j < snapshotInfos.size(); j++) { |
| 1241 | + SnapshotDiffJob diffJob = |
| 1242 | + getSnapshotDiffJobFromDb(snapshotInfos.get(i), |
| 1243 | + snapshotInfos.get(j)); |
| 1244 | + if (diffJob == null) { |
| 1245 | + notFoundJobs++; |
| 1246 | + } |
1265 | 1247 | } |
1266 | 1248 | } |
| 1249 | + |
| 1250 | + // assert that rejected jobs were removed from the job table as well. |
| 1251 | + assertEquals(expectRejectedJobsCount, notFoundJobs); |
| 1252 | + } finally { |
| 1253 | + blockWorkers.countDown(); |
1267 | 1254 | } |
| 1255 | + } |
1268 | 1256 |
|
1269 | | - // assert that rejected jobs were removed from the job table as well. |
1270 | | - assertEquals(expectRejectedJobsCount, notFoundJobs); |
1271 | | - executorService.shutdown(); |
| 1257 | + private List<SnapshotInfo> createTestSnapshots(int count) throws IOException { |
| 1258 | + List<SnapshotInfo> snapshotInfos = new ArrayList<>(); |
| 1259 | + for (int i = 0; i < count; i++) { |
| 1260 | + UUID snapshotId = UUID.randomUUID(); |
| 1261 | + String snapshotName = "snap-" + snapshotId; |
| 1262 | + SnapshotInfo snapInfo = new SnapshotInfo.Builder() |
| 1263 | + .setSnapshotId(snapshotId) |
| 1264 | + .setVolumeName(VOLUME_NAME) |
| 1265 | + .setBucketName(BUCKET_NAME) |
| 1266 | + .setName(snapshotName) |
| 1267 | + .setSnapshotPath("fromSnapshotPath") |
| 1268 | + .build(); |
| 1269 | + snapshotInfos.add(snapInfo); |
| 1270 | + when(snapshotInfoTable.get(getTableKey(VOLUME_NAME, BUCKET_NAME, snapshotName))) |
| 1271 | + .thenReturn(snapInfo); |
| 1272 | + } |
| 1273 | + return snapshotInfos; |
1272 | 1274 | } |
1273 | 1275 |
|
1274 | 1276 | private SnapshotDiffResponse submitJob(SnapshotDiffManager diffManager, |
@@ -1641,7 +1643,7 @@ public void testGetSnapshotDiffReportWhenDone() throws Exception { |
1641 | 1643 |
|
1642 | 1644 | SnapshotDiffManager spy = spy(snapshotDiffManager); |
1643 | 1645 | SnapshotDiffReportOzone dummyReport = new SnapshotDiffReportOzone( |
1644 | | - SnapshotDiffManager.getSnapshotRootPath(ctx.volumeName, ctx.bucketName).toString(), |
| 1646 | + spy.getSnapshotRootPath(ctx.volumeName, ctx.bucketName).toString(), |
1645 | 1647 | ctx.volumeName, ctx.bucketName, ctx.fromSnapshotName, ctx.toSnapshotName, |
1646 | 1648 | expectedEntries, null); |
1647 | 1649 | doReturn(dummyReport).when(spy).createPageResponse(any(SnapshotDiffJob.class), |
|
0 commit comments