diff --git a/daemon/fw/asf-measurements.hpp b/daemon/fw/asf-measurements.hpp index 48833151..10c081c1 100644 --- a/daemon/fw/asf-measurements.hpp +++ b/daemon/fw/asf-measurements.hpp @@ -35,8 +35,9 @@ namespace nfd::fw::asf { -/** \brief Strategy information for each face in a namespace -*/ +/** + * \brief Strategy information for each face in a namespace. + */ class FaceInfo { public: @@ -72,12 +73,6 @@ class FaceInfo cancelTimeout(interestName); } - bool - hasTimeout() const - { - return getLastRtt() == RTT_TIMEOUT; - } - time::nanoseconds getLastRtt() const { @@ -123,7 +118,8 @@ class FaceInfo //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/** \brief Stores strategy information about each face in this namespace +/** + * \brief Stores strategy information about each face in this namespace. */ class NamespaceInfo final : public StrategyInfo { @@ -183,7 +179,8 @@ class NamespaceInfo final : public StrategyInfo //////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////// -/** \brief Helper class to retrieve and create strategy measurements +/** + * \brief Helper class to retrieve and create strategy measurements. */ class AsfMeasurements : noncopyable { diff --git a/daemon/fw/asf-probing-module.cpp b/daemon/fw/asf-probing-module.cpp index fb07db24..d186d222 100644 --- a/daemon/fw/asf-probing-module.cpp +++ b/daemon/fw/asf-probing-module.cpp @@ -57,14 +57,94 @@ ProbingModule::scheduleProbe(const fib::Entry& fibEntry, time::milliseconds inte }); } +static auto +getFaceRankForProbing(const FaceStats& fs) noexcept +{ + // The RTT is used to store the status of the face: + // - A positive value indicates data was received and is assumed to indicate a working face (group 1), + // - RTT_NO_MEASUREMENT indicates a face is unmeasured (group 2), + // - RTT_TIMEOUT indicates a face is timed out (group 3). + // These groups are defined in the technical report. + // + // Unlike during forwarding, we adjust the ranking such that unmeasured faces (group 2) + // are prioritized before working faces (group 1), and working faces are prioritized + // before timed out faces (group 3). We assign each group a priority value from 1-3 + // to ensure lowest-to-highest ordering consistent with this logic. + // Additionally, unmeasured faces will always be chosen to probe if they exist. + + // Working faces are ranked second in priority; if RTT is not + // a special value, we assume the face to be in this group. + int priority = 2; + if (fs.rtt == FaceInfo::RTT_NO_MEASUREMENT) { + priority = 1; + } + else if (fs.rtt == FaceInfo::RTT_TIMEOUT) { + priority = 3; + } + + // We set SRTT by default to the max value; if a face is working, we instead set it to the actual value. + // Unmeasured and timed out faces are not sorted by SRTT. + auto srtt = priority == 2 ? fs.srtt : time::nanoseconds::max(); + + // For ranking, group takes the priority over SRTT (if present) or cost, SRTT (if present) + // takes priority over cost, and cost takes priority over FaceId. + // FaceId is included to ensure all unique entries are included in the ranking (see #5310). + return std::tuple(priority, srtt, fs.cost, fs.face->getId()); +} + +bool +ProbingModule::FaceStatsProbingCompare::operator()(const FaceStats& lhs, const FaceStats& rhs) const noexcept +{ + return getFaceRankForProbing(lhs) < getFaceRankForProbing(rhs); +} + +static Face* +chooseFace(const ProbingModule::FaceStatsProbingSet& rankedFaces) +{ + static std::uniform_real_distribution<> randDist; + static auto& rng = ndn::random::getRandomNumberEngine(); + const double randomNumber = randDist(rng); + + const auto nFaces = rankedFaces.size(); + const double rankSum = (nFaces + 1) * nFaces / 2; + size_t rank = 1; + double offset = 0.0; + + for (const auto& faceStat : rankedFaces) { + // n + 1 - j + // p = --------- + // sum(ranks) + double probability = static_cast(nFaces + 1 - rank) / rankSum; + rank++; + + // Is the random number within the bounds of this face's probability + the previous faces' + // probability? + // + // e.g. (FaceId: 1, p=0.5), (FaceId: 2, p=0.33), (FaceId: 3, p=0.17) + // randomNumber = 0.92 + // + // The face with FaceId: 3 should be picked + // (0.68 < 0.5 + 0.33 + 0.17) == true + // + offset += probability; + if (randomNumber <= offset) { + // Found face to probe + return faceStat.face; + } + } + + // Given a set of Faces, this method should always select a Face to probe + NDN_CXX_UNREACHABLE; +} + Face* ProbingModule::getFaceToProbe(const Face& inFace, const Interest& interest, const fib::Entry& fibEntry, const Face& faceUsed) { - FaceInfoFacePairSet rankedFaces; + FaceStatsProbingSet rankedFaces; - // Put eligible faces into rankedFaces. If a face does not have an RTT measurement, - // immediately pick the face for probing + // Put eligible faces into rankedFaces. If one or more faces do not have an RTT measurement, + // the lowest ranked one will always be returned. for (const auto& hop : fibEntry.getNextHops()) { Face& hopFace = hop.getFace(); @@ -76,13 +156,13 @@ ProbingModule::getFaceToProbe(const Face& inFace, const Interest& interest, } FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest.getName(), hopFace.getId()); - // If no RTT has been recorded, probe this face if (info == nullptr || info->getLastRtt() == FaceInfo::RTT_NO_MEASUREMENT) { - return &hopFace; + rankedFaces.insert({&hopFace, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, hop.getCost()}); + } + else { + rankedFaces.insert({&hopFace, info->getLastRtt(), info->getSrtt(), hop.getCost()}); } - - // Add FaceInfo to container sorted by RTT - rankedFaces.insert({info, &hopFace}); } if (rankedFaces.empty()) { @@ -90,6 +170,11 @@ ProbingModule::getFaceToProbe(const Face& inFace, const Interest& interest, return nullptr; } + // If the top face is unmeasured, immediately return it. + if (rankedFaces.begin()->rtt == FaceInfo::RTT_NO_MEASUREMENT) { + return rankedFaces.begin()->face; + } + return chooseFace(rankedFaces); } @@ -103,7 +188,8 @@ ProbingModule::isProbingNeeded(const fib::Entry& fibEntry, const Name& interestN if (!info.isFirstProbeScheduled()) { // Schedule first probe between 0 and 5 seconds static std::uniform_int_distribution<> randDist(0, 5000); - auto interval = randDist(ndn::random::getRandomNumberEngine()); + static auto& rng = ndn::random::getRandomNumberEngine(); + auto interval = randDist(rng); scheduleProbe(fibEntry, time::milliseconds(interval)); info.setIsFirstProbeScheduled(true); } @@ -122,48 +208,6 @@ ProbingModule::afterForwardingProbe(const fib::Entry& fibEntry, const Name& inte scheduleProbe(fibEntry, m_probingInterval); } -Face* -ProbingModule::chooseFace(const FaceInfoFacePairSet& rankedFaces) -{ - static std::uniform_real_distribution<> randDist; - double randomNumber = randDist(ndn::random::getRandomNumberEngine()); - uint64_t rankSum = (rankedFaces.size() + 1) * rankedFaces.size() / 2; - - uint64_t rank = 1; - double offset = 0.0; - - for (const auto& pair : rankedFaces) { - double probability = getProbingProbability(rank++, rankSum, rankedFaces.size()); - - // Is the random number within the bounds of this face's probability + the previous faces' - // probability? - // - // e.g. (FaceId: 1, p=0.5), (FaceId: 2, p=0.33), (FaceId: 3, p=0.17) - // randomNumber = 0.92 - // - // The face with FaceId: 3 should be picked - // (0.68 < 0.5 + 0.33 + 0.17) == true - // - if (randomNumber <= offset + probability) { - // Found face to probe - return pair.second; - } - offset += probability; - } - - // Given a set of Faces, this method should always select a Face to probe - NDN_CXX_UNREACHABLE; -} - -double -ProbingModule::getProbingProbability(uint64_t rank, uint64_t rankSum, uint64_t nFaces) -{ - // p = n + 1 - j ; n: # faces - // --------- - // sum(ranks) - return static_cast(nFaces + 1 - rank) / rankSum; -} - void ProbingModule::setProbingInterval(time::milliseconds probingInterval) { diff --git a/daemon/fw/asf-probing-module.hpp b/daemon/fw/asf-probing-module.hpp index 0d6b2127..173f21a3 100644 --- a/daemon/fw/asf-probing-module.hpp +++ b/daemon/fw/asf-probing-module.hpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2022, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -30,7 +30,19 @@ namespace nfd::fw::asf { -/** \brief ASF Probing Module +/** + * \brief Container for ranking-related values. + */ +struct FaceStats +{ + Face* face = nullptr; + time::nanoseconds rtt = 0_ns; + time::nanoseconds srtt = 0_ns; + uint64_t cost = 0; +}; + +/** + * \brief ASF Probing Module. */ class ProbingModule { @@ -60,35 +72,16 @@ class ProbingModule return m_probingInterval; } -private: - // Used to associate FaceInfo with the face in a NextHop - using FaceInfoFacePair = std::pair; +public: + static constexpr time::milliseconds DEFAULT_PROBING_INTERVAL = 1_min; + static constexpr time::milliseconds MIN_PROBING_INTERVAL = 1_s; - struct FaceInfoCompare + struct FaceStatsProbingCompare { bool - operator()(const FaceInfoFacePair& leftPair, const FaceInfoFacePair& rightPair) const - { - const FaceInfo& lhs = *leftPair.first; - const FaceInfo& rhs = *rightPair.first; - - // Sort by RTT: if a face has timed-out, rank it behind non-timed-out faces - return (!lhs.hasTimeout() && rhs.hasTimeout()) || - (lhs.hasTimeout() == rhs.hasTimeout() && lhs.getSrtt() < rhs.getSrtt()); - } + operator()(const FaceStats& lhs, const FaceStats& rhs) const noexcept; }; - - using FaceInfoFacePairSet = std::set; - - static Face* - chooseFace(const FaceInfoFacePairSet& rankedFaces); - - static double - getProbingProbability(uint64_t rank, uint64_t rankSum, uint64_t nFaces); - -public: - static constexpr time::milliseconds DEFAULT_PROBING_INTERVAL = 1_min; - static constexpr time::milliseconds MIN_PROBING_INTERVAL = 1_s; + using FaceStatsProbingSet = std::set; private: time::milliseconds m_probingInterval; diff --git a/daemon/fw/asf-strategy.cpp b/daemon/fw/asf-strategy.cpp index 2373c69e..24aa9d75 100644 --- a/daemon/fw/asf-strategy.cpp +++ b/daemon/fw/asf-strategy.cpp @@ -35,8 +35,6 @@ NFD_REGISTER_STRATEGY(AsfStrategy); AsfStrategy::AsfStrategy(Forwarder& forwarder, const Name& name) : Strategy(forwarder) , ProcessNackTraits(this) - , m_measurements(getMeasurements()) - , m_probing(m_measurements) { ParsedInstanceName parsed = parseInstanceName(name); if (parsed.version && *parsed.version != getStrategyName()[-1].toVersion()) { @@ -73,7 +71,7 @@ AsfStrategy::afterReceiveInterest(const Interest& interest, const FaceEndpoint& // Check if the interest is new and, if so, skip the retx suppression check if (!hasPendingOutRecords(*pitEntry)) { - auto* faceToUse = getBestFaceForForwarding(interest, ingress.face, fibEntry, pitEntry); + auto faceToUse = getBestFaceForForwarding(interest, ingress.face, fibEntry, pitEntry); if (faceToUse == nullptr) { NFD_LOG_INTEREST_FROM(interest, ingress, "new no-nexthop"); sendNoRouteNack(ingress.face, pitEntry); @@ -86,7 +84,7 @@ AsfStrategy::afterReceiveInterest(const Interest& interest, const FaceEndpoint& return; } - auto* faceToUse = getBestFaceForForwarding(interest, ingress.face, fibEntry, pitEntry, false); + auto faceToUse = getBestFaceForForwarding(interest, ingress.face, fibEntry, pitEntry, false); if (faceToUse != nullptr) { auto suppressResult = m_retxSuppression->decidePerUpstream(*pitEntry, *faceToUse); if (suppressResult == RetxSuppressionResult::SUPPRESS) { @@ -160,6 +158,7 @@ AsfStrategy::beforeSatisfyInterest(const Data& data, const FaceEndpoint& ingress // Extend PIT entry timer to allow slower probes to arrive this->setExpiryTimer(pitEntry, 50_ms); faceInfo->cancelTimeout(data.getName()); + faceInfo->setNTimeouts(0); } void @@ -218,50 +217,52 @@ AsfStrategy::sendProbe(const Interest& interest, const FaceEndpoint& ingress, co m_probing.afterForwardingProbe(fibEntry, interest.getName()); } -struct FaceStats +static auto +getFaceRankForForwarding(const FaceStats& fs) noexcept { - Face* face; - time::nanoseconds rtt; - time::nanoseconds srtt; - uint64_t cost; -}; - -struct FaceStatsCompare -{ - bool - operator()(const FaceStats& lhs, const FaceStats& rhs) const - { - time::nanoseconds lhsValue = getValueForSorting(lhs); - time::nanoseconds rhsValue = getValueForSorting(rhs); - - // Sort by RTT and then by cost - return std::tie(lhsValue, lhs.cost) < std::tie(rhsValue, rhs.cost); + // The RTT is used to store the status of the face: + // - A positive value indicates data was received and is assumed to indicate a working face (group 1), + // - RTT_NO_MEASUREMENT indicates a face is unmeasured (group 2), + // - RTT_TIMEOUT indicates a face is timed out (group 3). + // These groups are defined in the technical report. + // + // When forwarding, we assume an order where working faces (group 1) are ranked + // higher than unmeasured faces (group 2), and unmeasured faces are ranked higher + // than timed out faces (group 3). We assign each group a priority value from 1-3 + // to ensure lowest-to-highest ordering consistent with this logic. + + // Working faces are ranked first in priority; if RTT is not + // a special value, we assume the face to be in this group. + int priority = 1; + if (fs.rtt == FaceInfo::RTT_NO_MEASUREMENT) { + priority = 2; } - -private: - static time::nanoseconds - getValueForSorting(const FaceStats& stats) - { - // These values allow faces with no measurements to be ranked better than timeouts - // srtt < RTT_NO_MEASUREMENT < RTT_TIMEOUT - if (stats.rtt == FaceInfo::RTT_TIMEOUT) { - return time::nanoseconds::max(); - } - else if (stats.rtt == FaceInfo::RTT_NO_MEASUREMENT) { - return time::nanoseconds::max() / 2; - } - else { - return stats.srtt; - } + else if (fs.rtt == FaceInfo::RTT_TIMEOUT) { + priority = 3; } -}; + + // We set SRTT by default to the max value; if a face is working, we instead set it to the actual value. + // Unmeasured and timed out faces are not sorted by SRTT. + auto srtt = priority == 1 ? fs.srtt : time::nanoseconds::max(); + + // For ranking, group takes the priority over SRTT (if present) or cost, SRTT (if present) + // takes priority over cost, and cost takes priority over FaceId. + // FaceId is included to ensure all unique entries are included in the ranking (see #5310) + return std::tuple(priority, srtt, fs.cost, fs.face->getId()); +} + +bool +AsfStrategy::FaceStatsForwardingCompare::operator()(const FaceStats& lhs, const FaceStats& rhs) const noexcept +{ + return getFaceRankForForwarding(lhs) < getFaceRankForForwarding(rhs); +} Face* AsfStrategy::getBestFaceForForwarding(const Interest& interest, const Face& inFace, const fib::Entry& fibEntry, const shared_ptr& pitEntry, bool isInterestNew) { - std::set rankedFaces; + FaceStatsForwardingSet rankedFaces; auto now = time::steady_clock::now(); for (const auto& nh : fibEntry.getNextHops()) { @@ -311,6 +312,7 @@ AsfStrategy::onTimeoutOrNack(const Name& interestName, FaceId faceId, bool isNac else { NFD_LOG_TRACE(interestName << " face=" << faceId << " timeout-count=" << nTimeouts); faceInfo.recordTimeout(interestName); + faceInfo.setNTimeouts(0); } } diff --git a/daemon/fw/asf-strategy.hpp b/daemon/fw/asf-strategy.hpp index 4607d4e2..83bff131 100644 --- a/daemon/fw/asf-strategy.hpp +++ b/daemon/fw/asf-strategy.hpp @@ -86,11 +86,18 @@ class AsfStrategy : public Strategy, public ProcessNackTraits sendNoRouteNack(Face& face, const shared_ptr& pitEntry); private: - AsfMeasurements m_measurements; + AsfMeasurements m_measurements{getMeasurements()}; NFD_PUBLIC_WITH_TESTS_ELSE_PRIVATE: + struct FaceStatsForwardingCompare + { + bool + operator()(const FaceStats& lhs, const FaceStats& rhs) const noexcept; + }; + using FaceStatsForwardingSet = std::set; + std::unique_ptr m_retxSuppression; - ProbingModule m_probing; + ProbingModule m_probing{m_measurements}; size_t m_nMaxTimeouts = 3; friend ProcessNackTraits; diff --git a/tests/daemon/fw/asf-strategy.t.cpp b/tests/daemon/fw/asf-strategy.t.cpp index ff8ed50d..0fde3a54 100644 --- a/tests/daemon/fw/asf-strategy.t.cpp +++ b/tests/daemon/fw/asf-strategy.t.cpp @@ -1,6 +1,6 @@ /* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* - * Copyright (c) 2014-2023, Regents of the University of California, + * Copyright (c) 2014-2024, Regents of the University of California, * Arizona Board of Regents, * Colorado State University, * University Pierre & Marie Curie, Sorbonne University, @@ -25,12 +25,15 @@ #include "fw/asf-strategy.hpp" +#include "tests/daemon/face/dummy-face.hpp" + #include "strategy-tester.hpp" #include "topology-tester.hpp" namespace nfd::tests { using fw::AsfStrategy; +using fw::asf::FaceInfo; // The tester is unused in this file, but it's used in various templated test suites. using AsfStrategyTester = StrategyTester; @@ -584,6 +587,325 @@ BOOST_AUTO_TEST_CASE(Parameters) checkValidity("/max-timeouts~1~2", false); } +BOOST_AUTO_TEST_CASE(FaceRankingForForwarding) +{ + const Name PRODUCER_PREFIX = "/ndn/edu/nodeD/ping"; + AsfStrategy::FaceStatsForwardingSet rankedFaces; + + //Group 1- Working Measured Faces + FaceInfo group1_a(nullptr); + group1_a.recordRtt(25_ms); + DummyFace face1_a; + face1_a.setId(1); + rankedFaces.insert({&face1_a, group1_a.getLastRtt(), group1_a.getSrtt(), 0}); + // Higher FaceId + FaceInfo group1_b(nullptr); + group1_b.recordRtt(25_ms); + DummyFace face1_b; + face1_b.setId(2); + rankedFaces.insert({&face1_b, group1_b.getLastRtt(), group1_b.getSrtt(), 0}); + //Higher SRTT + FaceInfo group1_c(nullptr); + group1_c.recordRtt(30_ms); + DummyFace face1_c; + face1_c.setId(3); + rankedFaces.insert({&face1_c, group1_c.getLastRtt(), group1_c.getSrtt(), 0}); + //Higher SRTT/Cost + FaceInfo group1_d(nullptr); + group1_d.recordRtt(30_ms); + DummyFace face1_d; + face1_d.setId(4); + rankedFaces.insert({&face1_d, group1_d.getLastRtt(), group1_d.getSrtt(), 1}); + //Group 2- Unmeasured Faces + FaceInfo group2_a(nullptr); + DummyFace face2_a; + face2_a.setId(5); + rankedFaces.insert({&face2_a, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, 0}); + //Higher FaceId + FaceInfo group2_b(nullptr); + DummyFace face2_b; + face2_b.setId(6); + rankedFaces.insert({&face2_b, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, 0}); + //Higher Cost + FaceInfo group2_c(nullptr); + DummyFace face2_c; + face2_c.setId(7); + rankedFaces.insert({&face2_c, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, 1}); + //Group 3- Timeout Faces + //Lowest cost, high SRTT + FaceInfo group3_a(nullptr); + group3_a.recordRtt(30_ms); + group3_a.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_a; + face3_a.setId(8); + rankedFaces.insert({&face3_a, group3_a.getLastRtt(), group3_a.getSrtt(), 0}); + //Lowest cost, lower SRTT, higher FaceId + FaceInfo group3_b(nullptr); + group3_b.recordRtt(30_ms); + group3_b.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_b; + face3_b.setId(9); + rankedFaces.insert({&face3_b, group3_b.getLastRtt(), group3_b.getSrtt(), 0}); + //Lowest cost, higher SRTT, higher FaceId + FaceInfo group3_c(nullptr); + group3_c.recordRtt(45_ms); + group3_c.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_c; + face3_c.setId(10); + rankedFaces.insert({&face3_c, group3_c.getLastRtt(), group3_c.getSrtt(), 0}); + //Lowest cost, no SRTT, higher FaceId + FaceInfo group3_d(nullptr); + group3_d.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_d; + face3_d.setId(11); + rankedFaces.insert({&face3_d, group3_d.getLastRtt(), FaceInfo::RTT_NO_MEASUREMENT, 0}); + //Higher cost, lower SRTT, higher FaceId + FaceInfo group3_e(nullptr); + group3_e.recordRtt(15_ms); + group3_e.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_e; + face3_e.setId(12); + rankedFaces.insert({&face3_e, group3_e.getLastRtt(), group3_e.getSrtt(), 1}); + //Higher cost, higher SRTT, higher FaceId + FaceInfo group3_f(nullptr); + group3_f.recordRtt(45_ms); + group3_f.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_f; + face3_f.setId(13); + rankedFaces.insert({&face3_f, group3_f.getLastRtt(), group3_f.getSrtt(), 1}); + //Higher cost, no SRTT, higher FaceId + FaceInfo group3_g(nullptr); + group3_g.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_g; + face3_g.setId(14); + rankedFaces.insert({&face3_g, FaceInfo::RTT_TIMEOUT, + FaceInfo::RTT_NO_MEASUREMENT, 1}); + auto face = rankedFaces.begin(); + //Group 1 - Working Measured Faces + BOOST_CHECK_EQUAL(face->rtt, group1_a.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_a.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 1); + face++; + BOOST_CHECK_EQUAL(face->rtt, group1_b.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_b.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 2); + face++; + BOOST_CHECK_EQUAL(face->rtt, group1_c.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_c.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 3); + face++; + BOOST_CHECK_EQUAL(face->rtt, group1_d.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_d.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 4); + face++; + //Group 2 - Unmeasured Faces + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 5); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 6); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 7); + face++; + //Group 3 - Timeout + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_a.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 8); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_b.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 9); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_c.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 10); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 11); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_e.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 12); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_f.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 13); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 14); + // face++; +} + +BOOST_AUTO_TEST_CASE(FaceRankingForProbing) +{ + const Name PRODUCER_PREFIX = "/ndn/edu/nodeD/ping"; + fw::asf::ProbingModule::FaceStatsProbingSet rankedFaces; + + //Group 2- Unmeasured Faces + FaceInfo group2_a(nullptr); + DummyFace face2_a; + face2_a.setId(1); + rankedFaces.insert({&face2_a, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, 0}); + //Higher FaceId + FaceInfo group2_b(nullptr); + DummyFace face2_b; + face2_b.setId(2); + rankedFaces.insert({&face2_b, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, 0}); + //Higher Cost + FaceInfo group2_c(nullptr); + DummyFace face2_c; + face2_c.setId(3); + rankedFaces.insert({&face2_c, FaceInfo::RTT_NO_MEASUREMENT, + FaceInfo::RTT_NO_MEASUREMENT, 1}); + + //Group 1- Working Measured Faces + FaceInfo group1_a(nullptr); + group1_a.recordRtt(25_ms); + DummyFace face1_a; + face1_a.setId(4); + rankedFaces.insert({&face1_a, group1_a.getLastRtt(), group1_a.getSrtt(), 0}); + // Higher FaceId + FaceInfo group1_b(nullptr); + group1_b.recordRtt(25_ms); + DummyFace face1_b; + face1_b.setId(5); + rankedFaces.insert({&face1_b, group1_b.getLastRtt(), group1_b.getSrtt(), 0}); + //Higher SRTT + FaceInfo group1_c(nullptr); + group1_c.recordRtt(30_ms); + DummyFace face1_c; + face1_c.setId(6); + rankedFaces.insert({&face1_c, group1_c.getLastRtt(), group1_c.getSrtt(), 0}); + //Higher SRTT/Cost + FaceInfo group1_d(nullptr); + group1_d.recordRtt(30_ms); + DummyFace face1_d; + face1_d.setId(7); + rankedFaces.insert({&face1_d, group1_d.getLastRtt(), group1_d.getSrtt(), 1}); + + //Group 3- Timeout Faces + //Lowest cost, high SRTT + FaceInfo group3_a(nullptr); + group3_a.recordRtt(30_ms); + group3_a.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_a; + face3_a.setId(8); + rankedFaces.insert({&face3_a, group3_a.getLastRtt(), group3_a.getSrtt(), 0}); + //Lowest cost, lower SRTT, higher FaceId + FaceInfo group3_b(nullptr); + group3_b.recordRtt(30_ms); + group3_b.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_b; + face3_b.setId(9); + rankedFaces.insert({&face3_b, group3_b.getLastRtt(), group3_b.getSrtt(), 0}); + //Lowest cost, higher SRTT, higher FaceId + FaceInfo group3_c(nullptr); + group3_c.recordRtt(45_ms); + group3_c.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_c; + face3_c.setId(10); + rankedFaces.insert({&face3_c, group3_c.getLastRtt(), group3_c.getSrtt(), 0}); + //Lowest cost, no SRTT, higher FaceId + FaceInfo group3_d(nullptr); + group3_d.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_d; + face3_d.setId(11); + rankedFaces.insert({&face3_d, group3_d.getLastRtt(), FaceInfo::RTT_NO_MEASUREMENT, 0}); + //Higher cost, lower SRTT, higher FaceId + FaceInfo group3_e(nullptr); + group3_e.recordRtt(15_ms); + group3_e.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_e; + face3_e.setId(12); + rankedFaces.insert({&face3_e, group3_e.getLastRtt(), group3_e.getSrtt(), 1}); + //Higher cost, higher SRTT, higher FaceId + FaceInfo group3_f(nullptr); + group3_f.recordRtt(45_ms); + group3_f.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_f; + face3_f.setId(13); + rankedFaces.insert({&face3_f, group3_f.getLastRtt(), group3_f.getSrtt(), 1}); + //Higher cost, no SRTT, higher FaceId + FaceInfo group3_g(nullptr); + group3_g.recordTimeout(PRODUCER_PREFIX); + DummyFace face3_g; + face3_g.setId(14); + rankedFaces.insert({&face3_g, group3_g.getLastRtt(), + FaceInfo::RTT_NO_MEASUREMENT, 1}); + auto face = rankedFaces.begin(); + + //Group 2 - Unmeasured Faces + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 1); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 2); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 3); + face++; + //Group 1 - Working Measured Faces + BOOST_CHECK_EQUAL(face->rtt, group1_a.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_a.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 4); + face++; + BOOST_CHECK_EQUAL(face->rtt, group1_b.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_b.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 5); + face++; + BOOST_CHECK_EQUAL(face->rtt, group1_c.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_c.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 6); + face++; + BOOST_CHECK_EQUAL(face->rtt, group1_d.getLastRtt()); + BOOST_CHECK_EQUAL(face->srtt, group1_d.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 7); + face++; + //Group 3 - Timeout + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_a.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 8); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_b.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 9); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_c.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 10); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 11); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_e.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 12); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, group3_f.getSrtt()); + BOOST_CHECK_EQUAL(face->face->getId(), 13); + face++; + BOOST_CHECK_EQUAL(face->rtt, FaceInfo::RTT_TIMEOUT); + BOOST_CHECK_EQUAL(face->srtt, FaceInfo::RTT_NO_MEASUREMENT); + BOOST_CHECK_EQUAL(face->face->getId(), 14); + // face++; +} + BOOST_AUTO_TEST_SUITE_END() // TestAsfStrategy BOOST_AUTO_TEST_SUITE_END() // Fw