when we are looking for an existing TCP connection to a backend to
reuse, we routinely (every 60s by default) clean up existing
connections from the cache.
7b5f590ee72fecf54c0c40b24e98ba03a406af53 removes a connection
from the cache more aggressively when it has failed, but I did not
notice that the same function might be called from the cache cleaning
algorithm. It caused the cache cleanup function to call this function
which in turns tried to remove the connection from the same cache,
invalidating the iterator of the cache algorithm, and causing a crash
when the function returned.
if (backendIt->second.d_idles.size() >= s_maxIdleConnectionsPerDownstream) {
auto old = backendIt->second.d_idles.template get<SequencedTag>().back();
- old->release();
+ old->release(false);
backendIt->second.d_idles.template get<SequencedTag>().pop_back();
}
if (entry->isIdle() && entry->getLastDataReceivedTime() < idleCutOff) {
/* idle for too long */
- (*connIt)->release();
+ (*connIt)->release(false);
connIt = sidx.erase(connIt);
continue;
}
}
if (entry->isIdle()) {
- (*connIt)->release();
+ (*connIt)->release(false);
}
connIt = sidx.erase(connIt);
}
bool reachedMaxConcurrentQueries() const override;
bool reachedMaxStreamID() const override;
bool isIdle() const override;
- void release() override
+ void release(bool removeFromCache) override
{
+ (void)removeFromCache;
}
private:
}
}
-void TCPConnectionToBackend::release(){
+void TCPConnectionToBackend::release(bool removeFromCache)
+{
d_ds->outstanding -= d_pendingResponses.size();
d_pendingResponses.clear();
d_ioState.reset();
}
- auto shared = std::dynamic_pointer_cast<TCPConnectionToBackend>(shared_from_this());
- if (!willBeReusable(true)) {
+ if (removeFromCache && !willBeReusable(true)) {
+ auto shared = std::dynamic_pointer_cast<TCPConnectionToBackend>(shared_from_this());
/* remove ourselves from the connection cache, this might mean that our
reference count drops to zero after that, so we need to be careful */
t_downstreamTCPConnectionsManager.removeDownstreamConnection(shared);
catch (const std::exception& e) {
vinfolog("Got an exception while handling TCP response from %s (client is %s): %s", conn->d_ds ? conn->d_ds->getNameWithAddr() : "unknown", conn->d_currentQuery.d_query.d_idstate.origRemote.toStringWithPort(), e.what());
ioGuard.release();
- conn->release();
+ conn->release(true);
return;
}
}
vinfolog("Got exception while notifying a timeout");
}
- release();
+ release(true);
}
void TCPConnectionToBackend::notifyAllQueriesFailed(const struct timeval& now, FailureReason reason)
vinfolog("Got exception while notifying");
}
- release();
+ release(true);
}
IOState TCPConnectionToBackend::handleResponse(std::shared_ptr<TCPConnectionToBackend>& conn, const struct timeval& now)
virtual bool reachedMaxStreamID() const = 0;
virtual bool reachedMaxConcurrentQueries() const = 0;
virtual bool isIdle() const = 0;
- virtual void release() = 0;
+ virtual void release(bool removeFromCache) = 0;
virtual void stopIO()
{
}
void queueQuery(std::shared_ptr<TCPQuerySender>& sender, TCPQuery&& query) override;
void handleTimeout(const struct timeval& now, bool write) override;
- void release() override;
+ void release(bool removeFromCache) override;
std::string toString() const override
{
we don't care about the ones still waiting for responses */
for (auto& backend : d_ownedConnectionsToBackend) {
for (auto& conn : backend.second) {
- conn->release();
+ conn->release(true);
}
}
d_ownedConnectionsToBackend.clear();
for (auto it = list.begin(); it != list.end(); ++it) {
if (*it == response.d_connection) {
try {
- response.d_connection->release();
+ response.d_connection->release(true);
}
catch (const std::exception& e) {
vinfolog("Error releasing connection: %s", e.what());
{
}
- void release()
+ void release(bool removeFomCache)
{
}