-
Notifications
You must be signed in to change notification settings - Fork 177
Cleanup the way Pollers handle InterruptedExceptions #478
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -44,9 +44,9 @@ public LocalActivityWorker.Task poll() { | |
log.trace("LocalActivity Task poll returned: " + task.getActivityId()); | ||
} | ||
return task; | ||
|
||
} catch (InterruptedException e) { | ||
throw new RuntimeException("local activity poll task interrupted", e); | ||
Thread.currentThread().interrupt(); | ||
return null; | ||
} | ||
} | ||
|
||
|
@@ -67,6 +67,7 @@ public Boolean apply(LocalActivityWorker.Task task, Duration maxWaitAllowed) { | |
} | ||
return accepted; | ||
} catch (InterruptedException e) { | ||
Thread.currentThread().interrupt(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here previously we potentially were completely loosing an interrupted flag of a worker thread. [WorkflowWorker -> ReplayWorkflowTaskHandler -> ReplayWorkflowRunTaskHandler -> LocalActivityPollTask#apply] |
||
return false; | ||
} | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -39,6 +39,15 @@ | |
public final class Poller<T> implements SuspendableWorker { | ||
|
||
public interface PollTask<TT> { | ||
/** | ||
* Pollers should shade all {@code java.lang.InterruptedException}s and raise {@code | ||
* Thread.interrupted()} flag if the exceptions happen. This follows GRPC stubs approach, see | ||
* {@code io.grpc.stub.ClientCalls#blockingUnaryCall}. Because pollers use GRPC subs anyway, | ||
* this implementation was chosen for consistency. The caller of the poll task is responsible | ||
* for handling the flag | ||
* | ||
* @return result of the task | ||
*/ | ||
TT poll(); | ||
} | ||
|
||
|
@@ -93,7 +102,7 @@ public Poller( | |
@Override | ||
public void start() { | ||
if (log.isInfoEnabled()) { | ||
log.info("start(): " + toString()); | ||
log.info("start(): " + this); | ||
} | ||
if (pollerOptions.getMaximumPollRatePerSecond() > 0.0) { | ||
pollRateThrottler = | ||
|
@@ -213,13 +222,7 @@ private class PollLoopTask implements Runnable { | |
@Override | ||
public void run() { | ||
try { | ||
if (pollExecutor.isShutdown()) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is not needed here. This condition was just checked in Poller in |
||
return; | ||
} | ||
pollBackoffThrottler.throttle(); | ||
if (pollExecutor.isTerminating()) { | ||
return; | ||
} | ||
if (pollRateThrottler != null) { | ||
pollRateThrottler.throttle(); | ||
} | ||
|
@@ -232,27 +235,45 @@ public void run() { | |
suspender.await(); | ||
} | ||
|
||
if (pollExecutor.isShutdown()) { | ||
if (shouldTerminate()) { | ||
return; | ||
} | ||
|
||
task.run(); | ||
pollBackoffThrottler.success(); | ||
} catch (InterruptedException e) { | ||
vitarb marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// we restore the flag here, so it can be checked and processed (with exit) in finally | ||
Thread.currentThread().interrupt(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should we also do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would say no. InterruptedException is not really something that should trigger a backoff, it's not really a "failure" of any sort, it's a sign that we should terminate the thread because something is canceled or shutting down. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I agree, main use case for this is probably shutdown and it doesn't really matter what happens with counters at that point, I just didn't want to have a code path where we are not incrementing the counter back. Not a blocker if you feel strongly about it though. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm actually not fully getting what do you mean by "leaking" and "incrementing the counter back". It sounds like you expect |
||
} catch (Throwable e) { | ||
pollBackoffThrottler.failure(); | ||
if (!pollExecutor.isShutdown() && !pollExecutor.isTerminating() | ||
|| !(e.getCause() instanceof InterruptedException) | ||
&& !(e instanceof RejectedExecutionException)) { | ||
// if we are terminating and getting rejected execution - it's normal | ||
if (!pollExecutor.isTerminating() || !(e instanceof RejectedExecutionException)) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's now replaced with a proper interrupted flag handling. |
||
uncaughtExceptionHandler.uncaughtException(Thread.currentThread(), e); | ||
} | ||
} finally { | ||
// Resubmit itself back to pollExecutor | ||
if (!pollExecutor.isTerminating() && !pollExecutor.isShutdown()) { | ||
if (!shouldTerminate()) { | ||
// Resubmit itself back to pollExecutor | ||
pollExecutor.execute(this); | ||
} else { | ||
log.info("poll loop done"); | ||
log.info("poll loop is terminated"); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Defines if the task should be terminated. | ||
* | ||
* This method preserves the interrupted flag of the current thread. | ||
* | ||
* @return true if pollExecutor is terminating, or the current thread is interrupted. | ||
*/ | ||
private boolean shouldTerminate() { | ||
boolean threadIsInterrupted = Thread.interrupted(); | ||
if (threadIsInterrupted) { | ||
Thread.currentThread().interrupt(); | ||
} | ||
return pollExecutor.isTerminating() || threadIsInterrupted; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
} | ||
} | ||
|
||
private class PollExecutionTask implements Poller.ThrowingRunnable { | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
copy-paste cleanup, Issue #204