-
Notifications
You must be signed in to change notification settings - Fork 137
Feature hostnames #309
Feature hostnames #309
Conversation
OK. Fixed all build issues now. And signed all commits. Re-signed the contributor agreement the third time now :-( |
Signed-off-by: Norbert Heusser <[email protected]>
Signed-off-by: Norbert Heusser <[email protected]>
test case parameter Signed-off-by: Norbert Heusser <[email protected]>
number of test case with listen failures will increase Signed-off-by: Norbert Heusser <[email protected]>
Signed-off-by: Norbert Heusser <[email protected]>
Signed-off-by: Norbert Heusser <[email protected]>
https://bugzilla.redhat.com/show_bug.cgi?id=496300 Signed-off-by: Norbert Heusser <[email protected]>
beb4c9c
to
1d1767e
Compare
Codecov Report
@@ Coverage Diff @@
## master #309 +/- ##
==========================================
+ Coverage 83.54% 83.60% +0.06%
==========================================
Files 49 49
Lines 9268 9339 +71
Branches 2477 2499 +22
==========================================
+ Hits 7743 7808 +65
- Misses 969 970 +1
- Partials 556 561 +5
Help us with your feedback. Take ten seconds to tell us how you rate us. Have a feature suggestion? Share it here. |
1d1767e
to
07039e2
Compare
Signed-off-by: Norbert Heusser <[email protected]>
07039e2
to
0c370f0
Compare
Did an additional force push with just a whitespace formatting correction to force a new cla-check. |
The cla-check finally recognized you ... hooray for that :-) Will review soon. |
I am happy the cla-check works now for :-) Perhaps a few comments on the design. As the getaddrinfo may result into multiple IP addresses I need to deal with that in the different parts of the solution. This holds for the synchronous system call as well as for the asynchronous lbuv based invocation. The solution design I have chosen are:
|
Looks overall good to me at first sight. Before I dig deeper: how much important is it to support multiple listeners? Could we instead return an error if the hostname resolves to multiple IPs? Or choose the first IP. I know that's sub-optimal, just wondering if it is worth the cost of additional complexity or if it's something that will probably never be used in practice. For example is it relevant for your own use case? Since you already did all the work it's a pity to simplify it, but if there's no immediate need for multiple listeners we could keep this code around in a branch and only introduce this support when there's a request for it. I guess same reasoning for outgoing connections. |
Regarding the listen sockets dealing with multiple IP addresses immediately comes into play, if we would introduce handling of IPv6 addresses. As systems with IPv6 only are still extremely rare today most systems have dual mode using IPv4 and IPV6 at the same time. So with activation of IPv6 additionally to IPv4 most hostnames will resolve one (or more) IPv6 addresses and an IPv4 address. For outgoing TCP connections the most common use case I have seen in the past is the configuration of multiple network interfaces and IP addresses to achieve fail safety. And this is beside the cluster scale-out concept one of the most common use cases to install a cluster as a HA solution. This might be less common over the last year due to the cheaper availability of network components supporting network bonding. Right now I need the hostname feature to be able to run our cluster in a docker swarm environment. And this does not support static IPs. But the customer plans to use multiple different virtual networks over his docker swarm. So I donÄt know right now, how fast we need it. But sooner or later we will have a customer asking for it. And in general the I don't like implementing half of a solution (was already thinking about adding IPv6 support straight away as well). Hostnames resolving to multiple IPs is nothing new, but has been the case since the beginning of the IP protocol definition. And to my opinion dealing with this should be default implementation in all IP network base applications. |
Fair enough, thanks for explaining. I'll give a deeper look at the PR. |
8d3b93b
to
641de84
Compare
Fixed the requested typos/errors in comments. And applied a clang-format (except for struct comments) to align the formatting. |
Looks good overall. The main bit I'm not entirely convinced about is the differentiation between |
What you describe was the first attempt I tried to implement, but I ran into problems with the close/destruction order of some corner cases, when I tried to do this. Will try to describe what I observed. The root cause, if I manage the listeners totally dynamically the listeners array (even for a single listener) will be created after the successful response from the name resolving. In case the name resolving does not work we will end up without any listener tcp handle. But in this case we don't have any handle to close in the UvTcpListenClose and therefor don't trigger the uvTcpListenCloseCbListener. And therefore the uv_tcp_listen will not trigger the UvTcpMaybeFireCloseCb. This all work fine as long as there are outgoing connection, which where connected successfully. In this case the outgoing connections will trigger the UvTcpMaybeFireCloseCb and all structures will get removed. But without any outgoing connections the structures will not be freed. I tried to solve this by direct invocation of UvTcpMaybeFireCloseCb from the UvTcpListenClose in case the listener array is not created. But this will fail, if there are outgoing connections, because they expect the whole tcp object to exists until their close_cb functions are finished. More generally description: Any suggestion welcome how to solve this problem. So how do I trigger the UvTcpMaybeFireCloseCb from the tcp_listen part of the code without knowing, if there are outgoing connections or not. |
I see, thanks. I'll have to look more closely myself as well then, perhaps there's a way out. |
Ok, after reading the code I understand the situation better. I believe we should handle this ordering issue with the same pattern that we use throughout the code: queues (which is what libuv itself does, and basically most asynchronous programs, in way form or another). My suggestion would be
/* Hold information about a single listening socket. */
struct uvTcpListener
{
struct UvTcp *t; /* Transport implementation */
struct uv_tcp_s tcp; /* Listening TCP socket handle */
queue queue; /* Current queue (either listeners or aborting) */
};
void UvTcpListenClose(struct UvTcp *t)
{
queue *head;
assert(t->closing);
assert(!QUEUE_IS_EMPTY(&t->listeners));
while (!QUEUE_IS_EMPTY(&t->accepting)) {
struct uvTcpIncoming *incoming;
head = QUEUE_HEAD(&t->accepting);
incoming = QUEUE_DATA(head, struct uvTcpIncoming, queue);
uvTcpIncomingAbort(incoming);
}
while (!QUEUE_IS_EMPTY(&t->listeners)) {
struct uvTcpListener *listener;
head = QUEUE_HEAD(&t->listener);
listener = QUEUE_DATA(head, struct uvTcpListener, queue);
uvTcpListenerAbort(listener);
}
}
/* The listening TCP handle has been closed, release all memory
* associated with the listener object. */
static void uvTcpListenerCloseCb(struct uv_handle_s *handle)
{
struct uvTcpListener *listener = handle->data;
struct UvTcp *t = listener->t;
QUEUE_REMOVE(&listener->queue);
RaftHeapFree(listener);
UvTcpMaybeFireCloseCb(t);
}
/* Stop listening for incoming connection on the given listener. */
static void uvTcpListenerAbort(struct uvTcpListener *listener)
{
struct UvTcp *t = listener->t;
/* After uv_close() returns we are guaranteed that no more listen callback
* will be called. */
QUEUE_REMOVE(&listener->queue);
QUEUE_PUSH(&t->aborting, &listener->queue);
uv_close((struct uv_handle_s *)&listener->tcp, uvTcpListenerCloseCb);
}
void UvTcpMaybeFireCloseCb(struct UvTcp *t)
{
if (!t->closing) {
return;
}
assert(QUEUE_IS_EMPTY(&t->listeners));
assert(QUEUE_IS_EMPTY(&t->accepting));
assert(QUEUE_IS_EMPTY(&t->connecting));
if (!QUEUE_IS_EMPTY(&t->aborting)) {
return;
}
if (t->close_cb != NULL) {
t->close_cb(t->transport);
}
} I believe that should work neatly, unless I'm missing something. One important note to add is that in the case you mentioned, where the name resolving does not work for the listening address, then the I'm even not entirely sure why you raised the issue of the name resolving not working, because as I said in that case things are simple, it's just error propagation before the loop is even started, so no actual closing sequence is involved (and certainly there won't be any outgoing connections attempts ongoing). Hope it all makes sense. |
After reading your long comment about adding an additional queue I was not really sure, if this is really what we need. So I removed the default listener from the code, which made the code around the listener much more straight forward. To avoid the UvTcpMaybeFireCloseCb not being invoked at all I now invoke it directly from UvTcpListenClose in case there is no listener. Please take a look, if the PR maybe accepted this way. |
This approach looks good to me, I've left a few minor comments, I'll give it another pass later. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another round of comments.
added assertions back into UvTcpMaybeFireCloseCb
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added just one small nit.
Looks good to me, thanks for your work and for bearing with review feedback :)
@stgraber @MathieuBordere this can be merged as far as I'm concerned. |
@MathieuBordere LGTM, feel free to merge if happy with it |
Thanks a lot for your contribution! |
This is the second PR to finalize hostname support for the raft library. This will close #298