str<<base<<"tcpgaveup" << ' '<< state->tcpGaveUp.load() << " " << now << "\r\n";
str<<base<<"tcpreadimeouts" << ' '<< state->tcpReadTimeouts.load() << " " << now << "\r\n";
str<<base<<"tcpwritetimeouts" << ' '<< state->tcpWriteTimeouts.load() << " " << now << "\r\n";
+ str<<base<<"tcpconnecttimeouts" << ' '<< state->tcpConnectTimeouts.load() << " " << now << "\r\n";
str<<base<<"tcpcurrentconnections" << ' '<< state->tcpCurrentConnections.load() << " " << now << "\r\n";
str<<base<<"tcpnewconnections" << ' '<< state->tcpNewConnections.load() << " " << now << "\r\n";
str<<base<<"tcpreusedconnections" << ' '<< state->tcpReusedConnections.load() << " " << now << "\r\n";
ret << endl;
ret << "Backends:" << endl;
- fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20d %-20d %-20f %-20f");
- ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Total connections" % "Reused connections" % "Avg queries/conn" % "Avg duration") << endl;
+ fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20d %-20d %20d %-20f %-20f");
+ ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Connect timeouts" % "Total connections" % "Reused connections" % "Avg queries/conn" % "Avg duration") << endl;
auto states = g_dstates.getLocal();
counter = 0;
for(const auto& s : *states) {
- ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpNewConnections % s->tcpReusedConnections % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
+ ret << (fmt % counter % s->getName() % s->remote.toStringWithPort() % s->tcpCurrentConnections % s->tcpDiedSendingQuery % s->tcpDiedReadingResponse % s->tcpGaveUp % s->tcpReadTimeouts % s->tcpWriteTimeouts % s->tcpConnectTimeouts % s->tcpNewConnections % s->tcpReusedConnections % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
++counter;
}
return iostate;
} else {
state->d_lastIOBlocked = true;
- return IOState::NeedWrite;
DEBUGLOG("partial write");
+ return iostate;
}
}
catch (const std::exception& e) {
return;
}
- state->d_readingFirstQuery = false;
++state->d_queriesCount;
++state->d_ci.cs->queries;
++g_stats.queries;
++state->d_ci.cs->tcpDiedSendingResponse;
}
- if (state->d_ioState->getState() == IOState::NeedWrite || state->d_readingFirstQuery) {
+ if (state->d_ioState->getState() == IOState::NeedWrite || state->d_queriesCount == 0) {
DEBUGLOG("Got an exception while handling TCP query: "<<e.what());
vinfolog("Got an exception while handling (%s) TCP query from %s: %s", (state->d_ioState->getState() == IOState::NeedRead ? "reading" : "writing"), state->d_ci.remote.toStringWithPort(), e.what());
}
delete citmp;
citmp = nullptr;
- /* let's update the remaining time */
- state->d_remainingTime = g_maxTCPConnectionDuration;
-
IncomingTCPConnectionState::handleIO(state, now);
}
catch(...) {
output << "# TYPE " << statesbase << "tcpdiedreadingresponse " << "counter" << "\n";
output << "# HELP " << statesbase << "tcpgaveup " << "The number of TCP connections failing after too many attempts" << "\n";
output << "# TYPE " << statesbase << "tcpgaveup " << "counter" << "\n";
+ output << "# HELP " << statesbase << "tcpconnecttimeouts " << "The number of TCP connect timeouts" << "\n";
+ output << "# TYPE " << statesbase << "tcpconnecttimeouts " << "counter" << "\n";
output << "# HELP " << statesbase << "tcpreadtimeouts " << "The number of TCP read timeouts" << "\n";
output << "# TYPE " << statesbase << "tcpreadtimeouts " << "counter" << "\n";
output << "# HELP " << statesbase << "tcpwritetimeouts " << "The number of TCP write timeouts" << "\n";
output << statesbase << "tcpgaveup" << label << " " << state->tcpGaveUp << "\n";
output << statesbase << "tcpreadtimeouts" << label << " " << state->tcpReadTimeouts << "\n";
output << statesbase << "tcpwritetimeouts" << label << " " << state->tcpWriteTimeouts << "\n";
+ output << statesbase << "tcpconnecttimeouts" << label << " " << state->tcpConnectTimeouts << "\n";
output << statesbase << "tcpcurrentconnections" << label << " " << state->tcpCurrentConnections << "\n";
output << statesbase << "tcpnewconnections" << label << " " << state->tcpNewConnections << "\n";
output << statesbase << "tcpreusedconnections" << label << " " << state->tcpReusedConnections << "\n";
{"tcpDiedSendingQuery", (double)a->tcpDiedSendingQuery},
{"tcpDiedReadingResponse", (double)a->tcpDiedReadingResponse},
{"tcpGaveUp", (double)a->tcpGaveUp},
+ {"tcpConnectTimeouts", (double)a->tcpConnectTimeouts},
{"tcpReadTimeouts", (double)a->tcpReadTimeouts},
{"tcpWriteTimeouts", (double)a->tcpWriteTimeouts},
{"tcpCurrentConnections", (double)a->tcpCurrentConnections},
stat_t tcpGaveUp{0};
stat_t tcpReadTimeouts{0};
stat_t tcpWriteTimeouts{0};
+ stat_t tcpConnectTimeouts{0};
stat_t tcpCurrentConnections{0};
stat_t tcpReusedConnections{0};
stat_t tcpNewConnections{0};
int order{1};
int weight{1};
int tcpConnectTimeout{5};
- int tcpRecvTimeout{30};
- int tcpSendTimeout{30};
+ int tcpRecvTimeout{5};
+ int tcpSendTimeout{5};
unsigned int checkInterval{1};
unsigned int lastCheck{0};
const unsigned int sourceItf{0};
conn->d_ioState->update(iostate, handleIOCallback, conn);
}
else {
- conn->d_ioState->update(iostate, handleIOCallback, conn, iostate == IOState::NeedRead ? conn->getBackendReadTTD(now) : conn->getBackendWriteTTD(now));
+ boost::optional<struct timeval> ttd{boost::none};
+ if (iostate == IOState::NeedRead) {
+ ttd = conn->getBackendReadTTD(now);
+ }
+ else if (conn->isFresh() && conn->d_queries == 0) {
+ /* first write just after the non-blocking connect */
+ ttd = conn->getBackendConnectTTD(now);
+ }
+ else {
+ ttd = conn->getBackendWriteTTD(now);
+ }
+
+ conn->d_ioState->update(iostate, handleIOCallback, conn, ttd);
}
}
}
}
socket->setNonBlocking();
+ gettimeofday(&d_connectionStartTime, nullptr);
auto handler = std::make_unique<TCPIOHandler>("", socket->releaseHandle(), 0, d_ds->d_tlsCtx, time(nullptr));
handler->tryConnect(d_ds->tcpFastOpen && isFastOpenEnabled(), d_ds->remote);
+ d_queries = 0;
d_handler = std::move(handler);
++d_ds->tcpCurrentConnections;
return true;
}
- catch(const std::runtime_error& e) {
+ catch (const std::runtime_error& e) {
vinfolog("Connection to downstream server %s failed: %s", d_ds->getName(), e.what());
d_downstreamFailures++;
if (d_downstreamFailures >= d_ds->retries) {
void TCPConnectionToBackend::handleTimeout(const struct timeval& now, bool write)
{
/* in some cases we could retry, here, reconnecting and sending our pending responses again */
- vinfolog("Timeout while %s TCP backend %s", (write ? "writing to" : "reading from"), d_ds->getName());
if (write) {
- ++d_ds->tcpWriteTimeouts;
+ if (isFresh() && d_queries == 0) {
+ ++d_ds->tcpConnectTimeouts;
+ vinfolog("Timeout while connecting to TCP backend %s", d_ds->getName());
+ }
+ else {
+ ++d_ds->tcpWriteTimeouts;
+ vinfolog("Timeout while writing to TCP backend %s", d_ds->getName());
+ }
}
else {
++d_ds->tcpReadTimeouts;
+ vinfolog("Timeout while reading from TCP backend %s", d_ds->getName());
}
try {
boost::optional<struct timeval> getBackendWriteTTD(const struct timeval& now) const
{
if (d_ds == nullptr) {
- throw std::runtime_error("getBackendReadTTD() called without any backend selected");
+ throw std::runtime_error("getBackendWriteTTD() called without any backend selected");
}
if (d_ds->tcpSendTimeout == 0) {
return boost::none;
return res;
}
+ boost::optional<struct timeval> getBackendConnectTTD(const struct timeval& now) const
+ {
+ if (d_ds == nullptr) {
+ throw std::runtime_error("getBackendConnectTTD() called without any backend selected");
+ }
+ if (d_ds->tcpConnectTimeout == 0) {
+ return boost::none;
+ }
+
+ struct timeval res = now;
+ res.tv_sec += d_ds->tcpConnectTimeout;
+
+ return res;
+ }
+
static const uint16_t s_xfrID;
PacketBuffer d_responseBuffer;
if (elapsed >= maxConnectionDuration) {
return true;
}
- d_remainingTime = maxConnectionDuration - elapsed;
}
return false;
size_t d_proxyProtocolNeed{0};
size_t d_queriesCount{0};
size_t d_currentQueriesCount{0};
- unsigned int d_remainingTime{0};
uint16_t d_querySize{0};
State d_state{State::doingHandshake};
- bool d_readingFirstQuery{true};
bool d_isXFR{false};
bool d_proxyProtocolPayloadHasTLV{false};
bool d_lastIOBlocked{false};
s_readBuffer.insert(s_readBuffer.end(), queries.at(2).begin(), queries.at(2).end());
/* make sure that the backend's timeout is shorter than the client's */
- backend->tcpSendTimeout = 1;
+ backend->tcpConnectTimeout = 1;
g_tcpRecvTimeout = 5;
bool timeout = false;
}
struct timeval later = now;
- later.tv_sec += backend->tcpSendTimeout + 1;
+ later.tv_sec += backend->tcpConnectTimeout + 1;
auto expiredConns = threadData.mplexer->getTimeouts(later, true);
BOOST_CHECK_EQUAL(expiredConns.size(), 1U);
for (const auto& cbData : expiredConns) {