Implementation¶
The main business logic of the rate limiting mechanism of the EOS MGM is implemented by the following C++ macro:
vi eos/mgm/Macros.hh
...
79 //------------------------------------------------------------------------------
80 //! Stall Macro
81 //------------------------------------------------------------------------------
82 #define MAYSTALL eos::mgm::InFlightRegistration tracker_helper(gOFS->mTracker); \
83 if (gOFS->IsStall) { \
84 XrdOucString stallmsg=""; \
85 int stalltime=0; \
86 if (gOFS->ShouldStall(__FUNCTION__,__AccessMode__, vid, stalltime, stallmsg)) { \
87 if (stalltime) { \
88 return gOFS->Stall(error,stalltime, stallmsg.c_str()); \
89 } else { \
90 return gOFS->Emsg("maystall", error, EPERM, stallmsg.c_str(), ""); \
91 } \
92 } else { \
93 if (!tracker_helper.IsOK()) { \
94 stallmsg="track request, stall the client 5 seconds"; \
95 stalltime = 5; \
96 return gOFS->Stall(error,stalltime, stallmsg.c_str()); \
97 } \
98 } \
99 }
This macro is called at strategic points throughout the EOS MGM code. For example the following code snippet calls the macro when a user has just opened a file:
vi eos/mgm/XrdMgmOfsFile.cc
...
186 /*----------------------------------------------------------------------------*/
187 int
188 XrdMgmOfsFile::open(eos::common::VirtualIdentity* invid,
189 const char* inpath,
190 XrdSfsFileOpenMode open_mode,
191 mode_t Mode,
192 const XrdSecEntity* client,
193 const char* ininfo)
...
349 if (ProcInterface::IsWriteAccess(path, pinfo.c_str())) {
350 SET_ACCESSMODE_W;
351 }
352
353 MAYSTALL;
354 MAYREDIRECT;
The MAYSTALL
macro calls the ShouldStall()
method. The following code
snippet shows that the method stalls the current operation if it finds any
rate:user:*
limit rule that has been exceeded. The method does not
take into account the type of the current operation. The length of the
stall is 5 seconds plus a random number of seconds between 0 and 6.
vi eos/mgm/XrdMgmOfs/ShouldStall.cc
...
31 bool
32 XrdMgmOfs::ShouldStall(const char* function,
33 int __AccessMode__,
34 eos::common::VirtualIdentity& vid,
35 int& stalltime, XrdOucString& stallmsg)
36 {
...
106 } else if (Access::gStallUserGroup) {
107 std::string usermatch = "rate:user:";
108 usermatch += vid.uid_string;
109 std::string groupmatch = "rate:group:";
110 groupmatch += vid.gid_string;
111 std::string userwildcardmatch = "rate:user:*";
...
115 for (it = Access::gStallRules.begin();
116 it != Access::gStallRules.end();
117 it++) {
118 std::string cmd = it->first.substr(it->first.rfind(":") + 1);
119 double cutoff = strtod(it->second.c_str(), 0) * 1.33;
120
121 if ((it->first.find(userwildcardmatch) == 0)) {
122 // catch all rule = global user rate cut
123 XrdSysMutexHelper statLock(gOFS->MgmStats.mMutex);
124
125 if (gOFS->MgmStats.StatAvgUid.count(cmd) &&
126 gOFS->MgmStats.StatAvgUid[cmd].count(vid.uid) &&
127 (gOFS->MgmStats.StatAvgUid[cmd][vid.uid].GetAvg5() > cutoff)) {
128 if (!stalltime) {
129 stalltime = 5;
130 }
131 smsg = Access::gStallComment[it->first];
132 }
133 } else if ((it->first.find(groupwildcardmatch) == 0)) {
...
171 if (stalltime) {
172 // add random offset between 0 and 5 to stalltime
173 int random_stall = rand() % 6;
174 stalltime += random_stall;
175
176 stallmsg = "Attention: you are currently hold in this instance and each"
177 " request is stalled for ";
178 stallmsg += (int) stalltime;
179 stallmsg += " seconds ... ";
180 stallmsg += smsg.c_str();
181 eos_static_info("info=\"stalling access to\" uid=%u gid=%u host=%s stall=%d",
182 vid.uid, vid.gid, vid.host.c_str(), stalltime);
183 gOFS->MgmStats.Add("Stall", vid.uid, vid.gid, 1);
184 return true;
185 }
186 } else {