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

handle TCP transports properly #201

Merged
merged 5 commits into from
Sep 12, 2019
Merged

handle TCP transports properly #201

merged 5 commits into from
Sep 12, 2019

Conversation

hanshasselberg
Copy link
Member

@hanshasselberg hanshasselberg commented Sep 10, 2019

The underlying issue is that UDP and TCP are different. And since any transport is allowed, memberlist must account for both cases. For example with TCP a ping fails while sending, while with UDP there is no response.

If there would be a way to detect if a transport is TCP or UDP I would prefer that over implementing ProtocolType().

I checked the other occurrences of encodeAndSendMsg and nothing else should need special handling.

@hanshasselberg hanshasselberg requested a review from a team September 10, 2019 09:54
Copy link
Member

@mkeeler mkeeler left a comment

Choose a reason for hiding this comment

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

Not having made a super thorough look into memberlist my thinking is that conceptually the transport should handle any underlying protocol specific behavior and this shouldn't have to be cared about int state.go.

I am not sure how we could accomplish this but it just seems odd that protocol specifics are in the state code and that maybe the Transport interface should be updated more extensively to provide an interface where more of the protocol specifics can be left to them.

@hanshasselberg
Copy link
Member Author

@mkeeler thanks for looking at it! I am trying to imagine what your transport would look like and I am not sure what you have in mind. I don't think the transport should handle these case, since they are not transport internal, but memberlist internal. This code changes the flow in probeNode, this is nothing the transport should be doing.

The code in probeNode assumed a UDP protocol and I added the TCP part. That doesn't seem to bad to me.
If I had to abstract it more, I would probably end up with a Prober interface and TCPProber and UDPProber implementing it.

transport.go Outdated
@@ -62,4 +69,6 @@ type Transport interface {
// Shutdown is called when memberlist is shutting down; this gives the
// transport a chance to clean up any listeners.
Shutdown() error

ProtocolType() ProtocolType
Copy link
Member

Choose a reason for hiding this comment

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

Note this is a breaking change to the exported interface. Any custom implementations of the interface will no longer implement this interface.

An alternative would be making a new interface like:

type TypedTransport interface {
  Transport
  ProtocolType() ProtocolType
}

Then you just have to do optional type assertions to see if the transport implements the extra method. I recently did this in memberlist when i had to change Broadcast: https://github.com/hashicorp/memberlist/blob/master/queue.go#L128

state.go Show resolved Hide resolved
state.go Outdated
} else {
close(fallbackCh)
}
if m.transport.ProtocolType() == ProtocolTypeUDP {
Copy link
Member

Choose a reason for hiding this comment

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

Ugh. So my earlier suggestion about piggybacking on the error in WriteTo falls apart at this line.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, but my net.OpError checking as well. This is why I took that out again. It is just an optimization. Since TCP failed before, it will likely fail again. I am fine with that, considering how broken it is atm.

state.go Show resolved Hide resolved
}
return false
}

// probeNode handles a single round of failure checking on a node.
func (m *Memberlist) probeNode(node *nodeState) {
Copy link
Member

Choose a reason for hiding this comment

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

maybe also worth adding a test that forces the mock transport to fail in the manner of a tcp failure so you can assert this entire method functions as you expect

Copy link
Member Author

Choose a reason for hiding this comment

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

good point. I tried to do that, but turns out it is not that easy. :( ended up not getting it in.

Copy link
Member Author

Choose a reason for hiding this comment

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

I did however ran many many tests with a consul cluster setup that had network areas.

Copy link
Member

@mkeeler mkeeler left a comment

Choose a reason for hiding this comment

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

LGTM

@mkeeler mkeeler merged commit 08c40f9 into master Sep 12, 2019
@mkeeler mkeeler deleted the transport_protocol_type branch September 12, 2019 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants