{
debugs(17, 4, path);
assert(path);
- paths_.emplace(paths_.begin(), path);
+ // Cannot use pathsToSkip for a faster (reverse) search because there
+ // may be unavailable paths past pathsToSkip. We could remember
+ // the last extraction index, but, to completely avoid a linear search,
+ // extract*() methods should return the path index.
+ const auto found = std::find_if(paths_.begin(), paths_.end(),
+ [path](const ResolvedPeerPath &candidate) {
+ return candidate.connection == path; // (refcounted) pointer comparison
+ });
+ assert(found != paths_.end());
+ assert(!found->available);
+ found->available = true;
+ increaseAvailability();
+
+ // if we restored availability of a path that we used to skip, update
+ const auto pathsToTheLeft = static_cast<size_type>(found - paths_.begin());
+ if (pathsToTheLeft < pathsToSkip) {
+ pathsToSkip = pathsToTheLeft;
+ } else {
+ // *found was unavailable so pathsToSkip could not end at it
+ Must(pathsToTheLeft != pathsToSkip);
+ }
}
void
ResolvedPeers::addPath(const Comm::ConnectionPointer &path)
{
paths_.emplace_back(path);
+ Must(paths_.back().available); // no pathsToSkip updates are needed
+ increaseAvailability();
+}
+
+/// \returns the beginning iterator for any available-path search
+ResolvedPeers::Paths::iterator
+ResolvedPeers::start()
+{
+ Must(pathsToSkip <= paths_.size());
+ return paths_.begin() + pathsToSkip; // may return end()
+}
+
+/// finalizes the iterator part of the given preliminary find*() result
+ResolvedPeers::Finding
+ResolvedPeers::makeFinding(const Paths::iterator &path, bool foundOther)
+{
+ return std::make_pair((foundOther ? paths_.end() : path), foundOther);
+}
+
+/// \returns the first available same-peer same-family Finding or <end,...>
+ResolvedPeers::Finding
+ResolvedPeers::findPrime(const Comm::Connection ¤tPeer)
+{
+ const auto path = start();
+ const auto foundNextOrSpare = path != paths_.end() &&
+ (currentPeer.getPeer() != path->connection->getPeer() || // next peer
+ ConnectionFamily(currentPeer) != ConnectionFamily(*path->connection));
+ return makeFinding(path, foundNextOrSpare);
+}
+
+/// \returns the first available same-peer different-family Finding or <end,...>
+ResolvedPeers::Finding
+ResolvedPeers::findSpare(const Comm::Connection ¤tPeer)
+{
+ const auto primeFamily = ConnectionFamily(currentPeer);
+ const auto primePeer = currentPeer.getPeer();
+ const auto path = std::find_if(start(), paths_.end(),
+ [primeFamily, primePeer](const ResolvedPeerPath &candidate) {
+ if (!candidate.available)
+ return false;
+ if (primePeer != candidate.connection->getPeer())
+ return true; // found next peer
+ if (primeFamily != ConnectionFamily(*candidate.connection))
+ return true; // found spare
+ return false;
+ });
+ const auto foundNext = path != paths_.end() &&
+ primePeer != path->connection->getPeer();
+ return makeFinding(path, foundNext);
+}
+
+/// \returns the first available same-peer Finding or <end,...>
+ResolvedPeers::Finding
+ResolvedPeers::findPeer(const Comm::Connection ¤tPeer)
+{
+ const auto path = start();
+ const auto foundNext = path != paths_.end() &&
+ currentPeer.getPeer() != path->connection->getPeer();
+ return makeFinding(path, foundNext);
}
Comm::ConnectionPointer
ResolvedPeers::extractFront()
{
Must(!empty());
- return extractFound("first: ", paths_.begin());
+ return extractFound("first: ", start());
}
Comm::ConnectionPointer
ResolvedPeers::extractPrime(const Comm::Connection ¤tPeer)
{
- if (!empty()) {
- const auto peerToMatch = currentPeer.getPeer();
- const auto familyToMatch = ConnectionFamily(currentPeer);
- const auto &conn = paths_.front();
- if (conn->getPeer() == peerToMatch && familyToMatch == ConnectionFamily(*conn))
- return extractFound("same-peer same-family match: ", paths_.begin());
- }
+ const auto found = findPrime(currentPeer).first;
+ if (found != paths_.end())
+ return extractFound("same-peer same-family match: ", found);
debugs(17, 7, "no same-peer same-family paths");
return nullptr;
}
-/// If spare paths exist for currentPeer, returns the first spare path iterator.
-/// Otherwise, if there are paths for other peers, returns one of those.
-/// Otherwise, returns the end() iterator.
-Comm::ConnectionList::iterator
-ResolvedPeers::findSpareOrNextPeer(const Comm::Connection ¤tPeer)
-{
- const auto peerToMatch = currentPeer.getPeer();
- const auto familyToAvoid = ConnectionFamily(currentPeer);
- // Optimization: Also stop at the first mismatching peer because all
- // same-peer paths are grouped together.
- return std::find_if(paths_.begin(), paths_.end(),
- [peerToMatch, familyToAvoid](const Comm::ConnectionPointer &conn) {
- return peerToMatch != conn->getPeer() ||
- familyToAvoid != ConnectionFamily(*conn);
- });
-}
-
Comm::ConnectionPointer
ResolvedPeers::extractSpare(const Comm::Connection ¤tPeer)
{
- auto found = findSpareOrNextPeer(currentPeer);
- if (found != paths_.end() && currentPeer.getPeer() == (*found)->getPeer())
+ const auto found = findSpare(currentPeer).first;
+ if (found != paths_.end())
return extractFound("same-peer different-family match: ", found);
debugs(17, 7, "no same-peer different-family paths");
/// convenience method to finish a successful extract*() call
Comm::ConnectionPointer
-ResolvedPeers::extractFound(const char *description, const Comm::ConnectionList::iterator &found)
+ResolvedPeers::extractFound(const char *description, const Paths::iterator &found)
{
- const auto path = *found;
- paths_.erase(found);
- debugs(17, 7, description << path);
- return path;
+ auto &path = *found;
+ debugs(17, 7, description << path.connection);
+ assert(path.available);
+ path.available = false;
+ decreaseAvailability();
+
+ // if we extracted the left-most available path, find the next one
+ if (static_cast<size_type>(found - paths_.begin()) == pathsToSkip) {
+ while (++pathsToSkip < paths_.size() && !paths_[pathsToSkip].available) {}
+ }
+
+ return path.connection;
}
bool
ResolvedPeers::haveSpare(const Comm::Connection ¤tPeer)
{
- const auto found = findSpareOrNextPeer(currentPeer);
- return found != paths_.end() &&
- currentPeer.getPeer() == (*found)->getPeer();
+ return findSpare(currentPeer).first != paths_.end();
+}
+
+/// whether paths_ have no (and will have no) Xs for the current peer based on
+/// the given findX(current peer) result
+bool
+ResolvedPeers::doneWith(const Finding &findings) const
+{
+ if (findings.first != paths_.end())
+ return false; // not done because the caller found a viable path X
+
+ // The caller did not find any path X. If the caller found any "other"
+ // paths, then we are done with paths X. If there are no "other" paths,
+ // then destinationsFinalized is the answer.
+ return findings.second ? true : destinationsFinalized;
}
bool
ResolvedPeers::doneWithSpares(const Comm::Connection ¤tPeer)
{
- const auto found = findSpareOrNextPeer(currentPeer);
- if (found == paths_.end())
- return destinationsFinalized;
- return currentPeer.getPeer() != (*found)->getPeer();
+ return doneWith(findSpare(currentPeer));
}
bool
-ResolvedPeers::doneWithPrimes(const Comm::Connection ¤tPeer) const
+ResolvedPeers::doneWithPrimes(const Comm::Connection ¤tPeer)
{
- const auto first = paths_.begin();
- if (first == paths_.end())
- return destinationsFinalized;
- return currentPeer.getPeer() != (*first)->getPeer() ||
- ConnectionFamily(currentPeer) != ConnectionFamily(**first);
+ return doneWith(findPrime(currentPeer));
}
bool
-ResolvedPeers::doneWithPeer(const Comm::Connection ¤tPeer) const
+ResolvedPeers::doneWithPeer(const Comm::Connection ¤tPeer)
{
- const auto first = paths_.begin();
- if (first == paths_.end())
- return destinationsFinalized;
- return currentPeer.getPeer() != (*first)->getPeer();
+ return doneWith(findPeer(currentPeer));
}
int
return conn.remote.isIPv4() ? AF_INET : AF_INET6;
}
+/// increments the number of available paths
+void
+ResolvedPeers::increaseAvailability()
+{
+ ++availablePaths;
+ assert(availablePaths <= paths_.size());
+}
+
+/// decrements the number of available paths
+void
+ResolvedPeers::decreaseAvailability()
+{
+ assert(availablePaths > 0);
+ --availablePaths;
+}
+
std::ostream &
operator <<(std::ostream &os, const ResolvedPeers &peers)
{
#include "comm/forward.h"
#include <iosfwd>
+#include <utility>
+
+class ResolvedPeerPath
+{
+public:
+ explicit ResolvedPeerPath(const Comm::ConnectionPointer &conn) : connection(conn), available(true) {}
+
+ Comm::ConnectionPointer connection; ///< (the address of) a path
+ bool available; ///< whether this path may be used (i.e., has not been tried already)
+};
/// cache_peer and origin server addresses (a.k.a. paths)
/// selected and resolved by the peering code
MEMPROXY_CLASS(ResolvedPeers);
public:
+ // ResolvedPeerPaths in addPath() call order
+ typedef std::vector<ResolvedPeerPath> Paths;
+ using size_type = Paths::size_type;
typedef RefCount<ResolvedPeers> Pointer;
ResolvedPeers();
/// whether we lack any known candidate paths
- bool empty() const { return paths_.empty(); }
+ bool empty() const { return !availablePaths; }
/// add a candidate path to try after all the existing paths
void addPath(const Comm::ConnectionPointer &);
- /// add a candidate path to try before all the existing paths
+ /// re-inserts the previously extracted address into the same position
void retryPath(const Comm::ConnectionPointer &);
/// extracts and returns the first queued address
bool haveSpare(const Comm::Connection ¤tPeer);
/// whether extractPrime() returns and will continue to return nil
- bool doneWithPrimes(const Comm::Connection ¤tPeer) const;
+ bool doneWithPrimes(const Comm::Connection ¤tPeer);
/// whether extractSpare() returns and will continue to return nil
bool doneWithSpares(const Comm::Connection ¤tPeer);
/// whether doneWithPrimes() and doneWithSpares() are true for currentPeer
- bool doneWithPeer(const Comm::Connection ¤tPeer) const;
+ bool doneWithPeer(const Comm::Connection ¤tPeer);
/// the current number of candidate paths
- Comm::ConnectionList::size_type size() const { return paths_.size(); }
+ size_type size() const { return availablePaths; }
/// whether all of the available candidate paths received from DNS
bool destinationsFinalized = false;
bool notificationPending = false;
private:
+ /// A find*() result: An iterator of the found path (or paths_.end()) and
+ /// whether the "other" path was found instead.
+ typedef std::pair<Paths::iterator, bool> Finding;
+
/// The protocol family of the given path, AF_INET or AF_INET6
static int ConnectionFamily(const Comm::Connection &conn);
- Comm::ConnectionList::iterator findSpareOrNextPeer(const Comm::Connection ¤tPeer);
- Comm::ConnectionPointer extractFound(const char *description, const Comm::ConnectionList::iterator &found);
+ Paths::iterator start();
+ Finding findSpare(const Comm::Connection ¤tPeer);
+ Finding findPrime(const Comm::Connection ¤tPeer);
+ Finding findPeer(const Comm::Connection ¤tPeer);
+ Comm::ConnectionPointer extractFound(const char *description, const Paths::iterator &found);
+ Finding makeFinding(const Paths::iterator &found, bool foundOther);
+
+ bool doneWith(const Finding &findings) const;
+
+ void increaseAvailability();
+ void decreaseAvailability();
+
+ Paths paths_; ///< resolved addresses in (peer, family) order
+
+ /// the number of leading paths_ elements that are all currently unavailable
+ /// i.e. the size of the front paths_ segment comprised of unavailable items
+ /// i.e. the position of the first available path (or paths_.size())
+ size_type pathsToSkip = 0;
- Comm::ConnectionList paths_;
+ /// the total number of currently available elements in paths_
+ size_type availablePaths = 0;
};
/// summarized ResolvedPeers (for debugging)