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-2022, 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 
26 #include "asf-probing-module.hpp"
27 #include "algorithm.hpp"
28 #include "common/global.hpp"
29 
30 #include <ndn-cxx/util/random.hpp>
31 
32 namespace nfd::fw::asf {
33 
35 
37  : m_probingInterval(DEFAULT_PROBING_INTERVAL)
38  , m_measurements(measurements)
39 {
40 }
41 
42 void
43 ProbingModule::scheduleProbe(const fib::Entry& fibEntry, time::milliseconds interval)
44 {
45  Name prefix = fibEntry.getPrefix();
46 
47  // Set the probing flag for the namespace to true after passed interval of time
48  getScheduler().schedule(interval, [this, prefix] {
49  NamespaceInfo* info = m_measurements.getNamespaceInfo(prefix);
50  if (info == nullptr) {
51  // FIB entry with the passed prefix has been removed or
52  // it has a name that is not controlled by the AsfStrategy
53  }
54  else {
55  info->setIsProbingDue(true);
56  }
57  });
58 }
59 
60 Face*
61 ProbingModule::getFaceToProbe(const Face& inFace, const Interest& interest,
62  const fib::Entry& fibEntry, const Face& faceUsed)
63 {
64  FaceInfoFacePairSet rankedFaces;
65 
66  // Put eligible faces into rankedFaces. If a face does not have an RTT measurement,
67  // immediately pick the face for probing
68  for (const auto& hop : fibEntry.getNextHops()) {
69  Face& hopFace = hop.getFace();
70 
71  // Don't send probe Interest back to the incoming face or use the same face
72  // as the forwarded Interest or use a face that violates scope
73  if (hopFace.getId() == inFace.getId() || hopFace.getId() == faceUsed.getId() ||
74  wouldViolateScope(inFace, interest, hopFace)) {
75  continue;
76  }
77 
78  FaceInfo* info = m_measurements.getFaceInfo(fibEntry, interest.getName(), hopFace.getId());
79  // If no RTT has been recorded, probe this face
80  if (info == nullptr || info->getLastRtt() == FaceInfo::RTT_NO_MEASUREMENT) {
81  return &hopFace;
82  }
83 
84  // Add FaceInfo to container sorted by RTT
85  rankedFaces.insert({info, &hopFace});
86  }
87 
88  if (rankedFaces.empty()) {
89  // No Face to probe
90  return nullptr;
91  }
92 
93  return chooseFace(rankedFaces);
94 }
95 
96 bool
97 ProbingModule::isProbingNeeded(const fib::Entry& fibEntry, const Name& interestName)
98 {
99  // Return the probing status flag for a namespace
100  NamespaceInfo& info = m_measurements.getOrCreateNamespaceInfo(fibEntry, interestName);
101 
102  // If a first probe has not been scheduled for a namespace
103  if (!info.isFirstProbeScheduled()) {
104  // Schedule first probe between 0 and 5 seconds
105  static std::uniform_int_distribution<> randDist(0, 5000);
106  auto interval = randDist(ndn::random::getRandomNumberEngine());
107  scheduleProbe(fibEntry, time::milliseconds(interval));
108  info.setIsFirstProbeScheduled(true);
109  }
110 
111  return info.isProbingDue();
112 }
113 
114 void
115 ProbingModule::afterForwardingProbe(const fib::Entry& fibEntry, const Name& interestName)
116 {
117  // After probing is done, need to set probing flag to false and
118  // schedule another future probe
119  NamespaceInfo& info = m_measurements.getOrCreateNamespaceInfo(fibEntry, interestName);
120  info.setIsProbingDue(false);
121 
122  scheduleProbe(fibEntry, m_probingInterval);
123 }
124 
125 Face*
126 ProbingModule::chooseFace(const FaceInfoFacePairSet& rankedFaces)
127 {
128  static std::uniform_real_distribution<> randDist;
129  double randomNumber = randDist(ndn::random::getRandomNumberEngine());
130  uint64_t rankSum = (rankedFaces.size() + 1) * rankedFaces.size() / 2;
131 
132  uint64_t rank = 1;
133  double offset = 0.0;
134 
135  for (const auto& pair : rankedFaces) {
136  double probability = getProbingProbability(rank++, rankSum, rankedFaces.size());
137 
138  // Is the random number within the bounds of this face's probability + the previous faces'
139  // probability?
140  //
141  // e.g. (FaceId: 1, p=0.5), (FaceId: 2, p=0.33), (FaceId: 3, p=0.17)
142  // randomNumber = 0.92
143  //
144  // The face with FaceId: 3 should be picked
145  // (0.68 < 0.5 + 0.33 + 0.17) == true
146  //
147  if (randomNumber <= offset + probability) {
148  // Found face to probe
149  return pair.second;
150  }
151  offset += probability;
152  }
153 
154  // Given a set of Faces, this method should always select a Face to probe
155  NDN_CXX_UNREACHABLE;
156 }
157 
158 double
159 ProbingModule::getProbingProbability(uint64_t rank, uint64_t rankSum, uint64_t nFaces)
160 {
161  // p = n + 1 - j ; n: # faces
162  // ---------
163  // sum(ranks)
164  return static_cast<double>(nFaces + 1 - rank) / rankSum;
165 }
166 
167 void
168 ProbingModule::setProbingInterval(time::milliseconds probingInterval)
169 {
170  if (probingInterval >= MIN_PROBING_INTERVAL) {
171  m_probingInterval = probingInterval;
172  }
173  else {
174  NDN_THROW(std::invalid_argument("Probing interval must be >= " +
175  to_string(MIN_PROBING_INTERVAL.count()) + " milliseconds"));
176  }
177 }
178 
179 } // namespace nfd::fw::asf
This file contains common algorithms used by forwarding strategies.
Generalization of a network interface.
Definition: face.hpp:56
FaceId getId() const noexcept
Returns the face ID.
Definition: face.hpp:121
Represents an entry in the FIB.
Definition: fib-entry.hpp:54
const NextHopList & getNextHops() const
Definition: fib-entry.hpp:66
const Name & getPrefix() const
Definition: fib-entry.hpp:60
Helper class to retrieve and create strategy measurements.
NamespaceInfo & getOrCreateNamespaceInfo(const fib::Entry &fibEntry, const Name &prefix)
static constexpr time::microseconds 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_NO_MEASUREMENT
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
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
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:32
Scheduler & getScheduler()
Returns the global Scheduler instance for the calling thread.
Definition: global.cpp:45