Skip to content

Commit efbac1c

Browse files
committed
Address comments
1 parent 6430094 commit efbac1c

1 file changed

Lines changed: 79 additions & 48 deletions

File tree

runners/google-cloud-dataflow-java/worker/src/test/java/org/apache/beam/runners/dataflow/worker/util/KeyGroupWorkQueueTest.java

Lines changed: 79 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@
3636
import java.util.concurrent.Executors;
3737
import java.util.concurrent.Future;
3838
import java.util.concurrent.TimeUnit;
39-
import java.util.concurrent.atomic.AtomicInteger;
4039
import java.util.concurrent.atomic.AtomicReference;
4140
import org.apache.beam.runners.dataflow.worker.streaming.ExecutableWork;
4241
import org.apache.beam.runners.dataflow.worker.streaming.Watermarks;
@@ -251,73 +250,103 @@ public void testPollWorkTargeted() {
251250

252251
@Test
253252
public void testConcurrentStress() throws InterruptedException, ExecutionException {
254-
final KeyGroupWorkQueue queue = new KeyGroupWorkQueue(fairQueue);
255-
final int producerThreads = 4;
256-
final int consumerThreads = 4;
257-
final int tasksPerProducer = 1000;
258-
final int totalTasks = producerThreads * tasksPerProducer;
253+
KeyGroupWorkQueue queue = new KeyGroupWorkQueue(fairQueue);
254+
int producerThreads = 4;
255+
int consumerThreads = 4;
256+
int tasksPerProducer = 1000;
257+
int totalTasks = producerThreads * tasksPerProducer;
258+
Runnable poisonPill =
259+
new Runnable() {
260+
@Override
261+
public void run() {}
262+
263+
@Override
264+
public String toString() {
265+
return "POISON_PILL";
266+
}
267+
};
259268

260269
ExecutorService executorService =
261270
Executors.newFixedThreadPool(producerThreads + consumerThreads);
262-
final CountDownLatch startLatch = new CountDownLatch(1);
263-
final CountDownLatch doneLatch = new CountDownLatch(producerThreads + consumerThreads);
264-
final AtomicInteger consumedCount = new AtomicInteger(0);
271+
CountDownLatch startLatch = new CountDownLatch(1);
272+
CountDownLatch producersDoneLatch = new CountDownLatch(producerThreads);
273+
CountDownLatch consumersDoneLatch = new CountDownLatch(consumerThreads);
274+
CountDownLatch consumedLatch = new CountDownLatch(totalTasks);
265275
List<Future<?>> futures = new ArrayList<>();
266276

267-
// Start producers
268-
for (int i = 0; i < producerThreads; i++) {
277+
// Start consumers
278+
for (int i = 0; i < consumerThreads; i++) {
279+
int consumerId = i;
269280
futures.add(
270281
executorService.submit(
271282
() -> {
272283
try {
273284
startLatch.await();
274-
for (int j = 0; j < tasksPerProducer; j++) {
275-
String compId = "comp-" + (j % 5);
276-
queue.offer(createQueuedWork(compId, 10));
285+
int iteration = consumerId % 4;
286+
while (true) {
287+
int strategy = iteration;
288+
iteration = (iteration + 1) % 4;
289+
Runnable task = null;
290+
if (strategy == 0) {
291+
String compId = "comp-" + (consumedLatch.getCount() % 5);
292+
task = queue.pollWork(compId, TEST_KEY_GROUP);
293+
} else if (strategy == 1) {
294+
task = queue.poll();
295+
} else if (strategy == 2) {
296+
task = queue.poll(10, TimeUnit.MICROSECONDS);
297+
} else if (strategy == 3) {
298+
task = queue.take();
299+
}
300+
301+
if (task == poisonPill) {
302+
break;
303+
}
304+
if (task != null) {
305+
consumedLatch.countDown();
306+
}
277307
}
278308
} catch (Exception e) {
279309
throw new RuntimeException(e);
280310
} finally {
281-
doneLatch.countDown();
311+
consumersDoneLatch.countDown();
282312
}
283313
}));
284314
}
285315

286-
// Start consumers (mix of poll and pollWork)
287-
for (int i = 0; i < consumerThreads; i++) {
288-
final int consumerId = i;
316+
// Start producers
317+
for (int i = 0; i < producerThreads; i++) {
289318
futures.add(
290319
executorService.submit(
291320
() -> {
292321
try {
293322
startLatch.await();
294-
while (consumedCount.get() < totalTasks) {
295-
Runnable task;
296-
if (consumerId % 2 == 0) {
297-
// Targeted poll
298-
String compId = "comp-" + (consumedCount.get() % 5);
299-
task = queue.pollWork(compId, TEST_KEY_GROUP);
300-
} else {
301-
// Global poll
302-
task = queue.poll();
303-
if (task == null) {
304-
task = queue.poll(10, TimeUnit.MICROSECONDS);
305-
}
306-
}
307-
if (task != null) {
308-
consumedCount.incrementAndGet();
309-
}
323+
for (int j = 0; j < tasksPerProducer; j++) {
324+
String compId = "comp-" + (j % 5);
325+
queue.offer(createQueuedWork(compId, 10));
310326
}
311327
} catch (Exception e) {
312328
throw new RuntimeException(e);
313329
} finally {
314-
doneLatch.countDown();
330+
producersDoneLatch.countDown();
315331
}
316332
}));
317333
}
318334

335+
// Release the start latch to start the test
319336
startLatch.countDown();
320-
assertTrue(doneLatch.await(30, TimeUnit.SECONDS));
337+
338+
// Wait for all tasks to be consumed
339+
assertTrue(consumedLatch.await(30, TimeUnit.SECONDS));
340+
341+
// Send poison pills to stop all consumers
342+
for (int i = 0; i < consumerThreads; i++) {
343+
queue.offer(poisonPill);
344+
}
345+
346+
// Wait for consumers to finish
347+
assertTrue(consumersDoneLatch.await(30, TimeUnit.SECONDS));
348+
// Wait for producers to finish
349+
assertTrue(producersDoneLatch.await(30, TimeUnit.SECONDS));
321350

322351
// Check for exceptions in threads
323352
for (Future<?> future : futures) {
@@ -355,10 +384,7 @@ public void testTakeBlocksAndWakesUp() throws InterruptedException {
355384
t.start();
356385

357386
assertTrue(started.await(30, TimeUnit.SECONDS));
358-
while (t.getState() != State.WAITING) {
359-
Thread.sleep(1);
360-
}
361-
assertEquals(Thread.State.WAITING, t.getState());
387+
waitForThreadState(t, State.WAITING);
362388

363389
queue.offer(task);
364390

@@ -391,10 +417,7 @@ public void testPollWithTimeout() throws InterruptedException {
391417
t1.start();
392418

393419
assertTrue(started.await(30, TimeUnit.SECONDS));
394-
while (t1.getState() != State.TIMED_WAITING) {
395-
Thread.sleep(1);
396-
}
397-
assertEquals(Thread.State.TIMED_WAITING, t1.getState());
420+
waitForThreadState(t1, State.TIMED_WAITING);
398421

399422
assertTrue(finished.await(30, TimeUnit.SECONDS));
400423
assertNull(result.get());
@@ -420,10 +443,7 @@ public void testPollWithTimeout() throws InterruptedException {
420443
t2.start();
421444

422445
assertTrue(started2.await(30, TimeUnit.SECONDS));
423-
while (t2.getState() != State.TIMED_WAITING) {
424-
Thread.sleep(1);
425-
}
426-
assertEquals(Thread.State.TIMED_WAITING, t2.getState());
446+
waitForThreadState(t2, State.TIMED_WAITING);
427447

428448
queue.offer(task);
429449

@@ -470,4 +490,15 @@ public void testPollWorkWithKeyGroup() {
470490
assertNull(polledNotExist);
471491
assertTrue(queue.isEmpty());
472492
}
493+
494+
private void waitForThreadState(Thread t, State state) throws InterruptedException {
495+
long timeoutMs = 30000;
496+
long start = System.currentTimeMillis();
497+
while (t.getState() != state) {
498+
if (System.currentTimeMillis() - start > timeoutMs) {
499+
fail("Thread did not reach " + state + " state within " + timeoutMs + "ms");
500+
}
501+
Thread.sleep(1);
502+
}
503+
}
473504
}

0 commit comments

Comments
 (0)