str<<base<<"tcpreadimeouts" << ' '<< state->tcpReadTimeouts.load() << " " << now << "\r\n";
str<<base<<"tcpwritetimeouts" << ' '<< state->tcpWriteTimeouts.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";
str<<base<<"tcpavgqueriesperconnection" << ' '<< state->tcpAvgQueriesPerConnection.load() << " " << now << "\r\n";
str<<base<<"tcpavgconnectionduration" << ' '<< state->tcpAvgConnectionDuration.load() << " " << now << "\r\n";
}
{ "reloadAllCertificates", true, "", "reload all DNSCrypt and TLS certificates, along with their associated keys" },
{ "RemoteLogAction", true, "RemoteLogger [, alterFunction [, serverID]]", "send the content of this query to a remote logger via Protocol Buffer. `alterFunction` is a callback, receiving a DNSQuestion and a DNSDistProtoBufMessage, that can be used to modify the Protocol Buffer content, for example for anonymization purposes. `serverID` is the server identifier." },
{ "RemoteLogResponseAction", true, "RemoteLogger [,alterFunction [,includeCNAME [, serverID]]]", "send the content of this response to a remote logger via Protocol Buffer. `alterFunction` is the same callback than the one in `RemoteLogAction` and `includeCNAME` indicates whether CNAME records inside the response should be parsed and exported. The default is to only exports A and AAAA records. `serverID` is the server identifier." },
+ { "requestTCPStatesDump", true, "", "Request a dump of the TCP states (incoming connections, outgoing connections) during the next scan. Useful for debugging purposes only" },
{ "rmACL", true, "netmask", "remove netmask from ACL" },
{ "rmCacheHitResponseRule", true, "id", "remove cache hit response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" },
{ "rmResponseRule", true, "id", "remove response rule in position 'id', or whose uuid matches if 'id' is an UUID string, or finally whose name matches if 'id' is a string but not a valid UUID" },
ret << endl;
ret << "Backends:" << endl;
- fmt = boost::format("%-3d %-20.20s %-20.20s %-20d %-20d %-25d %-20d %-20d %-20d %-20f %-20f");
- ret << (fmt % "#" % "Name" % "Address" % "Connections" % "Died sending query" % "Died reading response" % "Gave up" % "Read timeouts" % "Write timeouts" % "Avg queries/conn" % "Avg duration") << 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;
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->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->tcpNewConnections % s->tcpReusedConnections % s->tcpAvgQueriesPerConnection % s->tcpAvgConnectionDuration) << endl;
++counter;
}
g_outputBuffer=ret.str();
});
+ luaCtx.writeFunction("requestTCPStatesDump", [] {
+ setLuaNoSideEffect();
+ extern std::atomic<uint64_t> g_tcpStatesDumpRequested;
+ g_tcpStatesDumpRequested += g_tcpclientthreads->getThreadsCount();
+ });
+
luaCtx.writeFunction("dumpStats", [] {
setLuaNoSideEffect();
vector<string> leftcolumn, rightcolumn;
int g_tcpRecvTimeout{2};
int g_tcpSendTimeout{2};
bool g_useTCPSinglePipe{false};
+std::atomic<uint64_t> g_tcpStatesDumpRequested{0};
class DownstreamConnectionsManager
{
result = std::move(list.front());
list.pop_front();
result->setReused();
+ ++ds->tcpReusedConnections;
return result;
}
}
DownstreamConnectionsManager::cleanupClosedTCPConnections();
lastTCPCleanup = now.tv_sec;
-#if 0
- /* just to keep things clean in the output, debug only */
- static std::mutex s_lock;
- std::lock_guard<decltype(s_lock)> lck(s_lock);
- data.mplexer->runForAllWatchedFDs([](bool isRead, int fd, const FDMultiplexer::funcparam_t& param, struct timeval ttd)
- {
- struct timeval lnow;
- gettimeofday(&lnow, nullptr);
- cerr<<"- "<<isRead<<" "<<fd<<": "<<" "<<(ttd.tv_sec-lnow.tv_sec)<<endl;
- if (param.type() == typeid(std::shared_ptr<IncomingTCPConnectionState>)) {
- auto state = boost::any_cast<std::shared_ptr<IncomingTCPConnectionState>>(param);
- cerr<<" - "<<state->toString()<<endl;
- }
- else if (param.type() == typeid(std::shared_ptr<TCPConnectionToBackend>)) {
- auto conn = boost::any_cast<std::shared_ptr<TCPConnectionToBackend>>(param);
- cerr<<" - "<<conn->toString()<<endl;
+ if (g_tcpStatesDumpRequested > 0) {
+ /* just to keep things clean in the output, debug only */
+ static std::mutex s_lock;
+ std::lock_guard<decltype(s_lock)> lck(s_lock);
+ if (g_tcpStatesDumpRequested > 0) {
+ /* no race here, we took the lock so it can only be increased in the meantime */
+ --g_tcpStatesDumpRequested;
+ errlog("Dumping the TCP states, as requested:");
+ data.mplexer->runForAllWatchedFDs([](bool isRead, int fd, const FDMultiplexer::funcparam_t& param, struct timeval ttd)
+ {
+ struct timeval lnow;
+ gettimeofday(&lnow, nullptr);
+ if (ttd.tv_sec > 0) {
+ errlog("- Descriptor %d is in %s state, TTD in %d", fd, (isRead ? "read" : "write"), (ttd.tv_sec-lnow.tv_sec));
+ }
+ else {
+ errlog("- Descriptor %d is in %s state, no TTD set", fd, (isRead ? "read" : "write"));
+ }
+
+ if (param.type() == typeid(std::shared_ptr<IncomingTCPConnectionState>)) {
+ auto state = boost::any_cast<std::shared_ptr<IncomingTCPConnectionState>>(param);
+ errlog(" - %s", state->toString());
+ }
+ else if (param.type() == typeid(std::shared_ptr<TCPConnectionToBackend>)) {
+ auto conn = boost::any_cast<std::shared_ptr<TCPConnectionToBackend>>(param);
+ errlog(" - %s", conn->toString());
+ }
+ else if (param.type() == typeid(TCPClientThreadData*)) {
+ errlog(" - Worker thread pipe");
+ }
+ });
}
- });
-#endif
+ }
}
if (now.tv_sec > lastTimeoutScan) {
output << "# TYPE " << statesbase << "tcpwritetimeouts " << "counter" << "\n";
output << "# HELP " << statesbase << "tcpcurrentconnections " << "The number of current TCP connections" << "\n";
output << "# TYPE " << statesbase << "tcpcurrentconnections " << "gauge" << "\n";
+ output << "# HELP " << statesbase << "tcpnewconnections " << "The number of established TCP connections in total" << "\n";
+ output << "# TYPE " << statesbase << "tcpnewconnections " << "counter" << "\n";
+ output << "# HELP " << statesbase << "tcpreusedsconnections " << "The number of times a TCP connection has been reused" << "\n";
+ output << "# TYPE " << statesbase << "tcpreusedsconnections " << "counter" << "\n";
output << "# HELP " << statesbase << "tcpavgqueriesperconn " << "The average number of queries per TCP connection" << "\n";
output << "# TYPE " << statesbase << "tcpavgqueriesperconn " << "gauge" << "\n";
output << "# HELP " << statesbase << "tcpavgconnduration " << "The average duration of a TCP connection (ms)" << "\n";
output << statesbase << "tcpreadtimeouts" << label << " " << state->tcpReadTimeouts << "\n";
output << statesbase << "tcpwritetimeouts" << label << " " << state->tcpWriteTimeouts << "\n";
output << statesbase << "tcpcurrentconnections" << label << " " << state->tcpCurrentConnections << "\n";
+ output << statesbase << "tcpnewconnections" << label << " " << state->tcpNewConnections << "\n";
+ output << statesbase << "tcpreusedconnections" << label << " " << state->tcpReusedConnections << "\n";
output << statesbase << "tcpavgqueriesperconn" << label << " " << state->tcpAvgQueriesPerConnection << "\n";
output << statesbase << "tcpavgconnduration" << label << " " << state->tcpAvgConnectionDuration << "\n";
}
{"tcpReadTimeouts", (double)a->tcpReadTimeouts},
{"tcpWriteTimeouts", (double)a->tcpWriteTimeouts},
{"tcpCurrentConnections", (double)a->tcpCurrentConnections},
+ {"tcpNewConnections", (double)a->tcpNewConnections},
+ {"tcpReusedConnections", (double)a->tcpReusedConnections},
{"tcpAvgQueriesPerConnection", (double)a->tcpAvgQueriesPerConnection},
{"tcpAvgConnectionDuration", (double)a->tcpAvgConnectionDuration},
{"dropRate", (double)a->dropRate}
stat_t tcpReadTimeouts{0};
stat_t tcpWriteTimeouts{0};
stat_t tcpCurrentConnections{0};
+ stat_t tcpReusedConnections{0};
+ stat_t tcpNewConnections{0};
pdns::stat_t_trait<double> tcpAvgQueriesPerConnection{0.0};
/* in ms */
pdns::stat_t_trait<double> tcpAvgConnectionDuration{0.0};
do {
vinfolog("TCP connecting to downstream %s (%d)", d_ds->getNameWithAddr(), d_downstreamFailures);
DEBUGLOG("Opening TCP connection to backend "<<d_ds->getNameWithAddr());
+ ++d_ds->tcpNewConnections;
try {
auto socket = std::make_unique<Socket>(d_ds->remote.sin4.sin_family, SOCK_STREAM, 0);
DEBUGLOG("result of socket() is "<<socket->getHandle());