Batch Processing¶
pScheduler can be used to run a series of tasks, called a batch, from the command line and return the results for further processing by other programs.
Each task is referred to as a job.
Jobs can be done singly or in multiple iterations with the latter being possible serially or in parallel.
Invocation¶
The batch processor is started using a pscheduler
command:
pscheduler batch [ OPTIONS ] [ INPUT-FILE ]
The OPTIONS
control the batch processor’s behavior and can be listed
using the --help
option.
The INPUT-FILE
is a path to the input. If not provided or is
-
, input will be taken from the standard input.
The final result (see _pscheduler_batch_output) is sent to the standard output.
Error and diagnostic output will be sent to standard error.
Input¶
Input to the batch processor is JSON in the form of a single object
containing three pairs, global
, jobs
and, optionally, a
schema
.
The schema
describes which version of the batch specification is
in use. If none is provided, it defaults to 1
. Specific
instances where other values are required are described below.
The global
Pair¶
The global
pair is an optional JSON object containing data and
transforms provided or applied to all jobs. It contains the following
pairs, all optional:
data
(Any JSON) - Data made available to all jq transforms as a
variable named $global
.
transform-pre
(pScheduler jq Transform) - A transform applied to
the task
object in each job before anything else is done.
transform-post
(pScheduler jq Transform) - A transform applied to
the task
object in each job after transform-pre and the job’s
task-transform have been applied.
The jobs
Pair¶
The jobs
pair is an array of objects, each containing a single job:
"jobs": [
{ ... Job 1 .. },
{ ... Job 2 .. },
{ ... Job n .. }
]
A job is described in a JSON object containing the following pairs:
label
(String) - A label for the job, used for reference in
debugging output.
enabled
(Boolean) - Determines whether or not the job is run.
Defaults to true
.
iterations
(Number or JQ Transform) - The number of times to run
the specified task. If a JQ transform is provided, the script should
calculate and return the number of iterations as an integer and the
schema
should be set to at least 3
. Input to the transform is
null
; any external data required should be accessed via
$global
.
parallel
(Boolean) - Whether or not the job’s iterations should be
run in parallel. This defaults to false
and implies sync-start
(see below) unless sync-start
is explicitly set false
.
setup-time
(Boolean) - The amount of time expected for pScheduler to
set up a single run. The default of PT15S
should be more than
sufficient in most cases. This is ignored if not doing a synchronized
start (see sync-start
, below).
backoff
(String) - ISO8601 duration indicating how long each
iteration run in parallel waits before being submitted to pScheduler.
The first will have no backoff, the second will have the indicated
backoff, the third will have twice that, etc. This value is ignored
if parallel
is false
.
sync-start
(Boolean) - If running in parallel, set the start time of
all iterations to be the same. The time is based on the number
of
times the task is run, backoff
and setup-time
. This value is
ignored if parallel
is false
. Note that tasks subject to
restrictions on being run at the same time will not necessarily start
in sync (or at all if no slip
is allowed as part of the task’s
schedule
section.
task
(Object or Array of Objects) - A pScheduler task
specification as would be produced using the task
command’s
--export
switch. Note that if the specification contains a
schedule
, those parameters will be ignored. If the value is an
array, the contents of the array will be treated as a set of tasks and
the number of iterations will be set to its length. This overrides
the contents of iterations
. Also note that a minimal batch
containing a single task can be exported using the --export
and
--export-format batch
switches.
task-transform
- A jq transform that operates on the task
’s
value for each iteration to make iteration-specific changes. The
$iteration
variable is provided to indicate which iteration
(starting with 0
) is being transformed. The script should operate
on the input in place.
For example, this job will run five sequential rtt
tests to
www.perfsonar.net
with 5, 10, 15, 20 and 25 pings sent. The
task-transform
adds a count
to the test specification that is
calculated based on the iteration:
{
"label": "rtt",
"iterations": 5,
"parallel": false,
"task": {
"test": {
"type": "rtt",
"spec": {
"schema": 1,
"dest": "www.perfsonar.net"
}
}
},
"task-transform": {
"script": [
".test.spec.count = ($iteration + 1) * 5"
]
}
}
continue-if
- A jq transform that determines, based on the results
of a job, whether the batch processor should continue to the next job
or abort the batch. The input given to the transform is the same as
the value of the results
pair in the output as described below.
For example:
[
{
"task": { ... },
"runs": [
{
"application/json": {
"schema": 1,
"duration": "PT2S",
"succeeded": true
},
"text/plain": " ... ",
"text/html": " ... "
}
]
}
]
The transform should return `true
for the batch to continue with
the next job or false
to abort the batch without processing any
subsequent jobs. Any other value is treated as an error and
the batch will be aborted with no results.
A continue-if
that decides whether to continue based on the
success or failure of the first run in a job would look like this:
{
"task": { ... },
"continue-if": {
"script": ".[0].runs[0].\"application/json\".succeeded"
}
}
Output¶
Once all jobs have been completed, the batch processor will output a
copy of the input with the addition of a results
pair in each job
containing information about what tasks were run and the results they
produced.
The results
pair is an array of JSON objects, with one element per
iteration. Each object contains the following pairs:
task
(pScheduler Task Specification) - The task that was submitted
to pScheduler and run.
runs
(Array of pScheduler Results) - An array of the results
produced by the task. In most cases, there will be a single element,
but for tasks that return multiple results (e.g., latencybg
),
there will be more than one. Each result is a JSON object containing
pairs named application/json
, text/plain
and text/html
for
each of the formats in which pScheduler can produce a result.
The batch
pair includes diagnostic information about the batch.
Invocation from Python¶
The batch processor can be invoked from Python on any system where
pScheduler’s Python library is installed. (On CentOS, this would be
the python-pscheduler
package.)
For example:
#!/usr/bin/env python3
import pscheduler.batchprocessor
import sys
batch = { ... }
def debug(message):
"""
Callback function for the batch processor to emit a line of
debug.
"""
print(message, file=sys.stderr)
processor = pscheduler.batchprocessor.BatchProcessor(batch)
# Leave out the debug argument for no debugging.
# This can be invoked multiple times to run the same batch repeatedly.
result = processor(debug=debug)
Tips and Tricks¶
Running Different Tasks as Part of the Same Job¶
Different tests can be run in parallel by using the task-transform
to alter the contents of the test
pair for each iteration.
Put an array of the tests to be run in the task’s
reference
pair. The length of the array should be the same as the specifiediterations
.Leave the task’s
test
section as an empty object ({}
).Add a
task-transform
that replaces the test with an element from the array (e.g.,.test = .reference.tests[$iteration]
).
This example runs a three-minute-long streaming latency test with a
throughput test to the same host during the second minute. The
backoff
value makes the througput test sleep for one minute before
it is scheduled and started so there’s latency data produced
beforehand and afterward.:
{
"label": "different-in-parallel",
"iterations": 2,
"parallel": true,
"backoff": "PT1M",
"task": {
"reference": {
"tests": [
{
"type": "latencybg",
"spec": {
"dest": "ps.example.net",
"duration": "PT3M"
}
},
{
"type": "throughput",
"spec": {
"dest": "ps.example.net",
"duration": "PT1M"
}
}
]
},
"#": "This is intentionally empty:",
"test": { }
},
"task-transform": {
"script": [
"# Replace the test section of the task with one of the",
"# tests in the reference block based on the iteration.",
".test = .reference.tests[$iteration]"
]
}
}