Skip to content

Commit 9e62f6c

Browse files
committed
Summary: tcf/ui_cli: introduce processes_guess
Unify the handling of "how many subprocesses" to use in the servers code with a utility to guess how many we shall use. This is fed into run_fn_on_each_server() so it can take care of the guessing for the caller (who can also override it if they know). Signed-off-by: Inaky Perez-Gonzalez <[email protected]>
1 parent 5f2fa16 commit 9e62f6c

File tree

5 files changed

+91
-25
lines changed

5 files changed

+91
-25
lines changed

commonl/__init__.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,41 @@ def retval(self):
209209
return None
210210

211211

212+
def processes_guess(factor: int):
213+
"""Very simple parallelization count adjuster
214+
215+
This is meant to be passed to something as
216+
*concurrent.futures.ProcessPoolExecutor*:
217+
218+
>>> paralellization_factor = -4
219+
>>> processes = commonl.processes_guess(paralellization_factor)
220+
>>> concurrent.futures.ProcessPoolExecutor(processes)
221+
222+
:param int factor: parallelization factor:
223+
224+
- positive: absolute number of threads to use; use *1* to
225+
serialize.
226+
227+
- 0: get the best value for a CPU intensive workload; this
228+
returns the amount of CPUs in the system.
229+
230+
- < 0: get the best value for an IO intensive workload, where we
231+
can do N IO operations / CPU.
232+
233+
"""
234+
if factor == 0:
235+
factor = int(os.environ.get("THREADS_GUESS", 0))
236+
if factor > 0:
237+
return factor
238+
if factor == 0:
239+
# factor for a CPU intensive workload
240+
return multiprocessing.cpu_count()
241+
# factor is negative; the absolute value is the intesiveness of
242+
# the IO vs CPU, so how many IO we can run in parallel for each CPU
243+
return -factor * multiprocessing.cpu_count()
244+
245+
246+
212247
class Process(fork_c): # COMPAT
213248
pass
214249

tcfl/servers.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import logging
2323
import os
2424

25+
import commonl
2526
import tcfl.ttb_client # COMPAT: FIXME remove
2627

2728
logger = logging.getLogger("tcfl.servers")
@@ -102,9 +103,9 @@ def _run_on_server(server_name, fn, *args,
102103
return None, e
103104

104105

105-
106106
def run_fn_on_each_server(servers: dict, fn: callable, *args,
107107
serialize: bool = False, traces: bool = False,
108+
parallelization_factor: int = -4,
108109
**kwargs):
109110
"""
110111
Run a function on each server in parallel
@@ -122,24 +123,22 @@ def run_fn_on_each_server(servers: dict, fn: callable, *args,
122123
:data:`tcfl.server_c.servers` for all servers or any other dict
123124
with whatever server names are chosen.
124125
125-
:param bool serialize: (optional, default *False*) if calls to
126-
each server need to be run in a single thread or can be run in
127-
parallel (default).
126+
:param int parallelization_factor: (optional, default -4, run
127+
four operations per processor) number of threads to use to
128+
parallelize the operation; use *1* to serialize.
128129
129130
:param bool traces: (optional, default *True*) if log messages for
130131
exceptions shall include stack traces.
131132
"""
132133

133-
if serialize:
134-
threads = 1
135-
else:
136-
threads = len(servers)
137-
134+
processes = min(
135+
len(servers),
136+
commonl.processes_guess(parallelization_factor))
138137
results = {}
139-
if threads == 0:
138+
if processes == 0:
140139
return results
141140

142-
with concurrent.futures.ProcessPoolExecutor(threads) as executor:
141+
with concurrent.futures.ProcessPoolExecutor(processes) as executor:
143142
futures = {
144143
# for each server, queue a thread that will call
145144
# _fn, who will call fn taking care of exceptions
@@ -164,6 +163,7 @@ def run_fn_on_each_server(servers: dict, fn: callable, *args,
164163
return results
165164

166165

166+
167167
def subsystem_setup(*args, **kwargs):
168168
"""
169169
Initialize the server management system in a synchronous way

tcfl/ui_cli.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,9 +67,14 @@ def args_targetspec_add(
6767
help = "Consider also disabled targets")
6868
if targetspec_n != 1:
6969
ap.add_argument(
70-
"--serialize", action = "store_true", default = False,
70+
"--serialize",
71+
action = "store_const", dest = "parellization_factor", default = 1,
7172
help = "Serialize (don't parallelize) the operation on"
7273
" multiple targets")
74+
ap.add_argument(
75+
"--parallelization-factor",
76+
action = "store", type = int, default = -4,
77+
help = "(advanced) parallelization factor")
7378
if isinstance(targetspec_n, bool):
7479
if targetspec_n:
7580
nargs = "+"

tcfl/ui_cli_servers.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ def _cmdline_servers(cli_args: argparse.Namespace):
6060

6161
r = tcfl.servers.run_fn_on_each_server(
6262
servers, _logged_in_username,
63-
serialize = cli_args.serialize, traces = cli_args.traces)
63+
parallelization_factor = cli_args.parallelization_factor,
64+
traces = cli_args.traces)
6465
# r now is a dict keyed by server_name of tuples usernames,
6566
# exception
6667
for server_name, ( username, _e ) in r.items():

tcfl/ui_cli_users.py

Lines changed: 37 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,8 @@ def _cmdline_login(cli_args: argparse.Namespace):
177177

178178
r = tcfl.servers.run_fn_on_each_server(
179179
servers, _login, credentials,
180-
serialize = cli_args.serialize, traces = cli_args.traces)
180+
parallelization_factor = cli_args.parallelization_factor,
181+
traces = cli_args.traces)
181182
# r now is a dict keyed by server_name of tuples usernames,
182183
# exception
183184
logged_count = 0
@@ -216,7 +217,8 @@ def _cmdline_logout(cli_args: argparse.Namespace):
216217

217218
r = tcfl.servers.run_fn_on_each_server(
218219
servers, _logout, cli_args,
219-
serialize = cli_args.serialize, traces = cli_args.traces)
220+
parallelization_factor = cli_args.parallelization_factor,
221+
traces = cli_args.traces)
220222
# r now is a dict keyed by server_name of tuples usernames,
221223
# exception
222224
for server_name, ( _, e ) in r.items():
@@ -258,7 +260,8 @@ def _cmdline_role_gain(cli_args: argparse.Namespace):
258260

259261
tcfl.servers.run_fn_on_each_server(
260262
servers, _user_role, cli_args, "gain",
261-
serialize = cli_args.serialize, traces = cli_args.traces)
263+
parallelization_factor = cli_args.parallelization_factor,
264+
traces = cli_args.traces)
262265

263266

264267
def _cmdline_role_drop(cli_args: argparse.Namespace):
@@ -269,7 +272,8 @@ def _cmdline_role_drop(cli_args: argparse.Namespace):
269272

270273
tcfl.servers.run_fn_on_each_server(
271274
servers, _user_role, cli_args, "drop",
272-
serialize = cli_args.serialize, traces = cli_args.traces)
275+
parallelization_factor = cli_args.parallelization_factor,
276+
traces = cli_args.traces)
273277

274278

275279

@@ -290,7 +294,8 @@ def _cmdline_user_list(cli_args: argparse.Namespace):
290294

291295
result = tcfl.servers.run_fn_on_each_server(
292296
tcfl.server_c.servers, _user_list, cli_args,
293-
serialize = cli_args.serialize, traces = cli_args.traces)
297+
parallelization_factor = cli_args.parallelization_factor,
298+
traces = cli_args.traces)
294299

295300
# so now result is a dictionary of SERVER: ( DATA, EXCEPTION ),
296301
# where DATA is dictionaries of USERNAME: USERDATA
@@ -399,9 +404,14 @@ def cmdline_setup(arg_subparser):
399404
"AKA is the short name of the server (defaults to the sole "
400405
"host name, without the domain). Find it with 'tcf servers'")
401406
ap.add_argument(
402-
"--serialize", action = "store_true", default = False,
407+
"--serialize",
408+
action = "store_const", dest = "parellization_factor", default = 1,
403409
help = "Serialize (don't parallelize) the operation on"
404-
" multiple servers")
410+
" multiple targets")
411+
ap.add_argument(
412+
"--parallelization-factor",
413+
action = "store", type = int, default = -4,
414+
help = "(advanced) parallelization factor")
405415
ap.add_argument(
406416
"username", nargs = '?', metavar = "USERNAME",
407417
action = "store", default = None,
@@ -434,9 +444,14 @@ def cmdline_setup_advanced(arg_subparser):
434444
help = "ID of user whose role is to be dropped"
435445
" (optional, defaults to yourself)")
436446
ap.add_argument(
437-
"--serialize", action = "store_true", default = False,
447+
"--serialize",
448+
action = "store_const", dest = "parellization_factor", default = 1,
438449
help = "Serialize (don't parallelize) the operation on"
439-
" multiple servers")
450+
" multiple targets")
451+
ap.add_argument(
452+
"--parallelization-factor",
453+
action = "store", type = int, default = -4,
454+
help = "(advanced) parallelization factor")
440455
ap.add_argument(
441456
"role", action = "store",
442457
help = "Role to gain")
@@ -452,9 +467,14 @@ def cmdline_setup_advanced(arg_subparser):
452467
help = "ID of user whose role is to be dropped"
453468
" (optional, defaults to yourself)")
454469
ap.add_argument(
455-
"--serialize", action = "store_true", default = False,
470+
"--serialize",
471+
action = "store_const", dest = "parellization_factor", default = 1,
456472
help = "Serialize (don't parallelize) the operation on"
457-
" multiple servers")
473+
" multiple targets")
474+
ap.add_argument(
475+
"--parallelization-factor",
476+
action = "store", type = int, default = -4,
477+
help = "(advanced) parallelization factor")
458478
ap.add_argument(
459479
"role", action = "store",
460480
help = "Role to drop")
@@ -467,9 +487,14 @@ def cmdline_setup_advanced(arg_subparser):
467487
"admin role privilege to list users others than your own)")
468488
tcfl.ui_cli.args_verbosity_add(ap)
469489
ap.add_argument(
470-
"--serialize", action = "store_true", default = False,
490+
"--serialize",
491+
action = "store_const", dest = "parellization_factor", default = 1,
471492
help = "Serialize (don't parallelize) the operation on"
472493
" multiple targets")
494+
ap.add_argument(
495+
"--parallelization-factor",
496+
action = "store", type = int, default = -4,
497+
help = "(advanced) parallelization factor")
473498
ap.add_argument("userid", action = "store",
474499
default = None, nargs = "*",
475500
help = "Users to list (default all)")

0 commit comments

Comments
 (0)