core: don't reschedule idle timer if it is already active#4297
core: don't reschedule idle timer if it is already active#4297carl-mastrangelo merged 7 commits intogrpc:masterfrom
Conversation
| } | ||
|
|
||
| /** Time source representing nanoseconds since fixed but arbitrary point in time. */ | ||
| interface Ticker { |
There was a problem hiding this comment.
FYI: Guava's ticker is an abstract class, and marked @Beta. I can't use it here, so I define an interface.
There was a problem hiding this comment.
I toyed with this some (just now) using Stopwatch. I think it ends up fine (no unnecessary now() calls in the normal paths), and it is a stable API.
There was a problem hiding this comment.
Switched to using stopwatch.
zhangkun83
left a comment
There was a problem hiding this comment.
Overall looks good with minor comments.
| } | ||
|
|
||
| private static class SystemTicker extends Ticker { | ||
| static class SystemTicker extends Ticker { |
There was a problem hiding this comment.
Reverted, this was originally going to be reused.
| private long runAt; | ||
| private boolean enabled; | ||
|
|
||
| Rescheduler(Runnable r, ChannelExecutor exec, ScheduledExecutorService scheduler) { |
There was a problem hiding this comment.
There is a way to decouple Rescheduler from ChannelExecutor. Pass an Executor here and make it an requirement that it must serialize its runnables and rechedule(). In ManagedChannelImpl, you could make an Executor that delegates to ChannelExecutor. I am strong for splitting this class out of ManagedChannelImpl, which is good for test-ability and also for the fitness of ManagedChannelImpl.
| private long runAt; | ||
| private boolean enabled; | ||
|
|
||
| Rescheduler(Runnable r, ChannelExecutor exec, ScheduledExecutorService scheduler) { |
| } | ||
|
|
||
| /** Time source representing nanoseconds since fixed but arbitrary point in time. */ | ||
| interface Ticker { |
There was a problem hiding this comment.
Switched to using stopwatch.
| } | ||
|
|
||
| private static class SystemTicker extends Ticker { | ||
| static class SystemTicker extends Ticker { |
There was a problem hiding this comment.
Reverted, this was originally going to be reused.
ejona86
left a comment
There was a problem hiding this comment.
Please still wait for @zhangkun83's review.
| boolean shouldAccept(Runnable runnable); | ||
| } | ||
| } | ||
| } No newline at end of file |
| return ((FutureRunnable) r).rescheduler.enabled; | ||
| } | ||
|
|
||
| private long nanoTime() { |
| } | ||
| } | ||
|
|
||
| private static final class FutureRunnable implements Runnable { |
There was a problem hiding this comment.
In ManagedChannelImplIdlenessTest.java there is a test that checks there are no more scheduled tasks in the scheduler. It fails because the tasks are still left over, but now disabled. I wanted to make it so the test scans the tasks and checks to see they are Rescheduler Runnables, and that they are disabled.
There needs to be a reference from the Runnable back to the Rescheduler, so this became static and took an explicit reference to the outer class. This is so isEnabled below works.
There was a problem hiding this comment.
Ah, I see. You could have added another method to FutureRunnable to do something like return Rescheduler.this, but that's close to the same amount of effort. Makes sense.
| assertFalse(runner.ran); | ||
| rescheduler.reschedule(1, TimeUnit.NANOSECONDS); | ||
| assertFalse(runner.ran); | ||
| rescheduler.cancel(/* permanent= */ false); |
There was a problem hiding this comment.
You probably need a test for cancel(true)
| rescheduler.reschedule(1, TimeUnit.NANOSECONDS); | ||
| assertFalse(runner.ran); | ||
| assertFalse(exec.executed); | ||
| rescheduler.reschedule(50, TimeUnit.NANOSECONDS); |
There was a problem hiding this comment.
Since you are keeping the flexibility of rescheduling with a shorter delay, you should cover it, or remove the flexibility.
@ejona86 @zhangkun83
I haven't quite got the tests to pass yet but I think this is close enough for review to take a look. The main idea is to avoid rescheduling the IdleModeTimer if it is already active. This has pretty good results on my machine for the latency benchmarks. With CPU freq scaling on, median latency goes from 197us to 185us. (no TLS, but with census enabled, direct executor).
The changes to get the tests to work are going to be harder since this change depends on
System.nanoTime. TheFakeClockwe use in our tests doesn't make it easy to mock this out, so I'm working on ideas to fix this. That said, it is starting to get invasive for tests to pass.Raw numbers from my runs: