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

Multiplex RPCs on a single connection. #34

Merged
merged 2 commits into from
Oct 12, 2017

Conversation

jakobr-google
Copy link
Contributor

A Channel will now multiplex RPCs on a single managed connection. The
connection will attempt to reconnect on failure, and will close the
underlying transport connection if no RPCs have been made for a while.

Part of #5.

A Channel will now multiplex RPCs on a single managed connection. The
connection will attempt to reconnect on failure, and will close the
underlying transport connection if no RPCs have been made for a while.

Part of grpc#5.
Copy link

@mravn-google mravn-google left a comment

Choose a reason for hiding this comment

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

LGTM

harness.signalIdle();
expect(
connectionStates, [ConnectionState.connecting, ConnectionState.ready]);
await new Future.delayed(const Duration(microseconds: 100));

Choose a reason for hiding this comment

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

You could complete a Completer in your onStateChanged callback when ConnectionState.idle appears. Then the test does not need an arbitrary delay.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done. I kind of wanted to make sure I didn't get another state change after idle, but I don't really think it matters.

{List<int> certificate, String password, String authority})
: this._(true, certificate, password, authority);
const ChannelOptions.insecure(
{Duration idleTimeout,

Choose a reason for hiding this comment

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

Is this formatting done by dartfmt?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Hard to believe, but yes. It insists. (And the Travis checks enforce using dartfmt.)

}
return context;
}
}

enum ConnectionState {

Choose a reason for hiding this comment

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

The enum constants should ideally use the same grammatical form. Here, connecting/ready/idle use one form, whereas transientFailure uses another, and shutdown can be read in two different ways, one of which is a command. Maybe connecting/ready/failed/idle/down?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


void _handleIdleTimeout() {
if (_timer == null) return;
_timer = null;

Choose a reason for hiding this comment

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

Use _cancelTimer instead of setting _timer to null in more than one place. Conveys intent better, I think. Plus it cancels unwanted timer events instead of attempting to ignore them when they occur. Unwanted timer events can make debugging hard, but I think your attempt at ignoring them may even fail, if a new timer has been set up in the mean time? You may need to receive the timer object with timer events and compare to current _timer before reacting.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've added in a check for the current state. Idle timeout should only fire while in the ready state. We only set the timeout when there are no more active streams, and we explicitly clear it when the connection is active again, so if we get a timeout callback, and:

  • _state == ready
  • _timer != null

then at least there are no active streams. Worst case, we end up closing an idle connection too soon, but the following would have to happen:

  1. Connection has been idle long enough for the timer to fire and schedule an event to call _handleTimeout().
  2. Before _handleTimeout() is actually called, the currently running code makes an RPC, making the connection active (and clearing _timer in the process).
  3. Still before the scheduled _handleTimeout() call is invoked, the RPC manages to finish, and a new idle timeout is scheduled.
  4. The original _handleTimeout() call finally runs, and closes the connection.

I don't think it's possible to complete an RPC without processing other events in the queue (especially since the onActiveStateChange call to mark the transport idle is delivered in a Future event, which would come later in the queue), so I we should be good now.

// TODO(jakobr): Log error.
_cancelTimer();
if (!_hasPendingCalls()) {
// If there are no pending calls, just go directly to idle.

Choose a reason for hiding this comment

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

[nit] This comment just repeats what the code already says.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

True, it does now. Removed.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants