Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@ public MultiThreadedEventProcessor(
*/
private class EventProcessorThread extends Thread {
private final Logger log;
private long pollStartMs;
private long timeSinceLastPollMs;
private long lastPollMs;

EventProcessorThread(
String name
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,9 @@ public DelayEventAccumulator(Time time, long takeDelayMs) {

@Override
public CoordinatorEvent take() {
CoordinatorEvent event = super.take();

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, the fix suggests the flakyness is because super.take() returns null spuriously. I don't quite follow why that would happen though. Javadoc for EventAccumulator::take mentions it returns null only when the accumulator is closed. We also assert the value captured within doAnswer is 100, recordedIdleTimesMs.size() == 8 and mockRuntimeMetrics.recordThreadIdleTime() is invoked 8 times so where is the extra invocation coming from?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct and was my initial confusion as well. The event processor shuts down at the end of the test so we have the processor thread still running when we perform the assertions.

When we call

long diff = time.milliseconds() - startMs;

we may be at the point when we process the 8 events (diff == 800ms) or when the thread does another iteration over handleEvents(), which does another time.sleep() (diff == 900ms). This was causing the flakiness.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we may be at the point when we process the 8 events (diff == 800ms) or when the thread does another iteration over handleEvents(), which does another time.sleep() (diff == 900ms). This was causing the flakiness.

Sorry, I'm still confused. Why would another iteration over handleEvents() invoke the doAnswer? In other words, shouldn't accumulator.take() block since there are no more events? I'd imagine EventAccumulator::take to block on condition.await() after 8 events have been processed.

@gaurav-narula gaurav-narula May 17, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nvm, I see your point - accumulator.take() may be invoked - the block on the delegate may happen after the timer has been incremented.

@gaurav-narula gaurav-narula May 17, 2024

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IIUC, it's the ordering that matters so we can avoid the null check and increment time only after super.take() returns.

            CoordinatorEvent event = super.take(); // blocks until there are new events
            time.sleep(takeDelayMs); // only increment timer after we get unblocked
            return event;

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@gaurav-narula thanks for the feedback. that makes sense, i have incorporated your suggestion

time.sleep(takeDelayMs);
return super.take();
return event;
}
}

Expand Down Expand Up @@ -475,9 +476,9 @@ public void testRecordThreadIdleRatio() throws Exception {
doAnswer(invocation -> {
long threadIdleTime = idleTimeCaptured.getValue();
assertEquals(100, threadIdleTime);
synchronized (recordedIdleTimesMs) {
recordedIdleTimesMs.add(threadIdleTime);
}

// No synchronization required as the test uses a single event processor thread.
recordedIdleTimesMs.add(threadIdleTime);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: might be useful to add a comment that this is safe because numThreads in line 470 is 1.

return null;
}).when(mockRuntimeMetrics).recordThreadIdleTime(idleTimeCaptured.capture());

Expand Down