-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
Fix reuse of client port on Linux. Implement for OSX. #21818
Conversation
557352f
to
da8a991
Compare
base/socket.jl
Outdated
function TCPSocket() | ||
function TCPSocket(; delay=true) # kw arg "delay": if true, libuv delays creation of the socket | ||
# fd till the first bind call | ||
|
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.
could we avoid starting the function with an empty line? it's not the usual formatting style
da8a991
to
58e104e
Compare
base/distributed/cluster.jl
Outdated
@@ -153,8 +153,16 @@ function start_worker(out::IO, cookie::AbstractString) | |||
init_worker(cookie) | |||
interface = IPv4(LPROC.bind_addr) | |||
if LPROC.bind_port == 0 | |||
(actual_port,sock) = listenany(interface, UInt16(9009)) | |||
LPROC.bind_port = actual_port | |||
# (actual_port,sock) = listenany(interface, UInt16(9009)) |
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.
When testing this PR, i.e., reusing client port numbers, our current strategy for workers to start listening for a free port starting from 9009 caused a major slowdown in addprocs
when it was part of a repeated addprocs(x); run tests....; rmprocs(workers)
cycle - as the test script indeed does. I suspect it was because the new workers listened on the same ports (starting from 9009), but setup of new connections with the same client port were being delayed, as TCP connections to previous workers (now in a TIME_WAIT state) still existed. And new connections with the same (clienthostport,serverhostport) tuple had to wait till the system cleaned them up (OSX in my case).
I changed the worker listening port selection logic to select a free ephemeral port and the slowdown disappeared. While that works, I am uncomfortable using ephemeral ports for workers to listen on as firewalling policies may disallow incoming connections to the ephemeral port range.
However, the current strategy of listening on a free port starting from 9009 is also not ideal when we have many workers on a node. For example, the 100th worker in an addprocs(100)
will try and fail in listening on every port between 9009 and 9108 before succeeding to listen on 9109.
Looking for suggestions to work around this issue.
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.
Maybe we can suggest that someone who needs that will write a UPnP package that automates the firewall configuration step? Although I think that the typical usage of this cluster code is intended to be in a closed environment where all of the devices are on the same side of the firewall (whether virtually or physically).
base/distributed/managers.jl
Outdated
rc = ccall(:jl_tcp_reuseport, Int32, (Ptr{Void},), s.handle) | ||
if rc > 0 # SO_REUSEPORT is unsupported, just return the ephemerally bound socket | ||
return s | ||
elseif rc < 0 | ||
throw(SystemError("setsockopt() SO_REUSEPORT : ")) | ||
end | ||
getsockname(s) | ||
is_apple() && bind_client_port(s) | ||
catch e | ||
# This is an issue only on systems with lots of client connections, hence delay the warning | ||
nworkers() > 128 && warn_once("Error trying to reuse client port number, falling back to plain socket : ", e) |
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.
why is this done as a try/catch rather than printing this message where we threw it 6 lines above and then returning from there
base/socket.jl
Outdated
else | ||
err = ccall(:uv_tcp_init_ex, Cint, (Ptr{Void}, Ptr{Void}, Cuint), | ||
eventloop(), tcp.handle, 2) # AF_INET is 2 | ||
end |
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 think you can call the _ex
version unconditionally, and just select between AF_INET = 2
and AF_UNSPEC = 0
(that's what libuv will do internally anyways)
Looks like this will be a bit better after #21801 is merged too. |
f5ad929
to
c354dfd
Compare
base/distributed/cluster.jl
Outdated
LPROC.bind_port = port | ||
else | ||
close(sock) | ||
error("no ports available") |
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.
should throw a more specific exception type if possible
Added a NEWS entry. Will this fix make 0.6? Should it be in the 0.6 section? |
AV failure is unrelated. |
Will merge this in a day. |
66ea85a
to
fa8c4d2
Compare
This is failing tests on the buildbots. https://build.julialang.org/builders/build_ubuntu14.04-x64/builds/366/steps/shell_2/logs/stdio |
Should fix buildbot failure #21818 (comment) A bit of a mystery as to how Linux CI passed correctly.....
Closes #19893 .
ci skip
till #21799 is merged.This commit should backport cleanly. Will add another commit that uses
getsockname
everywhere once #21801 is closed.