XRootD
XrdThrottleConfig.cc
Go to the documentation of this file.
1 /******************************************************************************/
2 /* */
3 /* (c) 2025 by the Morgridge Institute for Research */
4 /* */
5 /* This file is part of the XRootD software suite. */
6 /* */
7 /* XRootD is free software: you can redistribute it and/or modify it under */
8 /* the terms of the GNU Lesser General Public License as published by the */
9 /* Free Software Foundation, either version 3 of the License, or (at your */
10 /* option) any later version. */
11 /* */
12 /* XRootD is distributed in the hope that it will be useful, but WITHOUT */
13 /* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
14 /* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
15 /* License for more details. */
16 /* */
17 /* You should have received a copy of the GNU Lesser General Public License */
18 /* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
19 /* COPYING (GPL license). If not, see <http://www.gnu.org/licenses/>. */
20 /* */
21 /* The copyright holder's institutional names and contributor's names may not */
22 /* be used to endorse or promote products derived from this software without */
23 /* specific prior written permission of the institution or contributor. */
24 /******************************************************************************/
25 
26 #include "XrdOuc/XrdOuca2x.hh"
27 #include "XrdOuc/XrdOucEnv.hh"
28 #include "XrdOuc/XrdOucStream.hh"
29 #include "XrdSys/XrdSysError.hh"
32 
33 #include <cstring>
34 #include <string>
35 #include <fcntl.h>
36 
37 using namespace XrdThrottle;
38 
39 #define TS_Xeq(key, func) NoGo = (strcmp(key, var) == 0) ? func(Config) : 0
40 int
41 Configuration::Configure(const std::string &config_file)
42 {
43  XrdOucEnv myEnv;
44  XrdOucStream Config(&m_log, getenv("XRDINSTANCE"), &myEnv, "(Throttle Config)> ");
45  int cfgFD;
46  if (config_file.empty()) {
47  m_log.Say("No filename specified.");
48  return 1;
49  }
50  if ((cfgFD = open(config_file.c_str(), O_RDONLY)) < 0) {
51  m_log.Emsg("Config", errno, "Unable to open configuration file", config_file.c_str());
52  return 1;
53  }
54  Config.Attach(cfgFD);
55  static const char *cvec[] = { "*** throttle (ofs) plugin config:", 0 };
56  Config.Capture(cvec);
57 
58  char *var, *val;
59  int NoGo = 0;
60  while( (var = Config.GetMyFirstWord()) )
61  {
62  if (!strcmp("throttle.fslib", var)) {
63  val = Config.GetWord();
64  if (!val || !val[0]) {m_log.Emsg("Config", "fslib not specified."); continue;}
65  m_fslib = val;
66  }
67  TS_Xeq("throttle.max_open_files", xmaxopen);
68  TS_Xeq("throttle.max_active_connections", xmaxconn);
69  TS_Xeq("throttle.throttle", xthrottle);
70  TS_Xeq("throttle.loadshed", xloadshed);
71  TS_Xeq("throttle.max_wait_time", xmaxwait);
72  TS_Xeq("throttle.trace", xtrace);
73  TS_Xeq("throttle.userconfig", xuserconfig);
74  if (NoGo)
75  {
76  m_log.Emsg("Config", "Throttle configuration failed.");
77  return 1;
78  }
79  }
80  return 0;
81 }
82 
83 /******************************************************************************/
84 /* x m a x o p e n */
85 /******************************************************************************/
86 
87 /* Function: xmaxopen
88 
89  Purpose: Parse the directive: throttle.max_open_files <limit>
90 
91  <limit> maximum number of open file handles for a unique entity.
92 
93  Output: 0 upon success or !0 upon failure.
94 */
95 int
96 Configuration::xmaxopen(XrdOucStream &Config)
97 {
98  auto val = Config.GetWord();
99  if (!val || val[0] == '\0')
100  {m_log.Emsg("Config", "Max open files not specified! Example usage: throttle.max_open_files 16000");}
101  long long max_open = -1;
102  if (XrdOuca2x::a2sz(m_log, "max open files value", val, &max_open, 1)) return 1;
103 
104  m_max_open = max_open;
105  return 0;
106 }
107 
108 
109 /******************************************************************************/
110 /* x m a x c o n n */
111 /******************************************************************************/
112 
113 /* Function: xmaxconn
114 
115  Purpose: Parse the directive: throttle.max_active_connections <limit>
116 
117  <limit> maximum number of connections with at least one open file for a given entity
118 
119  Output: 0 upon success or !0 upon failure.
120 */
121 int
122 Configuration::xmaxconn(XrdOucStream &Config)
123 {
124  auto val = Config.GetWord();
125  if (!val || val[0] == '\0')
126  {m_log.Emsg("Config", "Max active connections not specified! Example usage: throttle.max_active_connections 4000");}
127  long long max_conn = -1;
128  if (XrdOuca2x::a2sz(m_log, "max active connections value", val, &max_conn, 1)) return 1;
129 
130  m_max_conn = max_conn;
131  return 0;
132 }
133 
134 /******************************************************************************/
135 /* x m a x w a i t */
136 /******************************************************************************/
137 
138 /* Function: xmaxwait
139 
140  Purpose: Parse the directive: throttle.max_wait_time <limit>
141 
142  <limit> maximum wait time, in seconds, before an operation should fail
143 
144  If the directive is not provided, the default is 30 seconds.
145 
146  Output: 0 upon success or !0 upon failure.
147 */
148 int
149 Configuration::xmaxwait(XrdOucStream &Config)
150 {
151  auto val = Config.GetWord();
152  if (!val || val[0] == '\0')
153  {m_log.Emsg("Config", "Max waiting time not specified (must be in seconds)! Example usage: throttle.max_wait_time 20");}
154  long long max_wait = -1;
155  if (XrdOuca2x::a2sz(m_log, "max waiting time value", val, &max_wait, 1)) return 1;
156 
157  m_max_wait = max_wait;
158 
159  return 0;
160 }
161 
162 /******************************************************************************/
163 /* x t h r o t t l e */
164 /******************************************************************************/
165 
166 /* Function: xthrottle
167 
168  Purpose: To parse the directive: throttle [data <drate>] [iops <irate>] [concurrency <climit>] [interval <rint>]
169 
170  <drate> maximum bytes per second through the server.
171  <irate> maximum IOPS per second through the server.
172  <climit> maximum number of concurrent IO connections.
173  <rint> minimum interval in milliseconds between throttle re-computing.
174 
175  Output: 0 upon success or !0 upon failure.
176 */
177 int
178 Configuration::xthrottle(XrdOucStream &Config)
179 {
180  long long drate = -1, irate = -1, rint = 1000, climit = -1;
181  char *val;
182 
183  while ((val = Config.GetWord()))
184  {
185  if (strcmp("data", val) == 0)
186  {
187  if (!(val = Config.GetWord()))
188  {m_log.Emsg("Config", "data throttle limit not specified."); return 1;}
189  if (XrdOuca2x::a2sz(m_log,"data throttle value",val,&drate,1)) return 1;
190  }
191  else if (strcmp("iops", val) == 0)
192  {
193  if (!(val = Config.GetWord()))
194  {m_log.Emsg("Config", "IOPS throttle limit not specified."); return 1;}
195  if (XrdOuca2x::a2sz(m_log,"IOPS throttle value",val,&irate,1)) return 1;
196  }
197  else if (strcmp("rint", val) == 0)
198  {
199  if (!(val = Config.GetWord()))
200  {m_log.Emsg("Config", "recompute interval not specified (in ms)."); return 1;}
201  if (XrdOuca2x::a2sp(m_log,"recompute interval value (in ms)",val,&rint,10)) return 1;
202  }
203  else if (strcmp("concurrency", val) == 0)
204  {
205  if (!(val = Config.GetWord()))
206  {m_log.Emsg("Config", "Concurrency limit not specified."); return 1;}
207  if (XrdOuca2x::a2sz(m_log,"Concurrency limit value",val,&climit,1)) return 1;
208  }
209  else
210  {
211  m_log.Emsg("Config", "Warning - unknown throttle option specified", val, ".");
212  }
213  }
214 
215  m_throttle_data_rate = drate;
216  m_throttle_iops_rate = irate;
217  m_throttle_concurrency_limit = climit;
218  m_throttle_recompute_interval_ms = rint;
219 
220  return 0;
221 }
222 
223 /******************************************************************************/
224 /* x l o a d s h e d */
225 /******************************************************************************/
226 
227 /* Function: xloadshed
228 
229  Purpose: To parse the directive: loadshed host <hostname> [port <port>] [frequency <freq>]
230 
231  <hostname> hostname of server to shed load to. Required
232  <port> port of server to shed load to. Defaults to 1094
233  <freq> A value from 1 to 100 specifying how often to shed load
234  (1 = 1% chance; 100 = 100% chance; defaults to 10).
235 
236  Output: 0 upon success or !0 upon failure.
237 */
238 int Configuration::xloadshed(XrdOucStream &Config)
239 {
240  long long port = 0, freq = 0;
241  char *val;
242  std::string hostname;
243 
244  while ((val = Config.GetWord()))
245  {
246  if (strcmp("host", val) == 0)
247  {
248  if (!(val = Config.GetWord()))
249  {m_log.Emsg("Config", "loadshed hostname not specified."); return 1;}
250  hostname = val;
251  }
252  else if (strcmp("port", val) == 0)
253  {
254  if (!(val = Config.GetWord()))
255  {m_log.Emsg("Config", "Port number not specified."); return 1;}
256  if (XrdOuca2x::a2sz(m_log,"Port number",val,&port,1, 65536)) return 1;
257  }
258  else if (strcmp("frequency", val) == 0)
259  {
260  if (!(val = Config.GetWord()))
261  {m_log.Emsg("Config", "Loadshed frequency not specified."); return 1;}
262  if (XrdOuca2x::a2sz(m_log,"Loadshed frequency",val,&freq,1,100)) return 1;
263  }
264  else
265  {
266  m_log.Emsg("Config", "Warning - unknown loadshed option specified", val, ".");
267  }
268  }
269 
270  if (hostname.empty())
271  {
272  m_log.Emsg("Config", "must specify hostname for loadshed parameter.");
273  return 1;
274  }
275 
276  m_loadshed_freq = freq;
277  m_loadshed_hostname = hostname;
278  m_loadshed_port = port;
279 
280  return 0;
281 }
282 
283 /******************************************************************************/
284 /* x t r a c e */
285 /******************************************************************************/
286 
287 /* Function: xtrace
288 
289  Purpose: To parse the directive: trace <events>
290 
291  <events> the blank separated list of events to trace. Trace
292  directives are cummalative.
293 
294  Output: 0 upon success or 1 upon failure.
295 */
296 
297 int Configuration::xtrace(XrdOucStream &Config)
298 {
299  char *val;
300  static const struct traceopts {const char *opname; int opval;} tropts[] =
301  {
302  {"all", TRACE_ALL},
303  {"off", TRACE_NONE},
304  {"none", TRACE_NONE},
305  {"debug", TRACE_DEBUG},
306  {"iops", TRACE_IOPS},
307  {"bandwidth", TRACE_BANDWIDTH},
308  {"ioload", TRACE_IOLOAD},
309  {"files", TRACE_FILES},
310  {"connections",TRACE_CONNS},
311  };
312  int i, neg, trval = 0, numopts = sizeof(tropts)/sizeof(struct traceopts);
313 
314  if (!(val = Config.GetWord()))
315  {
316  m_log.Emsg("Config", "trace option not specified");
317  return 1;
318  }
319  while (val)
320  {
321  if (!strcmp(val, "off"))
322  {
323  trval = 0;
324  }
325  else
326  {
327  if ((neg = (val[0] == '-' && val[1])))
328  {
329  val++;
330  }
331  for (i = 0; i < numopts; i++)
332  {
333  if (!strcmp(val, tropts[i].opname))
334  {
335  if (neg)
336  {
337  if (tropts[i].opval) trval &= ~tropts[i].opval;
338  else trval = TRACE_ALL;
339  }
340  else if (tropts[i].opval) trval |= tropts[i].opval;
341  else trval = TRACE_NONE;
342  break;
343  }
344  }
345  if (i >= numopts)
346  {
347  m_log.Say("Config warning: ignoring invalid trace option '", val, "'.");
348  }
349  }
350  val = Config.GetWord();
351  }
352  m_trace_levels = trval;
353  return 0;
354 }
355 
356 /******************************************************************************/
357 /* x u s e r c o n f i g */
358 /******************************************************************************/
359 
360 /* Function: xuserconfig
361 
362  Purpose: Parse the directive: throttle.userconfig <filepath>
363 
364  <filepath> path to the per-user configuration file in INI format
365 
366  Output: 0 upon success or !0 upon failure.
367 */
368 int Configuration::xuserconfig(XrdOucStream &Config)
369 {
370  auto val = Config.GetWord();
371  if (!val || val[0] == '\0')
372  {
373  m_log.Emsg("Config", "User config file path not specified! Example usage: throttle.userconfig /etc/xrootd/throttle-users.conf"); return 1;
374  }
375 
376  m_user_config_file = val;
377  m_log.Emsg("Config Info: using user config file at '", val, "'.");
378  return 0;
379 }
#define open
Definition: XrdPosix.hh:78
#define TS_Xeq(key, func)
#define TRACE_IOLOAD
#define TRACE_BANDWIDTH
#define TRACE_FILES
#define TRACE_CONNS
#define TRACE_IOPS
#define TRACE_NONE
Definition: XrdTrace.hh:34
#define TRACE_DEBUG
Definition: XrdTrace.hh:36
#define TRACE_ALL
Definition: XrdTrace.hh:35
static int a2sp(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition: XrdOuca2x.cc:213
static int a2sz(XrdSysError &, const char *emsg, const char *item, long long *val, long long minv=-1, long long maxv=-1)
Definition: XrdOuca2x.cc:257
int Emsg(const char *esfx, int ecode, const char *text1, const char *text2=0)
Definition: XrdSysError.cc:116
void Say(const char *text1, const char *text2=0, const char *txt3=0, const char *text4=0, const char *text5=0, const char *txt6=0)
Definition: XrdSysError.cc:162
int Configure(const std::string &config_file)
XrdCmsConfig Config