XRootD
XrdThrottleManager Class Reference

#include <XrdThrottleManager.hh>

+ Collaboration diagram for XrdThrottleManager:

Public Member Functions

 XrdThrottleManager (XrdSysError *lP, XrdOucTrace *tP)
 
 ~XrdThrottleManager ()
 
void Apply (int reqsize, int reqops, int uid)
 
bool CheckLoadShed (const std::string &opaque)
 
bool CloseFile (const std::string &entity)
 
void FromConfig (XrdThrottle::Configuration &config)
 
std::tuple< std::string, uint16_t > GetUserInfo (const XrdSecEntity *client)
 
void Init ()
 
bool IsThrottling ()
 
bool OpenFile (const std::string &entity, std::string &open_error_message)
 
void PerformLoadShed (const std::string &opaque, std::string &host, unsigned &port)
 
void PrepLoadShed (const char *opaque, std::string &lsOpaque)
 
void SetLoadShed (std::string &hostname, unsigned port, unsigned frequency)
 
void SetMaxConns (unsigned long max_conns)
 
void SetMaxOpen (unsigned long max_open)
 
void SetMaxWait (unsigned long max_wait)
 
void SetMonitor (XrdXrootdGStream *gstream)
 
void SetThrottles (float reqbyterate, float reqoprate, int concurrency, float interval_length)
 
XrdThrottleTimer StartIOTimer (uint16_t uid, bool &ok)
 

Protected Member Functions

void StopIOTimer (std::chrono::steady_clock::duration &event_duration, uint16_t uid)
 

Friends

class XrdThrottleTimer
 

Detailed Description

Definition at line 53 of file XrdThrottleManager.hh.

Constructor & Destructor Documentation

◆ XrdThrottleManager()

XrdThrottleManager::XrdThrottleManager ( XrdSysError *  lP,
XrdOucTrace *  tP 
)

Definition at line 44 of file XrdThrottleManager.cc.

44  :
45  m_trace(tP),
46  m_log(lP),
47  m_interval_length_seconds(1.0),
48  m_bytes_per_second(-1),
49  m_ops_per_second(-1),
50  m_concurrency_limit(-1),
51  m_last_round_allocation(100*1024),
52  m_loadshed_host(""),
53  m_loadshed_port(0),
54  m_loadshed_frequency(0)
55 {
56  // Until the first recompute fills them in, no user is in the wake order.
57  std::fill(m_wake_order_0.begin(), m_wake_order_0.end(), -1);
58  std::fill(m_wake_order_1.begin(), m_wake_order_1.end(), -1);
59 }

◆ ~XrdThrottleManager()

XrdThrottleManager::~XrdThrottleManager ( )
inline

Definition at line 107 of file XrdThrottleManager.hh.

107 {} // The buffmanager is never deleted

Member Function Documentation

◆ Apply()

void XrdThrottleManager::Apply ( int  reqsize,
int  reqops,
int  uid 
)

Definition at line 325 of file XrdThrottleManager.cc.

326 {
327  if (m_bytes_per_second < 0)
328  reqsize = 0;
329  if (m_ops_per_second < 0)
330  reqops = 0;
331  while (reqsize || reqops)
332  {
333  // Subtract the requested out of the shares
334  AtomicBeg(m_compute_var);
335  GetShares(m_primary_bytes_shares[uid], reqsize);
336  if (reqsize)
337  {
338  TRACE(BANDWIDTH, "Using secondary shares; request has " << reqsize << " bytes left.");
339  GetShares(m_secondary_bytes_shares[uid], reqsize);
340  TRACE(BANDWIDTH, "Finished with secondary shares; request has " << reqsize << " bytes left.");
341  }
342  else
343  {
344  TRACE(BANDWIDTH, "Filled byte shares out of primary; " << m_primary_bytes_shares[uid] << " left.");
345  }
346  GetShares(m_primary_ops_shares[uid], reqops);
347  if (reqops)
348  {
349  GetShares(m_secondary_ops_shares[uid], reqops);
350  }
351  StealShares(uid, reqsize, reqops);
352  AtomicEnd(m_compute_var);
353 
354  if (reqsize || reqops)
355  {
356  if (reqsize) TRACE(BANDWIDTH, "Sleeping to wait for throttle fairshare.");
357  if (reqops) TRACE(IOPS, "Sleeping to wait for throttle fairshare.");
358  m_compute_var.Wait();
359  m_loadshed_limit_hit++;
360  }
361  }
362 
363 }
#define AtomicBeg(Mtx)
#define AtomicEnd(Mtx)
#define TRACE(act, x)
Definition: XrdTrace.hh:63

References AtomicBeg, AtomicEnd, TRACE, and XrdSysCondVar::Wait().

+ Here is the call graph for this function:

◆ CheckLoadShed()

bool XrdThrottleManager::CheckLoadShed ( const std::string &  opaque)

Definition at line 777 of file XrdThrottleManager.cc.

778 {
779  if (m_loadshed_port == 0)
780  {
781  return false;
782  }
783  if (m_loadshed_limit_hit == 0)
784  {
785  return false;
786  }
787  if (static_cast<unsigned>(rand()) % 100 > m_loadshed_frequency)
788  {
789  return false;
790  }
791  if (opaque.empty())
792  {
793  return false;
794  }
795  return true;
796 }

◆ CloseFile()

bool XrdThrottleManager::CloseFile ( const std::string &  entity)

Definition at line 259 of file XrdThrottleManager.cc.

260 {
261  if (m_max_open == 0 && m_max_conns == 0) return true;
262 
263  bool result = true;
264  const std::lock_guard<std::mutex> lock(m_file_mutex);
265  if (m_max_open) {
266  auto iter = m_file_counters.find(entity);
267  if (iter == m_file_counters.end()) {
268  TRACE(FILES, "WARNING: User " << entity << " closed a file but throttle plugin never saw an open file");
269  result = false;
270  } else if (iter->second == 0) {
271  TRACE(FILES, "WARNING: User " << entity << " closed a file but throttle plugin thinks all files were already closed");
272  result = false;
273  } else {
274  iter->second--;
275  }
276  if (result) TRACE(FILES, "User " << entity << " closed a file; " << iter->second <<
277  " remain open");
278  }
279 
280  if (m_max_conns) {
281  auto pid = XrdSysThread::Num();
282  auto conn_iter = m_active_conns.find(entity);
283  auto conn_count_iter = m_conn_counters.find(entity);
284  if (conn_iter == m_active_conns.end() || !(conn_iter->second)) {
285  TRACE(CONNS, "WARNING: User " << entity << " closed a file on a connection we are not"
286  " tracking");
287  return false;
288  }
289  auto pid_iter = conn_iter->second->find(pid);
290  if (pid_iter == conn_iter->second->end()) {
291  TRACE(CONNS, "WARNING: User " << entity << " closed a file on a connection we are not"
292  " tracking");
293  return false;
294  }
295  if (pid_iter->second == 0) {
296  TRACE(CONNS, "WARNING: User " << entity << " closed a file on connection the throttle"
297  " plugin thinks was idle");
298  } else {
299  pid_iter->second--;
300  }
301  if (conn_count_iter == m_conn_counters.end()) {
302  TRACE(CONNS, "WARNING: User " << entity << " closed a file but the throttle plugin never"
303  " observed an open file");
304  } else if (pid_iter->second == 0) {
305  if (conn_count_iter->second == 0) {
306  TRACE(CONNS, "WARNING: User " << entity << " had a connection go idle but the "
307  " throttle plugin already thought all connections were idle");
308  } else {
309  conn_count_iter->second--;
310  TRACE(CONNS, "User " << entity << " had connection on thread " << pid << " go idle; "
311  << conn_count_iter->second << " active connections remain");
312  }
313  }
314  }
315 
316  return result;
317 }
static unsigned long Num(void)

References XrdSysThread::Num(), and TRACE.

Referenced by XrdThrottle::File::close(), and XrdThrottle::File::open().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ FromConfig()

void XrdThrottleManager::FromConfig ( XrdThrottle::Configuration &  config)

Definition at line 62 of file XrdThrottleManager.cc.

63 {
64 
65  auto max_open = config.GetMaxOpen();
66  if (max_open != -1) SetMaxOpen(max_open);
67  auto max_conn = config.GetMaxConn();
68  if (max_conn != -1) SetMaxConns(max_conn);
69  auto max_wait = config.GetMaxWait();
70  if (max_wait != -1) SetMaxWait(max_wait);
71 
73  config.GetThrottleIOPSRate(),
74  config.GetThrottleConcurrency(),
75  static_cast<float>(config.GetThrottleRecomputeIntervalMS())/1000.0);
76 
77  m_trace->What = config.GetTraceLevels();
78 
79  auto loadshed_host = config.GetLoadshedHost();
80  auto loadshed_port = config.GetLoadshedPort();
81  auto loadshed_freq = config.GetLoadshedFreq();
82  if (!loadshed_host.empty() && loadshed_port > 0 && loadshed_freq > 0)
83  {
84  // Loadshed specified, so set it.
85  SetLoadShed(loadshed_host, loadshed_port, loadshed_freq);
86  }
87 }
void SetThrottles(float reqbyterate, float reqoprate, int concurrency, float interval_length)
void SetMaxOpen(unsigned long max_open)
void SetLoadShed(std::string &hostname, unsigned port, unsigned frequency)
void SetMaxWait(unsigned long max_wait)
void SetMaxConns(unsigned long max_conns)
long long GetLoadshedPort() const
long long GetThrottleDataRate() const
long long GetThrottleConcurrency() const
const std::string & GetLoadshedHost() const
long long GetLoadshedFreq() const
long long GetThrottleIOPSRate() const
long long GetThrottleRecomputeIntervalMS() const

References XrdThrottle::Configuration::GetLoadshedFreq(), XrdThrottle::Configuration::GetLoadshedHost(), XrdThrottle::Configuration::GetLoadshedPort(), XrdThrottle::Configuration::GetMaxConn(), XrdThrottle::Configuration::GetMaxOpen(), XrdThrottle::Configuration::GetMaxWait(), XrdThrottle::Configuration::GetThrottleConcurrency(), XrdThrottle::Configuration::GetThrottleDataRate(), XrdThrottle::Configuration::GetThrottleIOPSRate(), XrdThrottle::Configuration::GetThrottleRecomputeIntervalMS(), XrdThrottle::Configuration::GetTraceLevels(), SetLoadShed(), SetMaxConns(), SetMaxOpen(), SetMaxWait(), SetThrottles(), and XrdOucTrace::What.

+ Here is the call graph for this function:

◆ GetUserInfo()

std::tuple< std::string, uint16_t > XrdThrottleManager::GetUserInfo ( const XrdSecEntity *  client)

Definition at line 119 of file XrdThrottleManager.cc.

119  {
120  // client can be null, if so, return nobody
121  if (!client) {
122  return std::make_tuple("nobody", GetUid("nobody"));
123  }
124 
125  // Try various potential "names" associated with the request, from the most
126  // specific to most generic.
127  std::string user;
128 
129  if (client->eaAPI && client->eaAPI->Get("token.subject", user)) {
130  if (client->vorg) user = std::string(client->vorg) + ":" + user;
131  } else if (client->eaAPI) {
132  std::string request_name;
133  if (client->eaAPI->Get("request.name", request_name) && !request_name.empty()) user = request_name;
134  }
135  if (user.empty()) {user = client->name ? client->name : "nobody";}
136  uint16_t uid = GetUid(user.c_str());
137  return std::make_tuple(user, uid);
138 }
XrdSecAttr * Get(const void *sigkey)
char * vorg
Entity's virtual organization(s)
Definition: XrdSecEntity.hh:71
XrdSecEntityAttr * eaAPI
non-const API to attributes
Definition: XrdSecEntity.hh:92
char * name
Entity's name.
Definition: XrdSecEntity.hh:69

References XrdSecEntity::eaAPI, XrdSecEntityAttr::Get(), XrdSecEntity::name, and XrdSecEntity::vorg.

Referenced by XrdThrottle::File::open().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ Init()

void XrdThrottleManager::Init ( )

Definition at line 90 of file XrdThrottleManager.cc.

91 {
92  TRACE(DEBUG, "Initializing the throttle manager.");
93  // Initialize all our shares to zero.
94  m_primary_bytes_shares.resize(m_max_users);
95  m_secondary_bytes_shares.resize(m_max_users);
96  m_primary_ops_shares.resize(m_max_users);
97  m_secondary_ops_shares.resize(m_max_users);
98  for (auto & waiter : m_waiter_info) {
99  waiter.m_manager = this;
100  }
101 
102  // Allocate each user 100KB and 10 ops to bootstrap;
103  for (int i=0; i<m_max_users; i++)
104  {
105  m_primary_bytes_shares[i] = m_last_round_allocation;
106  m_secondary_bytes_shares[i] = 0;
107  m_primary_ops_shares[i] = 10;
108  m_secondary_ops_shares[i] = 0;
109  }
110 
111  int rc;
112  pthread_t tid;
113  if ((rc = XrdSysThread::Run(&tid, XrdThrottleManager::RecomputeBootstrap, static_cast<void *>(this), 0, "Buffer Manager throttle")))
114  m_log->Emsg("ThrottleManager", rc, "create throttle thread");
115 
116 }
#define DEBUG(x)
Definition: XrdBwmTrace.hh:54
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:95
static int Run(pthread_t *, void *(*proc)(void *), void *arg, int opts=0, const char *desc=0)

References DEBUG, XrdSysError::Emsg(), XrdSysThread::Run(), and TRACE.

+ Here is the call graph for this function:

◆ IsThrottling()

bool XrdThrottleManager::IsThrottling ( )
inline

Definition at line 69 of file XrdThrottleManager.hh.

69 {return (m_ops_per_second > 0) || (m_bytes_per_second > 0);}

◆ OpenFile()

bool XrdThrottleManager::OpenFile ( const std::string &  entity,
std::string &  open_error_message 
)

Definition at line 182 of file XrdThrottleManager.cc.

183 {
184  if (m_max_open == 0 && m_max_conns == 0) return true;
185 
186  const std::lock_guard<std::mutex> lock(m_file_mutex);
187  auto iter = m_file_counters.find(entity);
188  unsigned long cur_open_files = 0, cur_open_conns;
189  if (m_max_open) {
190  if (iter == m_file_counters.end()) {
191  m_file_counters[entity] = 1;
192  TRACE(FILES, "User " << entity << " has opened their first file");
193  cur_open_files = 1;
194  } else if (iter->second < m_max_open) {
195  iter->second++;
196  cur_open_files = iter->second;
197  } else {
198  std::stringstream ss;
199  ss << "User " << entity << " has hit the limit of " << m_max_open << " open files";
200  TRACE(FILES, ss.str());
201  error_message = ss.str();
202  return false;
203  }
204  }
205 
206  if (m_max_conns) {
207  auto pid = XrdSysThread::Num();
208  auto conn_iter = m_active_conns.find(entity);
209  auto conn_count_iter = m_conn_counters.find(entity);
210  if ((conn_count_iter != m_conn_counters.end()) && (conn_count_iter->second == m_max_conns) &&
211  (conn_iter == m_active_conns.end() || ((*(conn_iter->second))[pid] == 0)))
212  {
213  // note: we are rolling back the increment in open files
214  if (m_max_open) iter->second--;
215  std::stringstream ss;
216  ss << "User " << entity << " has hit the limit of " << m_max_conns <<
217  " open connections";
218  TRACE(CONNS, ss.str());
219  error_message = ss.str();
220  return false;
221  }
222  if (conn_iter == m_active_conns.end()) {
223  std::unique_ptr<std::unordered_map<pid_t, unsigned long>> conn_map(
224  new std::unordered_map<pid_t, unsigned long>());
225  (*conn_map)[pid] = 1;
226  m_active_conns[entity] = std::move(conn_map);
227  if (conn_count_iter == m_conn_counters.end()) {
228  m_conn_counters[entity] = 1;
229  cur_open_conns = 1;
230  } else {
231  m_conn_counters[entity] ++;
232  cur_open_conns = m_conn_counters[entity];
233  }
234  } else {
235  auto pid_iter = conn_iter->second->find(pid);
236  if (pid_iter == conn_iter->second->end() || pid_iter->second == 0) {
237  (*(conn_iter->second))[pid] = 1;
238  conn_count_iter->second++;
239  cur_open_conns = conn_count_iter->second;
240  } else {
241  (*(conn_iter->second))[pid] ++;
242  cur_open_conns = conn_count_iter->second;
243  }
244  }
245  TRACE(CONNS, "User " << entity << " has " << cur_open_conns << " open connections");
246  }
247  if (m_max_open) TRACE(FILES, "User " << entity << " has " << cur_open_files << " open files");
248  return true;
249 }

References XrdSysThread::Num(), and TRACE.

Referenced by XrdThrottle::File::open().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ PerformLoadShed()

void XrdThrottleManager::PerformLoadShed ( const std::string &  opaque,
std::string &  host,
unsigned &  port 
)

Definition at line 823 of file XrdThrottleManager.cc.

824 {
825  host = m_loadshed_host;
826  host += "?";
827  host += opaque;
828  port = m_loadshed_port;
829 }

◆ PrepLoadShed()

void XrdThrottleManager::PrepLoadShed ( const char *  opaque,
std::string &  lsOpaque 
)

Definition at line 799 of file XrdThrottleManager.cc.

800 {
801  if (m_loadshed_port == 0)
802  {
803  return;
804  }
805  if (opaque && opaque[0])
806  {
807  XrdOucEnv env(opaque);
808  // Do not load shed client if it has already been done once.
809  if (env.Get("throttle.shed") != 0)
810  {
811  return;
812  }
813  lsOpaque = opaque;
814  lsOpaque += "&throttle.shed=1";
815  }
816  else
817  {
818  lsOpaque = "throttle.shed=1";
819  }
820 }

References XrdOucEnv::Get().

Referenced by XrdThrottle::File::open().

+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ SetLoadShed()

void XrdThrottleManager::SetLoadShed ( std::string &  hostname,
unsigned  port,
unsigned  frequency 
)
inline

Definition at line 80 of file XrdThrottleManager.hh.

81  {m_loadshed_host = hostname; m_loadshed_port = port; m_loadshed_frequency = frequency;}

Referenced by FromConfig().

+ Here is the caller graph for this function:

◆ SetMaxConns()

void XrdThrottleManager::SetMaxConns ( unsigned long  max_conns)
inline

Definition at line 85 of file XrdThrottleManager.hh.

85 {m_max_conns = max_conns;}

Referenced by FromConfig().

+ Here is the caller graph for this function:

◆ SetMaxOpen()

void XrdThrottleManager::SetMaxOpen ( unsigned long  max_open)
inline

Definition at line 83 of file XrdThrottleManager.hh.

83 {m_max_open = max_open;}

Referenced by FromConfig().

+ Here is the caller graph for this function:

◆ SetMaxWait()

void XrdThrottleManager::SetMaxWait ( unsigned long  max_wait)
inline

Definition at line 87 of file XrdThrottleManager.hh.

87 {m_max_wait_time = std::chrono::seconds(max_wait);}

Referenced by FromConfig().

+ Here is the caller graph for this function:

◆ SetMonitor()

void XrdThrottleManager::SetMonitor ( XrdXrootdGStream *  gstream)
inline

Definition at line 89 of file XrdThrottleManager.hh.

89 {m_gstream = gstream;}

◆ SetThrottles()

void XrdThrottleManager::SetThrottles ( float  reqbyterate,
float  reqoprate,
int  concurrency,
float  interval_length 
)
inline

Definition at line 76 of file XrdThrottleManager.hh.

77  {m_interval_length_seconds = interval_length; m_bytes_per_second = reqbyterate;
78  m_ops_per_second = reqoprate; m_concurrency_limit = concurrency;}

Referenced by FromConfig().

+ Here is the caller graph for this function:

◆ StartIOTimer()

XrdThrottleTimer XrdThrottleManager::StartIOTimer ( uint16_t  uid,
bool &  ok 
)

Definition at line 711 of file XrdThrottleManager.cc.

712 {
713  int cur_counter = m_io_active.fetch_add(1, std::memory_order_acq_rel);
714  m_io_total++;
715 
716  while (m_concurrency_limit >= 0 && cur_counter >= m_concurrency_limit)
717  {
718  // If the user has essentially no concurrency, then we let them
719  // temporarily exceed the limit. This prevents potential waits for
720  // every single read for an infrequent user.
721  if (m_waiter_info[uid].m_concurrency < 1)
722  {
723  break;
724  }
725  m_loadshed_limit_hit++;
726  m_io_active.fetch_sub(1, std::memory_order_acq_rel);
727  TRACE(DEBUG, "ThrottleManager (user=" << uid << "): IO concurrency limit hit; waiting for other IOs to finish.");
728  ok = m_waiter_info[uid].Wait();
729  if (!ok) {
730  TRACE(DEBUG, "ThrottleManager (user=" << uid << "): timed out waiting for other IOs to finish.");
731  return XrdThrottleTimer();
732  }
733  cur_counter = m_io_active.fetch_add(1, std::memory_order_acq_rel);
734  }
735 
736  ok = true;
737  return XrdThrottleTimer(this, uid);
738 }
friend class XrdThrottleTimer

References DEBUG, TRACE, and XrdThrottleTimer.

◆ StopIOTimer()

void XrdThrottleManager::StopIOTimer ( std::chrono::steady_clock::duration &  event_duration,
uint16_t  uid 
)
protected

Definition at line 744 of file XrdThrottleManager.cc.

745 {
746  m_io_active_time += event_duration.count();
747  auto old_active = m_io_active.fetch_sub(1, std::memory_order_acq_rel);
748  m_waiter_info[uid].m_io_time += event_duration.count();
749  if (old_active == static_cast<unsigned>(m_concurrency_limit))
750  {
751  // If we are below the concurrency limit threshold and have another waiter
752  // for our user, then execute it immediately. Otherwise, we will give
753  // someone else a chance to run (as we have gotten more than our share recently).
754  unsigned waiting_users = m_waiting_users;
755  if (waiting_users == 0) waiting_users = 1;
756  if (m_waiter_info[uid].m_concurrency < m_concurrency_limit / waiting_users)
757  {
758  std::unique_lock<std::mutex> lock(m_waiter_info[uid].m_mutex);
759  if (m_waiter_info[uid].m_waiting > 0)
760  {
761  m_waiter_info[uid].NotifyOne(std::move(lock));
762  return;
763  }
764  }
765  NotifyOne();
766  }
767 }

Friends And Related Function Documentation

◆ XrdThrottleTimer

friend class XrdThrottleTimer
friend

Definition at line 56 of file XrdThrottleManager.hh.

Referenced by StartIOTimer().


The documentation for this class was generated from the following files: