NFD: Named Data Networking Forwarding Daemon 24.07-28-gdcc0e6e0
Loading...
Searching...
No Matches
asf-probing-module.cpp
Go to the documentation of this file.
1/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
2/*
3 * Copyright (c) 2014-2025, Regents of the University of California,
4 * 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
27#include "algorithm.hpp"
28#include "common/global.hpp"
29#include "face/face.hpp"
30
31#include <ndn-cxx/util/random.hpp>
32
33namespace nfd::fw::asf {
34
35NFD_LOG_INIT(AsfProbingModule);
36
38
40 : m_probingInterval(DEFAULT_PROBING_INTERVAL)
41 , m_measurements(measurements)
42{
43}
44
45void
46ProbingModule::scheduleProbe(const fib::Entry& fibEntry, time::milliseconds interval)
47{
48 Name prefix = fibEntry.getPrefix();
49
50 // Set the probing flag for the namespace to true after passed interval of time
51 getScheduler().schedule(interval, [this, prefix] {
52 NamespaceInfo* info = m_measurements.getNamespaceInfo(prefix);
53 if (info == nullptr) {
54 // FIB entry with the passed prefix has been removed or
55 // it has a name that is not controlled by the AsfStrategy
56 }
57 else {
58 info->setIsProbingDue(true);
59 }
60 });
61}
62
63static auto
65{
66 // The RTT is used to store the status of the face:
67 // - A positive value indicates data was received and is assumed to indicate a working face (group 1),
68 // - RTT_NO_MEASUREMENT indicates a face is unmeasured (group 2),
69 // - RTT_TIMEOUT indicates a face is timed out (group 3).
70 // These groups are defined in the technical report.
71 //
72 // Unlike during forwarding, we adjust the ranking such that unmeasured faces (group 2)
73 // are prioritized before working faces (group 1), and working faces are prioritized
74 // before timed out faces (group 3). We assign each group a priority value from 1-3
75 // to ensure lowest-to-highest ordering consistent with this logic.
76 // Additionally, unmeasured faces will always be chosen to probe if they exist.
77
78 // Working faces are ranked second in priority; if RTT is not
79 // a special value, we assume the face to be in this group.
80 int priority = 2;
81 if (fs.rtt == FaceInfo::RTT_NO_MEASUREMENT) {
82 priority = 1;
83 }
84 else if (fs.rtt == FaceInfo::RTT_TIMEOUT) {
85 priority = 3;
86 }
87
88 // We set SRTT by default to the max value; if a face is working, we instead set it to the actual value.
89 // Unmeasured and timed out faces are not sorted by SRTT.
90 auto srtt = priority == 2 ? fs.srtt : time::nanoseconds::max();
91
92 // For ranking, group takes the priority over SRTT (if present) or cost, SRTT (if present)
93 // takes priority over cost, and cost takes priority over FaceId.
94 // FaceId is included to ensure all unique entries are included in the ranking (see #5310).
95 return std::tuple(priority, srtt, fs.cost, fs.face->getId());
96}
97
98bool
100{
102}
103
104static Face*
106{
107 static std::uniform_real_distribution<> randDist;
108 static auto& rng = ndn::random::getRandomNumberEngine();
109 const double randomNumber = randDist(rng);
110
111 const auto nFaces = rankedFaces.size();
112 const double rankSum = (nFaces + 1) * nFaces / 2;
113 size_t rank = 1;
114 double offset = 0.0;
115
116 for (const auto& faceStat : rankedFaces) {
117 // n + 1 - j
118 // p = ---------
119 // sum(ranks)
120 double probability = static_cast<double>(nFaces + 1 - rank) / rankSum;
121 rank++;
122
123 // Is the random number within the bounds of this face's probability + the previous faces'
124 // probability?
125 //
126 // e.g. (FaceId: 1, p=0.5), (FaceId: 2, p=0.33), (FaceId: 3, p=0.17)
127 // randomNumber = 0.92
128 //
129 // The face with FaceId: 3 should be picked
130 // (0.68 < 0.5 + 0.33 + 0.17) == true
131 //
132 offset += probability;
133 if (randomNumber <= offset) {
134 // Found face to probe
135 return faceStat.face;
136 }
137 }
138
139 // Given a set of Faces, this method should always select a Face to probe
140 NDN_CXX_UNREACHABLE;
141}
142
143Face*
144ProbingModule::getFaceToProbe(const Face& inFace, const Interest& interest,
145 const fib::Entry& fibEntry, const Face& faceUsed)
146{
147 FaceStatsProbingSet rankedFaces;
148
149 // Put eligible faces into rankedFaces. If one or more faces do not have an RTT measurement,
150 // the lowest ranked one will always be returned.
151 for (const auto& hop : fibEntry.getNextHops()) {
152 Face& hopFace = hop.getFace();
153
154 // Don't send probe Interest back to the incoming face or use the same face
155 // as the forwarded Interest or use a face that violates scope
156 if (hopFace.getId() == inFace.getId() || hopFace.getId() == faceUsed.getId() ||
157 wouldViolateScope(inFace, interest, hopFace)) {
158 continue;
159 }
160
161 FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest.getName(), hopFace.getId());
162 if (info == nullptr || info->getLastRtt() == FaceInfo::RTT_NO_MEASUREMENT) {
163 rankedFaces.insert({&hopFace, FaceInfo::RTT_NO_MEASUREMENT,
164 FaceInfo::RTT_NO_MEASUREMENT, hop.getCost()});
165 }
166 else {
167 rankedFaces.insert({&hopFace, info->getLastRtt(), info->getSrtt(), hop.getCost()});
168 }
169 }
170
171 if (ndn_cxx_getLogger().isLevelEnabled(ndn::util::LogLevel::TRACE)) {
172 NFD_LOG_TRACE("Current ranking of the faces for probing: " << interest.getName());
173 for (const FaceStats& probeRank : rankedFaces) {
174 NFD_LOG_TRACE(" Face: " << probeRank.face->getId() << ", " << probeRank.rtt << ", " << probeRank.srtt);
175 }
176 }
177
178 if (rankedFaces.empty()) {
179 // No Face to probe
180 return nullptr;
181 }
182
183 // If the top face is unmeasured, immediately return it.
184 if (rankedFaces.begin()->rtt == FaceInfo::RTT_NO_MEASUREMENT) {
185 return rankedFaces.begin()->face;
186 }
187
188 return chooseFace(rankedFaces);
189}
190
191bool
192ProbingModule::isProbingNeeded(const fib::Entry& fibEntry, const Name& interestName)
193{
194 // Return the probing status flag for a namespace
195 NamespaceInfo& info = m_measurements.getOrCreateNamespaceInfo(fibEntry, interestName);
196
197 // If a first probe has not been scheduled for a namespace
198 if (!info.isFirstProbeScheduled()) {
199 // Schedule first probe between 0 and 5 seconds
200 static std::uniform_int_distribution<> randDist(0, 5000);
201 static auto& rng = ndn::random::getRandomNumberEngine();
202 auto interval = randDist(rng);
203 scheduleProbe(fibEntry, time::milliseconds(interval));
204 info.setIsFirstProbeScheduled(true);
205 }
206
207 return info.isProbingDue();
208}
209
210void
211ProbingModule::afterForwardingProbe(const fib::Entry& fibEntry, const Name& interestName)
212{
213 // After probing is done, need to set probing flag to false and
214 // schedule another future probe
215 NamespaceInfo& info = m_measurements.getOrCreateNamespaceInfo(fibEntry, interestName);
216 info.setIsProbingDue(false);
217
218 scheduleProbe(fibEntry, m_probingInterval);
219}
220
221void
222ProbingModule::setProbingInterval(time::milliseconds probingInterval)
223{
224 if (probingInterval >= MIN_PROBING_INTERVAL) {
225 m_probingInterval = probingInterval;
226 }
227 else {
228 NDN_THROW(std::invalid_argument("Probing interval must be >= " +
229 std::to_string(MIN_PROBING_INTERVAL.count()) + " milliseconds"));
230 }
231}
232
233} // namespace nfd::fw::asf
This file contains common algorithms used by forwarding strategies.
Generalization of a network interface.
Definition face.hpp:118
FaceId getId() const noexcept
Returns the face ID.
Definition face.hpp:195
Represents an entry in the FIB.
Definition fib-entry.hpp:91
const Name & getPrefix() const noexcept
Definition fib-entry.hpp:97
const NextHopList & getNextHops() const noexcept
Helper class to retrieve and create strategy measurements.
NamespaceInfo & getOrCreateNamespaceInfo(const fib::Entry &fibEntry, const Name &prefix)
static constexpr time::milliseconds DEFAULT_MEASUREMENTS_LIFETIME
FaceInfo * getFaceInfo(const fib::Entry &fibEntry, const Name &interestName, FaceId faceId)
NamespaceInfo * getNamespaceInfo(const Name &prefix)
Strategy information for each face in a namespace.
static constexpr time::nanoseconds RTT_TIMEOUT
static constexpr time::nanoseconds RTT_NO_MEASUREMENT
time::nanoseconds getSrtt() const
time::nanoseconds getLastRtt() const
Stores strategy information about each face in this namespace.
void setIsFirstProbeScheduled(bool isScheduled)
void setIsProbingDue(bool isProbingDue)
Face * getFaceToProbe(const Face &inFace, const Interest &interest, const fib::Entry &fibEntry, const Face &faceUsed)
bool isProbingNeeded(const fib::Entry &fibEntry, const Name &interestName)
static constexpr time::milliseconds MIN_PROBING_INTERVAL
std::set< FaceStats, FaceStatsProbingCompare > FaceStatsProbingSet
void afterForwardingProbe(const fib::Entry &fibEntry, const Name &interestName)
void setProbingInterval(time::milliseconds probingInterval)
void scheduleProbe(const fib::Entry &fibEntry, time::milliseconds interval)
ProbingModule(AsfMeasurements &measurements)
static constexpr time::milliseconds DEFAULT_PROBING_INTERVAL
#define NFD_LOG_INIT(name)
Definition logger.hpp:31
#define NFD_LOG_TRACE
Definition logger.hpp:37
static Face * chooseFace(const ProbingModule::FaceStatsProbingSet &rankedFaces)
static auto getFaceRankForProbing(const FaceStats &fs) noexcept
bool wouldViolateScope(const Face &inFace, const Interest &interest, const Face &outFace)
Determine whether forwarding the Interest in pitEntry to outFace would violate scope.
Definition algorithm.cpp:33
ndn::Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition global.cpp:45
Container for ranking-related values.
bool operator()(const FaceStats &lhs, const FaceStats &rhs) const noexcept