grpc-js: Fix and optimize IDLE timeouts #2643
Merged
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
This fixes #2642: in the current idle timer implementation, if
getConnectivityState(true)
is called multiple times, it can lose track of idle timer handles without cancelling them, resulting in those timers firing inappropriately and setting the connectivity state to IDLE when that should not happen.This change has two layers of defense against that failure: first, the timer now cannot be started if one is already running, so it should not be possible to lose track of existing running timers. Second, the client now tracks the timestamp of the last activity, and the idle timer will only actually set the state to IDLE if enough time has passed since the last activity.
In addition, this second change is part of an optimization to the idle timeout behavior: previously, the timer would start every time the number of active calls increased from 0, and was cancelled every time the number of active calls decreased to 0. This could cause a lot of timer churn if the user was making many requests in sequence, one at a time. Now instead, the timer is started the first time it is reasonable to do so, and after that it runs for the full timeout and then is started again every time the timer fires, with a variable timeout depending on the time since the last activity. As a result, there is only activity related to the idle timer at most twice per timeout period.