Submitting Sub or Child Jobs

Problem

You want to submit sub-jobs within a task.

Solution

Get a reference to the scheduler_context interface by calling the task’s get_property() and passing in JOB_SCHEDULER_CONTEXT.

self.scheduler_context = self.get_property(TaskProperties.JOB_SCHEDULER_CONTEXT)

With a reference to the scheduler_context interface, call create_child_job() to create a job.

job2 = self.scheduler_context.create_child_job()
 1from agiparallel.client import ClusterJobScheduler
 2from agiparallel.constants import TaskProperties
 3
 4
 5class ParentTask():
 6    def __init__(self):
 7        self.result = None
 8        self.scheduler_context = None
 9
10    def execute(self):
11        self.scheduler_context = self.get_property(TaskProperties.JOB_SCHEDULER_CONTEXT)
12
13        job2 = self.scheduler_context.create_child_job()
14        job2.name = "child_job"
15        task = ChildTask()
16        job2.add_task(task)
17        job2.submit()
18        job2.wait_until_done()
19        self.result = "From Parent -> " + str(task.result)
20
21
22class ChildTask():
23    def __init__(self):
24        self.result = None
25
26    def execute(self):
27        self.result = "From Child"
28
29
30def child_tasks_example():
31    with ClusterJobScheduler("localhost") as client:
32        client.connect()
33        job = client.create_job()
34        task = ParentTask()
35        job.add_task(task)
36        job.name = "parent_job"
37        job.submit()
38        job.wait_until_done()
39        print(job.tasks[0].result)
40
41
42if __name__ == "__main__":
43    child_tasks_example()

Discussion

Along with being submitted by a client, jobs can also be submitted in a task. Submitting sub-jobs can be a powerful tool used to handle dynamic workloads such as divide and conquer computations. Sub-jobs have all the same fields and properties as a standard job, e.g. events, monitoring, status reporting, etc. When sub-jobs are submitted they are submitted back to the original client’s job scheduler and tracked the same way a task submitted from a regular client is tracked. Sub-jobs can also have sub-jobs of their own, which enables tasks to recursively spawn work to child jobs.

There are some advanced details about sub-jobs that are useful to know when debugging an issue. The most important concept to understand is how sub-jobs deal with allocated resources. Internally, a sub-job is treated just like a regular job; before a job can run, it needs available resources to consume. For both jobs and sub-jobs, cores are the most common of these resources.

To explain how they are different is best done through an example. If a regular job is submitted to a job scheduler that has no cores available, the job gets queued and waits until a core becomes available. When a sub-job is submitted to a job scheduler that has no cores available, the child job gets immediately executed. This happens because internally the parent task yields all its resources to the child job. This works transparently without the user doing anything. If this is not the desired behavior, there is an overload to create_child_job() that allows a yield_all_resources_to_children parameter. If the yield_all_resources_to_children parameter is False, resource yielding will not be automatic. The sub-job waits in the queue like a regular job.

Note

When submitting sub-jobs, the most common use case is that the child job handles the workload of its parent while the parent waits for the work to finish. By default, the reason all resources are provided to the children is to try to make this use case as easy as possible.