Skip to content

Commit c98af28

Browse files
committed
Add function to shutdown tasks
1 parent 3e0d914 commit c98af28

6 files changed

Lines changed: 114 additions & 1 deletion

File tree

executorlib/executor/flux.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
from executorlib.standalone.inputcheck import (
55
check_command_line_argument_lst,
66
check_init_function,
7+
check_log_obj_size,
78
check_oversubscribe,
89
check_plot_dependency_graph,
910
check_pmi,
1011
check_refresh_rate,
12+
check_terminate_tasks_on_shutdown,
1113
validate_number_of_cores,
1214
)
1315
from executorlib.task_scheduler.interactive.blockallocation import (
@@ -62,6 +64,7 @@ class FluxJobExecutor(BaseExecutor):
6264
debugging purposes and to get an overview of the specified dependencies.
6365
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
6466
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
67+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
6568
6669
Examples:
6770
```
@@ -102,6 +105,7 @@ def __init__(
102105
plot_dependency_graph: bool = False,
103106
plot_dependency_graph_filename: Optional[str] = None,
104107
log_obj_size: bool = False,
108+
terminate_tasks_on_shutdown: bool = True,
105109
):
106110
"""
107111
The executorlib.FluxJobExecutor leverages either the message passing interface (MPI), the SLURM workload manager
@@ -147,6 +151,7 @@ def __init__(
147151
debugging purposes and to get an overview of the specified dependencies.
148152
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
149153
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
154+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
150155
151156
"""
152157
default_resource_dict: dict = {
@@ -162,6 +167,9 @@ def __init__(
162167
resource_dict.update(
163168
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
164169
)
170+
check_terminate_tasks_on_shutdown(
171+
terminate_tasks_on_shutdown=terminate_tasks_on_shutdown
172+
)
165173
if not disable_dependencies:
166174
super().__init__(
167175
executor=DependencyTaskScheduler(
@@ -246,6 +254,8 @@ class FluxClusterExecutor(BaseExecutor):
246254
plot_dependency_graph (bool): Plot the dependencies of multiple future objects without executing them. For
247255
debugging purposes and to get an overview of the specified dependencies.
248256
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
257+
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
258+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
249259
250260
Examples:
251261
```
@@ -282,6 +292,8 @@ def __init__(
282292
refresh_rate: float = 0.01,
283293
plot_dependency_graph: bool = False,
284294
plot_dependency_graph_filename: Optional[str] = None,
295+
log_obj_size: bool = False,
296+
terminate_tasks_on_shutdown: bool = True,
285297
):
286298
"""
287299
The executorlib.FluxClusterExecutor leverages either the message passing interface (MPI), the SLURM workload
@@ -323,6 +335,8 @@ def __init__(
323335
plot_dependency_graph (bool): Plot the dependencies of multiple future objects without executing them. For
324336
debugging purposes and to get an overview of the specified dependencies.
325337
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
338+
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
339+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
326340
327341
"""
328342
default_resource_dict: dict = {
@@ -338,12 +352,20 @@ def __init__(
338352
resource_dict.update(
339353
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
340354
)
355+
check_log_obj_size(log_obj_size=log_obj_size)
341356
if not plot_dependency_graph:
342357
import pysqa # noqa
343358

344359
from executorlib.task_scheduler.file.task_scheduler import (
345360
create_file_executor,
346361
)
362+
if terminate_tasks_on_shutdown:
363+
from executorlib.task_scheduler.file.queue_spawner import (
364+
terminate_with_pysqa,
365+
)
366+
terminate_function = terminate_with_pysqa
367+
else:
368+
terminate_function = None
347369

348370
super().__init__(
349371
executor=create_file_executor(
@@ -361,6 +383,7 @@ def __init__(
361383
block_allocation=block_allocation,
362384
init_function=init_function,
363385
disable_dependencies=disable_dependencies,
386+
terminate_function=terminate_function,
364387
)
365388
)
366389
else:

executorlib/executor/slurm.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from executorlib.executor.base import BaseExecutor
44
from executorlib.standalone.inputcheck import (
55
check_init_function,
6+
check_log_obj_size,
67
check_plot_dependency_graph,
78
check_refresh_rate,
9+
check_terminate_tasks_on_shutdown,
810
validate_number_of_cores,
911
)
1012
from executorlib.task_scheduler.interactive.blockallocation import (
@@ -58,6 +60,8 @@ class SlurmClusterExecutor(BaseExecutor):
5860
plot_dependency_graph (bool): Plot the dependencies of multiple future objects without executing them. For
5961
debugging purposes and to get an overview of the specified dependencies.
6062
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
63+
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
64+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
6165
6266
Examples:
6367
```
@@ -94,6 +98,8 @@ def __init__(
9498
refresh_rate: float = 0.01,
9599
plot_dependency_graph: bool = False,
96100
plot_dependency_graph_filename: Optional[str] = None,
101+
log_obj_size: bool = False,
102+
terminate_tasks_on_shutdown: bool = True,
97103
):
98104
"""
99105
The executorlib.SlurmClusterExecutor leverages either the message passing interface (MPI), the SLURM workload
@@ -135,6 +141,8 @@ def __init__(
135141
plot_dependency_graph (bool): Plot the dependencies of multiple future objects without executing them. For
136142
debugging purposes and to get an overview of the specified dependencies.
137143
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
144+
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
145+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
138146
139147
"""
140148
default_resource_dict: dict = {
@@ -150,12 +158,20 @@ def __init__(
150158
resource_dict.update(
151159
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
152160
)
161+
check_log_obj_size(log_obj_size=log_obj_size)
153162
if not plot_dependency_graph:
154163
import pysqa # noqa
155164

156165
from executorlib.task_scheduler.file.task_scheduler import (
157166
create_file_executor,
158167
)
168+
if terminate_tasks_on_shutdown:
169+
from executorlib.task_scheduler.file.queue_spawner import (
170+
terminate_with_pysqa,
171+
)
172+
terminate_function = terminate_with_pysqa
173+
else:
174+
terminate_function = None
159175

160176
super().__init__(
161177
executor=create_file_executor(
@@ -173,6 +189,7 @@ def __init__(
173189
block_allocation=block_allocation,
174190
init_function=init_function,
175191
disable_dependencies=disable_dependencies,
192+
terminate_function=terminate_function,
176193
)
177194
)
178195
else:
@@ -239,6 +256,7 @@ class SlurmJobExecutor(BaseExecutor):
239256
debugging purposes and to get an overview of the specified dependencies.
240257
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
241258
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
259+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
242260
243261
Examples:
244262
```
@@ -275,6 +293,7 @@ def __init__(
275293
plot_dependency_graph: bool = False,
276294
plot_dependency_graph_filename: Optional[str] = None,
277295
log_obj_size: bool = False,
296+
terminate_tasks_on_shutdown: bool = True,
278297
):
279298
"""
280299
The executorlib.SlurmJobExecutor leverages either the message passing interface (MPI), the SLURM workload
@@ -320,6 +339,7 @@ def __init__(
320339
debugging purposes and to get an overview of the specified dependencies.
321340
plot_dependency_graph_filename (str): Name of the file to store the plotted graph in.
322341
log_obj_size (bool): Enable debug mode which reports the size of the communicated objects.
342+
terminate_tasks_on_shutdown (bool): Shutdown all tasks when the Executor is shutdown, this is the default.
323343
324344
"""
325345
default_resource_dict: dict = {
@@ -335,6 +355,9 @@ def __init__(
335355
resource_dict.update(
336356
{k: v for k, v in default_resource_dict.items() if k not in resource_dict}
337357
)
358+
check_terminate_tasks_on_shutdown(
359+
terminate_tasks_on_shutdown=terminate_tasks_on_shutdown
360+
)
338361
if not disable_dependencies:
339362
super().__init__(
340363
executor=DependencyTaskScheduler(

executorlib/standalone/inputcheck.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,33 @@ def validate_number_of_cores(
194194

195195

196196
def check_file_exists(file_name: Optional[str]):
197+
"""
198+
Check if file exists and raise a ValueError if it does not or file_name is None.
199+
"""
197200
if file_name is None:
198201
raise ValueError("file_name is not set.")
199202
if not os.path.exists(file_name):
200203
raise ValueError("file_name is not written to the file system.")
204+
205+
206+
def check_log_obj_size(log_obj_size: bool) -> None:
207+
"""
208+
Check if log_obj_size is True and raise a ValueError if it is.
209+
"""
210+
if log_obj_size:
211+
raise ValueError(
212+
"log_obj_size is not supported for the executorlib.SlurmClusterExecutor and executorlib.FluxClusterExecutor."
213+
"Please use log_obj_size=False instead of log_obj_size=True."
214+
)
215+
216+
217+
def check_terminate_tasks_on_shutdown(terminate_tasks_on_shutdown: bool) -> None:
218+
"""
219+
Check if terminate_tasks_on_shutdown is False and raise a ValueError if it is.
220+
"""
221+
if not terminate_tasks_on_shutdown:
222+
raise ValueError(
223+
"terminate_tasks_on_shutdown is not supported for the executorlib.SingleNodeExecutor, "
224+
"executorlib.SlurmJobExecutor and executorlib.FluxJobExecutor."
225+
"Please use terminate_tasks_on_shutdown=True instead of terminate_tasks_on_shutdown=False."
226+
)

executorlib/task_scheduler/file/queue_spawner.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,27 @@ def execute_with_pysqa(
8181
return queue_id
8282

8383

84+
def terminate_with_pysqa(
85+
queue_id: int,
86+
config_directory: Optional[str] = None,
87+
backend: Optional[str] = None,
88+
):
89+
"""
90+
Delete job from queuing system
91+
92+
Args:
93+
queue_id (int): Queuing system ID of the job to delete.
94+
config_directory (str, optional): path to the config directory.
95+
backend (str, optional): name of the backend used to spawn tasks.
96+
"""
97+
qa = QueueAdapter(
98+
directory=config_directory,
99+
queue_type=backend,
100+
execute_command=_pysqa_execute_command,
101+
)
102+
qa.delete_job(process_id=queue_id)
103+
104+
84105
def _pysqa_execute_command(
85106
commands: str,
86107
working_directory: Optional[str] = None,

executorlib/task_scheduler/file/shared.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@
1010
from executorlib.standalone.command import get_command_path
1111
from executorlib.standalone.serialize import serialize_funct_h5
1212
from executorlib.task_scheduler.file.hdf import dump, get_output
13+
from executorlib.task_scheduler.file.subprocess_spawner import (
14+
terminate_subprocess
15+
)
1316

1417

1518
class FutureItem:
@@ -86,9 +89,16 @@ def execute_tasks_h5(
8689
with contextlib.suppress(queue.Empty):
8790
task_dict = future_queue.get_nowait()
8891
if task_dict is not None and "shutdown" in task_dict and task_dict["shutdown"]:
89-
if terminate_function is not None:
92+
if terminate_function is not None and terminate_function == terminate_subprocess:
9093
for task in process_dict.values():
9194
terminate_function(task=task)
95+
elif terminate_function is not None:
96+
for queue_id in process_dict.values():
97+
terminate_function(
98+
queue_id=queue_id,
99+
config_directory=pysqa_config_directory,
100+
backend=backend,
101+
)
92102
future_queue.task_done()
93103
future_queue.join()
94104
break

tests/test_standalone_inputcheck.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
check_hostname_localhost,
1919
check_pysqa_config_directory,
2020
check_file_exists,
21+
check_terminate_tasks_on_shutdown,
22+
check_log_obj_size,
2123
validate_number_of_cores,
2224
)
2325

@@ -119,3 +121,11 @@ def test_validate_number_of_cores(self):
119121
),
120122
int,
121123
)
124+
125+
def test_check_log_obj_size(self):
126+
with self.assertRaises(ValueError):
127+
check_log_obj_size(log_obj_size=True)
128+
129+
def test_terminate_tasks_on_shutdown(self):
130+
with self.assertRaises(ValueError):
131+
check_terminate_tasks_on_shutdown(terminate_tasks_on_shutdown=False)

0 commit comments

Comments
 (0)