Skip to content
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

Don't schedule multiple pings. #3295

Merged
merged 1 commit into from
Aug 1, 2017
Merged

Don't schedule multiple pings. #3295

merged 1 commit into from
Aug 1, 2017

Conversation

stephenh
Copy link
Contributor

If onTransportActive ran while SendPing was already scheduled, we would
schedule another SendPing, which seems fine, but the server might observe
us sending pings too quickly, and make us GOAWAY.

Fixes #3274.

@grpc-kokoro
Copy link

Thanks for your pull request. The automated tests will run as soon as one of the admins verifies this change is ok for us to run on our infrastructure.

@@ -170,8 +171,10 @@ public synchronized void onDataReceived() {
}
// schedule a new ping
state = State.PING_SCHEDULED;
pingFuture =
scheduler.schedule(sendPing, keepAliveTimeInNanos, TimeUnit.NANOSECONDS);
if (pingFuture == null) {
Copy link
Member

@dapengzhang0 dapengzhang0 Jul 31, 2017

Choose a reason for hiding this comment

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

You can move this condition check if (pingFuture == null) up by replacing the condition check if (state == State.PING_SCHEDULED) above with it.

Edited:
You can remove this condition check if (pingFuture == null) and replace the condition check if (state == State.PING_SCHEDULED) above with if (pingFuture != null).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hm, that seems reasonable, but AFAIU onDataReceived can happen while we are in State.IDLE/IDLE_WITH_PING_SENT and so with your suggestion we would move to PING_SCHEDULED when really we might want to stay in IDLE. (Or something like that.)

I did make your suggested change and the tests still pass though...

Copy link
Member

Choose a reason for hiding this comment

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

I agree that we should leave this as-is. Mixing pingFuture checks with state machine checks makes the code complicated and easily broken (as is evidenced by the change breaking the IDLE case).

Copy link
Member

Choose a reason for hiding this comment

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

@stephenh Your argument makes sense. We should stay IDLE when we are in IDLE while ping had been scheduled, and then onDataReceived. But the check if (pingFuture == null) still looks strange, may be Preconditions.checkState(pingFuture == null, "There should be no outstanding pingFuture") at this place.

Copy link
Member

Choose a reason for hiding this comment

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

Adding a check to guarantee pingFuture == null before the scheduling SGTM.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah sure, that's a good idea.

@dapengzhang0
Copy link
Member

ok to test

@@ -69,6 +69,7 @@ public void run() {
// Schedule a shutdown. It fires if we don't receive the ping response within the timeout.
shutdownFuture = scheduler.schedule(shutdown, keepAliveTimeoutInNanos,
TimeUnit.NANOSECONDS);
pingFuture = null;
Copy link
Member

Choose a reason for hiding this comment

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

It seems like this should be done in all cases (but doesn't matter with DELAYED). If the state is IDLE, the current code would prevent all further PINGS (because it would never be rescheduled). Tying pingFuture == null to when there is no callback scheduled prevents having to consider most of those cases. So move clearing the field to before the if?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Great catch. I updated the transportGoesIdle test to reproduce this "will never reschedule" bug, and then fixed it by moving the pingFuture = null to the top of the Runnable.

@@ -170,8 +171,10 @@ public synchronized void onDataReceived() {
}
// schedule a new ping
state = State.PING_SCHEDULED;
pingFuture =
scheduler.schedule(sendPing, keepAliveTimeInNanos, TimeUnit.NANOSECONDS);
if (pingFuture == null) {
Copy link
Member

Choose a reason for hiding this comment

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

I agree that we should leave this as-is. Mixing pingFuture checks with state machine checks makes the code complicated and easily broken (as is evidenced by the change breaking the IDLE case).

If onTransportActive ran while SendPing was already scheduled, we would
schedule another SendPing, which seems fine, but the server might observe
us sending pings too quickly, and make us GOAWAY.

Fixes grpc#3274.
@ejona86
Copy link
Member

ejona86 commented Aug 1, 2017

Waiting for @dapengzhang0's review before merging.

Copy link
Member

@dapengzhang0 dapengzhang0 left a comment

Choose a reason for hiding this comment

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

LGTM. Mocking Future might produce some errorprone warnings internally, but as we may rewrite the tests per #2868 in the future, it fine for now.

@dapengzhang0 dapengzhang0 merged commit db9b7ed into grpc:master Aug 1, 2017
@dapengzhang0
Copy link
Member

@stephenh thanks a lot for the fix!

@lock lock bot locked as resolved and limited conversation to collaborators Jan 20, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants