blob: d186d2220a3a26fc884f8e237e4a77a012032e90 [file] [log] [blame]
Vince Lehman8a4c29e2016-07-11 08:49:35 +00001/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -05002/*
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -05003 * Copyright (c) 2014-2024, Regents of the University of California,
Vince Lehman8a4c29e2016-07-11 08:49:35 +00004 * Arizona Board of Regents,
5 * Colorado State University,
6 * University Pierre & Marie Curie, Sorbonne University,
7 * Washington University in St. Louis,
8 * Beijing Institute of Technology,
9 * The University of Memphis.
10 *
11 * This file is part of NFD (Named Data Networking Forwarding Daemon).
12 * See AUTHORS.md for complete list of NFD authors and contributors.
13 *
14 * NFD is free software: you can redistribute it and/or modify it under the terms
15 * of the GNU General Public License as published by the Free Software Foundation,
16 * either version 3 of the License, or (at your option) any later version.
17 *
18 * NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
19 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
20 * PURPOSE. See the GNU General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License along with
23 * NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
24 */
25
26#include "asf-probing-module.hpp"
Ashlesh Gawande2a73f352016-12-01 15:37:03 +000027#include "algorithm.hpp"
Davide Pesavento2cae8ca2019-04-18 20:48:05 -040028#include "common/global.hpp"
Vince Lehman8a4c29e2016-07-11 08:49:35 +000029
Davide Pesaventob8bd5ee2019-02-03 22:53:40 -050030#include <ndn-cxx/util/random.hpp>
31
Davide Pesaventoe422f9e2022-06-03 01:30:23 -040032namespace nfd::fw::asf {
Vince Lehman8a4c29e2016-07-11 08:49:35 +000033
Davide Pesaventoa3a7a4e2022-05-29 16:06:22 -040034static_assert(ProbingModule::DEFAULT_PROBING_INTERVAL < AsfMeasurements::MEASUREMENTS_LIFETIME);
Vince Lehman8a4c29e2016-07-11 08:49:35 +000035
36ProbingModule::ProbingModule(AsfMeasurements& measurements)
37 : m_probingInterval(DEFAULT_PROBING_INTERVAL)
38 , m_measurements(measurements)
39{
40}
41
42void
Davide Pesaventoa6f637a2019-08-28 23:23:20 -040043ProbingModule::scheduleProbe(const fib::Entry& fibEntry, time::milliseconds interval)
Vince Lehman8a4c29e2016-07-11 08:49:35 +000044{
Junxiao Shifc021862016-08-25 21:51:18 +000045 Name prefix = fibEntry.getPrefix();
Vince Lehman8a4c29e2016-07-11 08:49:35 +000046
47 // Set the probing flag for the namespace to true after passed interval of time
Davide Pesavento3dade002019-03-19 11:29:56 -060048 getScheduler().schedule(interval, [this, prefix] {
Junxiao Shifc021862016-08-25 21:51:18 +000049 NamespaceInfo* info = m_measurements.getNamespaceInfo(prefix);
Vince Lehman8a4c29e2016-07-11 08:49:35 +000050 if (info == nullptr) {
Davide Pesaventoa6f637a2019-08-28 23:23:20 -040051 // FIB entry with the passed prefix has been removed or
52 // it has a name that is not controlled by the AsfStrategy
Vince Lehman8a4c29e2016-07-11 08:49:35 +000053 }
54 else {
55 info->setIsProbingDue(true);
56 }
57 });
58}
59
awlane5fdcbec2023-12-15 14:56:05 -060060static auto
61getFaceRankForProbing(const FaceStats& fs) noexcept
62{
63 // The RTT is used to store the status of the face:
64 // - A positive value indicates data was received and is assumed to indicate a working face (group 1),
65 // - RTT_NO_MEASUREMENT indicates a face is unmeasured (group 2),
66 // - RTT_TIMEOUT indicates a face is timed out (group 3).
67 // These groups are defined in the technical report.
68 //
69 // Unlike during forwarding, we adjust the ranking such that unmeasured faces (group 2)
70 // are prioritized before working faces (group 1), and working faces are prioritized
71 // before timed out faces (group 3). We assign each group a priority value from 1-3
72 // to ensure lowest-to-highest ordering consistent with this logic.
73 // Additionally, unmeasured faces will always be chosen to probe if they exist.
74
75 // Working faces are ranked second in priority; if RTT is not
76 // a special value, we assume the face to be in this group.
77 int priority = 2;
78 if (fs.rtt == FaceInfo::RTT_NO_MEASUREMENT) {
79 priority = 1;
80 }
81 else if (fs.rtt == FaceInfo::RTT_TIMEOUT) {
82 priority = 3;
83 }
84
85 // We set SRTT by default to the max value; if a face is working, we instead set it to the actual value.
86 // Unmeasured and timed out faces are not sorted by SRTT.
87 auto srtt = priority == 2 ? fs.srtt : time::nanoseconds::max();
88
89 // For ranking, group takes the priority over SRTT (if present) or cost, SRTT (if present)
90 // takes priority over cost, and cost takes priority over FaceId.
91 // FaceId is included to ensure all unique entries are included in the ranking (see #5310).
92 return std::tuple(priority, srtt, fs.cost, fs.face->getId());
93}
94
95bool
96ProbingModule::FaceStatsProbingCompare::operator()(const FaceStats& lhs, const FaceStats& rhs) const noexcept
97{
98 return getFaceRankForProbing(lhs) < getFaceRankForProbing(rhs);
99}
100
101static Face*
102chooseFace(const ProbingModule::FaceStatsProbingSet& rankedFaces)
103{
104 static std::uniform_real_distribution<> randDist;
105 static auto& rng = ndn::random::getRandomNumberEngine();
106 const double randomNumber = randDist(rng);
107
108 const auto nFaces = rankedFaces.size();
109 const double rankSum = (nFaces + 1) * nFaces / 2;
110 size_t rank = 1;
111 double offset = 0.0;
112
113 for (const auto& faceStat : rankedFaces) {
114 // n + 1 - j
115 // p = ---------
116 // sum(ranks)
117 double probability = static_cast<double>(nFaces + 1 - rank) / rankSum;
118 rank++;
119
120 // Is the random number within the bounds of this face's probability + the previous faces'
121 // probability?
122 //
123 // e.g. (FaceId: 1, p=0.5), (FaceId: 2, p=0.33), (FaceId: 3, p=0.17)
124 // randomNumber = 0.92
125 //
126 // The face with FaceId: 3 should be picked
127 // (0.68 < 0.5 + 0.33 + 0.17) == true
128 //
129 offset += probability;
130 if (randomNumber <= offset) {
131 // Found face to probe
132 return faceStat.face;
133 }
134 }
135
136 // Given a set of Faces, this method should always select a Face to probe
137 NDN_CXX_UNREACHABLE;
138}
139
Junxiao Shia6de4292016-07-12 02:08:10 +0000140Face*
Davide Pesavento3dade002019-03-19 11:29:56 -0600141ProbingModule::getFaceToProbe(const Face& inFace, const Interest& interest,
142 const fib::Entry& fibEntry, const Face& faceUsed)
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000143{
awlane5fdcbec2023-12-15 14:56:05 -0600144 FaceStatsProbingSet rankedFaces;
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000145
awlane5fdcbec2023-12-15 14:56:05 -0600146 // Put eligible faces into rankedFaces. If one or more faces do not have an RTT measurement,
147 // the lowest ranked one will always be returned.
Davide Pesavento3dade002019-03-19 11:29:56 -0600148 for (const auto& hop : fibEntry.getNextHops()) {
Junxiao Shia6de4292016-07-12 02:08:10 +0000149 Face& hopFace = hop.getFace();
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000150
151 // Don't send probe Interest back to the incoming face or use the same face
Ashlesh Gawande2a73f352016-12-01 15:37:03 +0000152 // as the forwarded Interest or use a face that violates scope
153 if (hopFace.getId() == inFace.getId() || hopFace.getId() == faceUsed.getId() ||
154 wouldViolateScope(inFace, interest, hopFace)) {
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000155 continue;
156 }
157
Saurab Dulal432be572021-01-26 12:09:29 -0600158 FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest.getName(), hopFace.getId());
Davide Pesaventoa6f637a2019-08-28 23:23:20 -0400159 if (info == nullptr || info->getLastRtt() == FaceInfo::RTT_NO_MEASUREMENT) {
awlane5fdcbec2023-12-15 14:56:05 -0600160 rankedFaces.insert({&hopFace, FaceInfo::RTT_NO_MEASUREMENT,
161 FaceInfo::RTT_NO_MEASUREMENT, hop.getCost()});
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000162 }
awlane5fdcbec2023-12-15 14:56:05 -0600163 else {
164 rankedFaces.insert({&hopFace, info->getLastRtt(), info->getSrtt(), hop.getCost()});
165 }
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000166 }
167
168 if (rankedFaces.empty()) {
169 // No Face to probe
170 return nullptr;
171 }
172
awlane5fdcbec2023-12-15 14:56:05 -0600173 // If the top face is unmeasured, immediately return it.
174 if (rankedFaces.begin()->rtt == FaceInfo::RTT_NO_MEASUREMENT) {
175 return rankedFaces.begin()->face;
176 }
177
Davide Pesaventoa6f637a2019-08-28 23:23:20 -0400178 return chooseFace(rankedFaces);
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000179}
180
181bool
Saurab Dulal432be572021-01-26 12:09:29 -0600182ProbingModule::isProbingNeeded(const fib::Entry& fibEntry, const Name& interestName)
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000183{
184 // Return the probing status flag for a namespace
Saurab Dulal432be572021-01-26 12:09:29 -0600185 NamespaceInfo& info = m_measurements.getOrCreateNamespaceInfo(fibEntry, interestName);
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000186
187 // If a first probe has not been scheduled for a namespace
188 if (!info.isFirstProbeScheduled()) {
189 // Schedule first probe between 0 and 5 seconds
Davide Pesaventoa6f637a2019-08-28 23:23:20 -0400190 static std::uniform_int_distribution<> randDist(0, 5000);
awlane5fdcbec2023-12-15 14:56:05 -0600191 static auto& rng = ndn::random::getRandomNumberEngine();
192 auto interval = randDist(rng);
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000193 scheduleProbe(fibEntry, time::milliseconds(interval));
Davide Pesaventoa6f637a2019-08-28 23:23:20 -0400194 info.setIsFirstProbeScheduled(true);
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000195 }
196
197 return info.isProbingDue();
198}
199
200void
Saurab Dulal432be572021-01-26 12:09:29 -0600201ProbingModule::afterForwardingProbe(const fib::Entry& fibEntry, const Name& interestName)
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000202{
203 // After probing is done, need to set probing flag to false and
204 // schedule another future probe
Saurab Dulal432be572021-01-26 12:09:29 -0600205 NamespaceInfo& info = m_measurements.getOrCreateNamespaceInfo(fibEntry, interestName);
Vince Lehman8a4c29e2016-07-11 08:49:35 +0000206 info.setIsProbingDue(false);
207
208 scheduleProbe(fibEntry, m_probingInterval);
209}
210
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500211void
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400212ProbingModule::setProbingInterval(time::milliseconds probingInterval)
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500213{
Ashlesh Gawande1ef93d02022-04-08 00:25:06 -0400214 if (probingInterval >= MIN_PROBING_INTERVAL) {
215 m_probingInterval = probingInterval;
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500216 }
217 else {
Davide Pesaventoa6f637a2019-08-28 23:23:20 -0400218 NDN_THROW(std::invalid_argument("Probing interval must be >= " +
Davide Pesavento2c9d2ca2024-01-27 16:36:51 -0500219 std::to_string(MIN_PROBING_INTERVAL.count()) + " milliseconds"));
Ashlesh Gawande92e4ea52017-07-19 11:38:12 -0500220 }
221}
222
Davide Pesaventoe422f9e2022-06-03 01:30:23 -0400223} // namespace nfd::fw::asf