//------------------------------------------------------------------------------
// File: EndpointDecider.cc
// Author: Georgios Bitzes - CERN
//------------------------------------------------------------------------------
/************************************************************************
* qclient - A simple redis C++ client with support for redirects *
* Copyright (C) 2019 CERN/Switzerland *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see .*
************************************************************************/
#include "EndpointDecider.hh"
#include "qclient/Status.hh"
#include "qclient/network/HostResolver.hh"
#include "qclient/Logger.hh"
#include
namespace qclient {
//----------------------------------------------------------------------------
// Constructor provided with cluster members as per configuration. We may
// contact different clusters when issued a redirection, however, outside of
// the original list.
//----------------------------------------------------------------------------
EndpointDecider::EndpointDecider(Logger *log, HostResolver *resolv, const Members &memb)
: logger(log), resolver(resolv), members(memb) {}
//------------------------------------------------------------------------------
// We were just notified of a redirection.
//------------------------------------------------------------------------------
void EndpointDecider::registerRedirection(const Endpoint &redir) {
resolvedEndpoints.clear();
redirection = redir;
}
//------------------------------------------------------------------------------
// The event loop needs to reconnect - which endpoint should we target?
//------------------------------------------------------------------------------
Endpoint EndpointDecider::getNext() {
resolvedEndpoints.clear();
if(!redirection.empty()) {
Endpoint retval = redirection;
redirection = {};
QCLIENT_LOG(logger, LogLevel::kInfo, "Received redirection to " << retval.toString());
return retval;
}
Endpoint retval = members.getEndpoints()[nextMember];
nextMember = (nextMember + 1) % members.size();
return retval;
}
//------------------------------------------------------------------------------
// Fetch one of the resolved endpoints, return true
//------------------------------------------------------------------------------
bool EndpointDecider::fetchServiceEndpoint(ServiceEndpoint &out) {
out = resolvedEndpoints.back();
resolvedEndpoints.pop_back();
return true;
}
//------------------------------------------------------------------------------
// Get next service endpoint. False means all DNS resolution attempts failed.
//------------------------------------------------------------------------------
bool EndpointDecider::getNextEndpoint(ServiceEndpoint &resolved) {
if(resolvedEndpoints.size() == 1 && nextMember == 0) {
fullCircle = true;
}
if(!resolvedEndpoints.empty()) {
return fetchServiceEndpoint(resolved);
}
for(size_t attempt = 0; attempt < members.size() + (!redirection.empty()); attempt++) {
Endpoint endpoint = getNext();
Status st;
resolvedEndpoints = resolver->resolve(endpoint.getHost(), endpoint.getPort(), st);
std::reverse(resolvedEndpoints.begin(), resolvedEndpoints.end());
if(!st.ok() || resolvedEndpoints.empty()) {
QCLIENT_LOG(logger, LogLevel::kWarn, "DNS resolution of " << endpoint.toString() << " failed: " << st.toString());
}
if(resolvedEndpoints.size() == 1 && nextMember == 0) {
fullCircle = true;
}
if(!resolvedEndpoints.empty()) {
return fetchServiceEndpoint(resolved);
}
}
//----------------------------------------------------------------------------
// All resolution requests for all members we know about failed!
//----------------------------------------------------------------------------
fullCircle = true;
QCLIENT_LOG(logger, LogLevel::kError, "Unable to resolve any endpoints, possible trouble with DNS");
return false;
}
//------------------------------------------------------------------------------
// Have we made a full circle yet? That is, have we tried all possible
// ServiceEndpoints at least once? Including possible redirects.
//------------------------------------------------------------------------------
bool EndpointDecider::madeFullCircle() const {
return fullCircle;
}
}