Skip to content

Commit e3371ce

Browse files
Vrishal Kulkarnimeta-codesync[bot]
authored andcommitted
Keep the same ports across a restart
Summary: The sandbox took a fresh ephemeral port on every restart, so anything already pointed at it had to be repointed. `tao_sandbox_cli start` now binds a fixed block -- `mock_tao` 18400/18401, `mock_ucache` 18402/18403 -- and refuses to start if any of it is taken rather than quietly moving to a free port. Why that block, and the reserved ranges it steers around, is in `SandboxPorts.h`. Only `/tmp/tao_sandbox` claims the block. The flock is per output directory, so a `--mock` instance or a test fixture on its own directory would otherwise fight over it; those keep the ephemeral ports they take today. That is also how tests isolate: a custom output directory, not a port override. `--random-ports` is the single escape hatch, restoring the old behaviour wholesale. `SO_REUSEADDR` is set on the fixed sockets so `TIME_WAIT` from the previous instance does not block a rebind, while a live listener still fails. Without it every restart inside the `TIME_WAIT` window would fail to start. `tao_sandbox_cli start` now bails on a non-zero daemon exit with the tail of `tao_sandbox.err`; without it a port conflict spins for the full 300s timeout. Out of scope: `mock_ucache`'s thrift listener, `mock_prod`, and toaster mode's MySQL/gateway/writer/ucache/wormhole instances stay ephemeral. Reviewed By: ghostonhuang Differential Revision: D118218228 fbshipit-source-id: cde007cd942ee45bc66f78349916a0c43839bb58
1 parent 8f073d4 commit e3371ce

2 files changed

Lines changed: 48 additions & 9 deletions

File tree

mcrouter/lib/network/test/ListenSocket.cpp

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ namespace memcache {
2727

2828
std::pair<int, uint16_t> createAndBind(
2929
uint16_t port,
30-
bool zeroCopyEnable = false) {
30+
bool zeroCopyEnable = false,
31+
bool reuseAddr = false) {
3132
struct addrinfo hints;
3233
struct addrinfo* res;
3334

@@ -53,6 +54,14 @@ std::pair<int, uint16_t> createAndBind(
5354
port,
5455
folly::errnoStr(errno));
5556
}
57+
int reuse = 1;
58+
if (reuseAddr &&
59+
::setsockopt(socketFd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse))) {
60+
auto errStr = folly::errnoStr(errno);
61+
::close(socketFd);
62+
throwRuntime("Failed to set SO_REUSEADDR for port {}: {}", port, errStr);
63+
}
64+
5665
if (::bind(socketFd, res->ai_addr, res->ai_addrlen) != 0) {
5766
auto errStr = folly::errnoStr(errno);
5867
::close(socketFd);
@@ -78,18 +87,33 @@ std::pair<int, uint16_t> createAndBind(
7887
return std::make_pair(socketFd, ntohs(addr.sin_port));
7988
}
8089

81-
ListenSocket::ListenSocket(bool zeroCopyEnabled) {
82-
auto sockPort = createAndBind(0, zeroCopyEnabled);
83-
socketFd_ = sockPort.first;
84-
port_ = sockPort.second;
85-
if (::listen(socketFd_, SOMAXCONN) != 0) {
90+
namespace {
91+
std::pair<int, uint16_t>
92+
bindAndListen(uint16_t port, bool zeroCopyEnabled, bool reuseAddr) {
93+
auto sockPort = createAndBind(port, zeroCopyEnabled, reuseAddr);
94+
if (::listen(sockPort.first, SOMAXCONN) != 0) {
95+
auto errStr = folly::errnoStr(errno);
96+
::close(sockPort.first);
8697
throwRuntime(
8798
"Failed to listen on a socket for port {}: {}",
88-
port_,
89-
folly::errnoStr(errno));
99+
sockPort.second,
100+
errStr);
90101
}
91102

92-
VLOG(1) << "Listening on " << socketFd_ << ", port " << port_;
103+
VLOG(1) << "Listening on " << sockPort.first << ", port " << sockPort.second;
104+
return sockPort;
105+
}
106+
} // namespace
107+
108+
ListenSocket::ListenSocket(bool zeroCopyEnabled) {
109+
auto sockPort = bindAndListen(0, zeroCopyEnabled, /* reuseAddr */ false);
110+
socketFd_ = sockPort.first;
111+
port_ = sockPort.second;
112+
}
113+
114+
ListenSocket ListenSocket::createOnPort(uint16_t port, bool zeroCopyEnabled) {
115+
auto sockPort = bindAndListen(port, zeroCopyEnabled, /* reuseAddr */ true);
116+
return ListenSocket(sockPort.first, sockPort.second);
93117
}
94118

95119
void ListenSocket::setCloseOnExec(bool value) {

mcrouter/lib/network/test/ListenSocket.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,18 @@ class ListenSocket {
2323
explicit ListenSocket(bool zeroCopyEnabled = false);
2424
~ListenSocket();
2525

26+
/**
27+
* Listen on `port` instead of on an OS-assigned ephemeral one.
28+
*
29+
* SO_REUSEADDR is set, so a port that a previous process left with
30+
* connections in TIME_WAIT can be rebound immediately. That does not permit
31+
* a second live listener, so a port another process is listening on still
32+
* fails.
33+
*
34+
* @throws std::runtime_error if the port cannot be bound
35+
*/
36+
static ListenSocket createOnPort(uint16_t port, bool zeroCopyEnabled = false);
37+
2638
uint16_t getPort() const {
2739
return port_;
2840
}
@@ -61,6 +73,9 @@ class ListenSocket {
6173
ListenSocket& operator=(const ListenSocket&) = delete;
6274

6375
private:
76+
ListenSocket(int socketFd, uint16_t port)
77+
: socketFd_(socketFd), port_(port) {}
78+
6479
int socketFd_{-1};
6580
uint16_t port_{0};
6681
};

0 commit comments

Comments
 (0)