Skip to content

Commit 406358e

Browse files
pditommasorobsymeclaudebentshermanchristopher-hakkaart
authored
Add hints process directive for executor-specific scheduling hints (#7034)
* Add ADR for hints process directive Propose a generic 'hints' process directive with namespaced keys as the extension point for executor-specific scheduling hints. First use case: AWS Batch consumable resources for license-seat management. Evaluates three options: dedicated consumableResources directive, overloading resourceLabels, and a new hints directive. Recommends hints for extensibility and separation of concerns. Ref: #5917 Signed-off-by: Rob Syme <rob.syme@gmail.com> * Fix module info formatting separator [ci fast] Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * Update ADR Signed-off-by: Ben Sherman <bentshermann@gmail.com> * Add `hints` process directive for executor-specific scheduling hints Introduce a new map-type `hints` process directive that provides a structured, extensible way for pipeline authors to pass executor-specific scheduling hints. Keys use `[executor/][scope.]hintName` format. Core features: - Multiple `hints` calls within a process body accumulate (merge) - Config overrides via withName:/withLabel: replace the entire map - Values support String, Integer, and Closure types - Two-tier validation: warnings for unknown unprefixed keys (global registry), errors for unknown executor-prefixed keys - Initial global catalog contains only `consumableResource` Seqera Platform integration: - `seqera/machineRequirement.*` hints map to MachineRequirementOpts fields (arch, provisioning, maxSpotAttempts, machineTypes, diskType, diskThroughputMiBps, diskIops, diskEncrypted, diskAllocation, diskMountPath, diskSize, capacityMode) - Hints override Seqera config scope values at the task level - Unknown `seqera/` keys produce an error Ref: #5917, #6960 Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * update docs Signed-off-by: Ben Sherman <bentshermann@gmail.com> * Add awsbatch/consumableResources hint Signed-off-by: Ben Sherman <bentshermann@gmail.com> * Fix HintHelper signatures to match TaskConfig.getHints() [ci fast] TaskConfig.getHints() returns Map<String, String>, but HintHelper declared Map<String, Object>, causing @CompileStatic compile errors in SeqeraTaskHandler. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * Use String hint values in HintHelperTest to match production path [ci fast] TaskConfig.getHints() always delivers Map<String, String>; tests now reflect that contract instead of relying on generics erasure. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * Address code review for hints directive - Enforce Map<String,String> shape; remove AWS-specific KNOWN_HINTS leak from core HintDefs. Core validates shape only; executors validate their own namespaces. - AWS Batch: harden consumableResources parser with clear errors, prefer awsbatch/-prefixed key, warn on unknown awsbatch/* keys. - Seqera: derive known hint keys from MachineRequirementOpts via @ConfigOption reflection; overlay preserves all declared fields. Accept unprefixed machineRequirement.* keys too — prefixed wins on collision. Drop dangling references to the removed `arch` field. - Replace Mock(TaskConfig) integration test in AWS Batch with a real ProcessBuilder → TaskConfig path; add compact SeqeraTaskHandler submit tests covering hint overlay and foreign-namespace passthrough. - Docs: spell out unprefixed-applies-to-any-executor semantics; list all Seqera-supported hints. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * minor edits Signed-off-by: Ben Sherman <bentshermann@gmail.com> * Fix ordering of seqera executor config options Signed-off-by: Ben Sherman <bentshermann@gmail.com> * Allow hint values to be any raw data type Signed-off-by: Ben Sherman <bentshermann@gmail.com> * Update docs/reference/config.md [ci skip] Co-authored-by: Chris Hakkaart <chris.hakkaart@seqera.io> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * Update docs/reference/process.md [ci skip] Co-authored-by: Chris Hakkaart <chris.hakkaart@seqera.io> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * Update docs/reference/process.md [ci skip] Co-authored-by: Chris Hakkaart <chris.hakkaart@seqera.io> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> * Update docs/reference/process.md [ci skip] Co-authored-by: Chris Hakkaart <chris.hakkaart@seqera.io> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> --------- Signed-off-by: Rob Syme <rob.syme@gmail.com> Signed-off-by: Paolo Di Tommaso <paolo.ditommaso@gmail.com> Signed-off-by: Ben Sherman <bentshermann@gmail.com> Co-authored-by: Rob Syme <rob.syme@gmail.com> Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Co-authored-by: Ben Sherman <bentshermann@gmail.com> Co-authored-by: Chris Hakkaart <chris.hakkaart@seqera.io>
1 parent b535377 commit 406358e

20 files changed

Lines changed: 1193 additions & 25 deletions

File tree

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# `hints` process directive for executor-specific scheduling hints
2+
3+
- Authors: Rob Syme
4+
- Status: accepted
5+
- Deciders: Paolo Di Tommaso, Ben Sherman, Rob Syme
6+
- Date: 2026-03-23
7+
- Tags: directive, executor, scheduling
8+
9+
## Summary
10+
11+
Introduce a `hints` process directive for executor-specific scheduling hints that don't map to existing directives.
12+
13+
## Problem Statement
14+
15+
Many executors can be configured in various ways on a per-task basis. For example:
16+
17+
- AWS Batch jobs can use *consumable resources* to limit concurrent job execution based on non-standard resources such as software license seats.
18+
19+
- Google Batch jobs can specify a *provisioning model* to control the use of spot vs on-demand VMs on a per-task basis.
20+
21+
- Seqera Scheduler supports a variety of resource and scheduling settings, including spot/on-demand provisioning.
22+
23+
These settings can be exposed by Nextflow as executor-specific config options, such as `google.batch.spot`, but config options are applied globally. In order to apply a setting to specific processes or tasks, it must be exposed as a process directive.
24+
25+
Process directives in Nextflow aim to provide a common vocabulary for executing tasks in many different environments. Directives such as `cpus`, `memory`, and `time` have broadly the same meaning across most executors, making it easier for users to write portable pipelines.
26+
27+
At the same time, many executors have custom settings not shared by other executors, and it is not practical to create a new process directive for every new setting. There are over 40 [process directives](https://docs.seqera.io/nextflow/reference/process#directives) at the time of writing, and every new directive adds cognitive load when a user is trying to find the right directive for a given situation.
28+
29+
There exist a few generic process directives already:
30+
31+
- The `clusterOptions` directive can be used to specify command-line arguments, primarily for HPC schedulers
32+
- The `ext` directive supports arbitrary key-values, but is designed primarily to customize the task script (e.g. tool arguments), not executor behavior
33+
- The `resourceLabels` directive also supports arbitrary key-values, but is intended for tagging and tracking resources, not controlling them
34+
35+
A new directive is needed to support executor-specific settings at a per-task level in a structured manner, without bloating the process directives for every new custom setting.
36+
37+
## Goals
38+
39+
- Provide a way to apply executor-specific settings to individual processes or tasks
40+
41+
- Avoid the proliferation of narrow, executor-specific directives (e.g. `consumableResources`, `schedulingPolicy`, etc.)
42+
43+
- Provide a single extension point that executors can consume selectively
44+
45+
- Allow settings to be specified as key-values, providing validation where possible
46+
47+
## Non-goals
48+
49+
- Replacing existing directives (`cpus`, `memory`, `accelerator`, `queue`) — those remain the right place for standard resources
50+
51+
## Decision
52+
53+
Introduce a `hints` process directive with namespaced keys. Executors consume the hints they understand and silently ignore the rest.
54+
55+
## Core Capabilities
56+
57+
### Syntax
58+
59+
The `hints` directive accepts a map of key-value pairs:
60+
61+
```groovy
62+
// process definition
63+
process runDragen {
64+
cpus 4
65+
memory '16 GB'
66+
hints consumableResources: ['my-dragen-license': 1, 'other-license': 2]
67+
68+
script:
69+
"""
70+
dragen --ref-dir /ref ...
71+
"""
72+
}
73+
```
74+
75+
```groovy
76+
// process config
77+
process {
78+
withName: 'runDragen' {
79+
hints = [
80+
consumableResources: ['my-dragen-license': 1, 'other-license': 2]
81+
]
82+
}
83+
}
84+
```
85+
86+
Keys are strings. Values may be any raw data type: strings, numbers, booleans, lists, or maps. Executors are responsible for defining which hints they recognize and what value type each hint expects.
87+
88+
In the above example, the `consumableResources` hint is given as a map of resource name to quantity. The AWS Batch executor supplies it to each job request using `ConsumableResourceProperties`.
89+
90+
### Namespacing
91+
92+
Keys can use dot-separated scopes to namespace settings as needed:
93+
94+
```groovy
95+
hints consumableResources: ['my-dragen-license': 1]
96+
hints 'scheduling.priority': 10
97+
hints 'scheduling.provisioningModel': 'spot'
98+
```
99+
100+
Keys can be routed to specific executors by prefixing with the executor name and a slash (`/`):
101+
102+
```groovy
103+
hints 'awsbatch/consumableResources': ['my-dragen-license': 1]
104+
hints 'seqera/scheduling.provisioningModel': 'spot'
105+
hints 'k8s/nodeSelector': 'gpu=true'
106+
```
107+
108+
The executor prefix gives pipeline developers the ability to target specific executors and have assurance that it won't accidentally apply to other executors (e.g. if another executor adds support for the same hint in the future).
109+
110+
### Validation
111+
112+
Nextflow should validate hints to the best of its ability, to catch errors such as typos:
113+
114+
- **Prefixed hints** can be validated against the set of hints declared by the corresponding executor. Unrecognized hints should be reported as errors.
115+
116+
- **Unprefixed hints** can be validated against the union of hints declared by all executors. Since unprefixed hints might be supported by executors that aren't currently loaded, unrecognized hints should be reported as warnings.
117+
118+
### Multiple hint resolution
119+
120+
The `hints` directive uses *replacement semantics* when specified multiple times, meaning that each `hints` setting completely replaces any previous settings:
121+
122+
```groovy
123+
process {
124+
// generic hint
125+
hints = [provisioningModel: 'spot']
126+
127+
// specific hint replaces generic hint
128+
withLabel: 'dragen' {
129+
hints = [consumableResources: ['my-dragen-license': 1]]
130+
}
131+
}
132+
```
133+
134+
Within a process definition, the `hints` directive uses *accumulation semantics*, meaning that subsequent `hints` directives are accumulated:
135+
136+
```groovy
137+
process runDragen {
138+
// multiple separate hints
139+
hints provisioningModel: 'spot'
140+
hints consumableResources: ['my-dragen-license': 1, 'other-license': 2]
141+
142+
// equivalent to...
143+
hints (
144+
provisioningModel: 'spot',
145+
consumableResources: ['my-dragen-license': 1, 'other-license': 2]
146+
)
147+
148+
// ...
149+
}
150+
```
151+
152+
This behavior is consistent with other directives such as `pod` and `resourceLabels`. In practice, this means that a given `hints` setting should specify all relevant hints for the given context.
153+
154+
For example, the `withLabel` selector above should also specify the `provisioningModel` hint if the intention is to preserve that hint for the selected processes:
155+
156+
```groovy
157+
process {
158+
hints = [provisioningModel: 'spot']
159+
160+
withLabel: 'dragen' {
161+
hints = [provisioningModel: 'spot', consumableResources: ['my-dragen-license': 1]]
162+
}
163+
}
164+
```
165+
166+
While this approach may lead to duplication, it gives users and developers more control over which hints are applied in a given context.
167+
168+
### Initial hint catalog
169+
170+
The following hints should be supported initially:
171+
172+
| Hint name | Value type | Executors | Use case |
173+
|--|--|--|--|
174+
| `consumableResources` | `Map<String, Integer>` | AWS Batch | License-aware scheduling ([#5917](https://github.com/nextflow-io/nextflow/issues/5917)) |
175+
| `scheduling.priority` | `Integer` | AWS Batch | Job scheduling priority ([#6998](https://github.com/nextflow-io/nextflow/issues/6998)) |
176+
| `scheduling.provisioningModel` | `String` | Google Batch | Spot VM scheduling ([#3530](https://github.com/nextflow-io/nextflow/issues/3530)) |
177+
178+
## Links
179+
180+
- [Community issue](https://github.com/nextflow-io/nextflow/issues/5917)

docs/executor.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ Resource requests and other job characteristics can be controlled via the follow
3333
- {ref}`process-resourcelabels`
3434
- {ref}`process-time`
3535

36+
The following {ref}`hints <process-hints>` are supported:
37+
38+
- `consumableResources`: Specify [AWS Batch consumable resources](https://docs.aws.amazon.com/batch/latest/userguide/resource-aware-scheduling.html) as a list of name-value pairs. For example:
39+
40+
```nextflow
41+
hints consumableResources: ['my-license-a': 1, 'my-license-b': 2]
42+
```
43+
3644
See {ref}`aws-batch` for more information.
3745

3846
(azurebatch-executor)=
@@ -441,6 +449,37 @@ Resource requests and other job characteristics can be controlled via the follow
441449
- {ref}`process-memory`
442450
- {ref}`process-time`
443451

452+
The following {ref}`hints <process-hints>` are supported:
453+
454+
- `machineRequirement.capacityMode`
455+
- `machineRequirement.diskAllocation`
456+
- `machineRequirement.diskEncrypted`
457+
- `machineRequirement.diskIops`
458+
- `machineRequirement.diskMountPath`
459+
- `machineRequirement.diskSize`
460+
- `machineRequirement.diskThroughputMiBps`
461+
- `machineRequirement.diskType`
462+
- `machineRequirement.machineTypes`
463+
- `machineRequirement.maxSpotAttempts`
464+
- `machineRequirement.provisioning`
465+
466+
Each hint overrides the corresponding field of the `seqera.executor.machineRequirement` config scope on a per-process basis. Keys may be used as-is or with the `seqera/` prefix to restrict them to this executor.
467+
468+
For example, to override the provisioning mode for a single process:
469+
470+
```nextflow
471+
process hello {
472+
hints 'seqera/machineRequirement.provisioning': 'spotFirst'
473+
474+
script:
475+
"""
476+
your_command --here
477+
"""
478+
}
479+
```
480+
481+
See {ref}`config-seqera` for the full config reference.
482+
444483
### Disk support
445484

446485
When the {ref}`process-disk` directive is specified, the Seqera executor provisions storage for the task container. There are two disk allocation strategies:

docs/reference/config.md

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1441,6 +1441,12 @@ The `seqera.executor` scope configures the Seqera scheduler service for the {ref
14411441

14421442
The following settings are available:
14431443

1444+
`seqera.executor.autoLabels`
1445+
: When `true`, automatically adds workflow metadata labels to the session with the `nextflow.io/` prefix (default: `false`). The following labels are added: `projectName`, `userName`, `runName`, `sessionId`, `resume`, `revision`, `commitId`, `repository`, `manifestName`, `runtimeVersion`. A `seqera.io/runId` label is also added, computed as a SipHash of the session ID and run name.
1446+
1447+
`seqera.executor.computeEnvId`
1448+
: The Seqera Platform compute environment ID. When specified, the scheduler resolves the compute environment directly by this ID instead of inferring a suitable compute environment. Used as a fallback when the workflow launch does not include a compute environment reference.
1449+
14441450
`seqera.executor.endpoint`
14451451
: The Seqera scheduler service endpoint URL (required).
14461452

@@ -1450,41 +1456,35 @@ The following settings are available:
14501456
`seqera.executor.region`
14511457
: The cloud region for task execution.
14521458

1453-
`seqera.executor.computeEnvId`
1454-
: The Seqera Platform compute environment ID. When specified, the scheduler resolves the compute environment directly by this ID instead of inferring a suitable compute environment. Used as a fallback when the workflow launch does not include a compute environment reference.
1455-
1456-
`seqera.executor.autoLabels`
1457-
: When `true`, automatically adds workflow metadata labels to the session with the `nextflow.io/` prefix (default: `false`). The following labels are added: `projectName`, `userName`, `runName`, `sessionId`, `resume`, `revision`, `commitId`, `repository`, `manifestName`, `runtimeVersion`. A `seqera.io/runId` label is also added, computed as a SipHash of the session ID and run name.
1458-
1459-
`seqera.executor.machineRequirement.provisioning`
1460-
: The instance provisioning mode. Can be `'spot'`, `'ondemand'`, or `'spotFirst'`.
1461-
1462-
`seqera.executor.machineRequirement.maxSpotAttempts`
1463-
: The maximum number of spot retry attempts before falling back to on-demand. Only used when `provisioning` is `'spot'` or `'spotFirst'`.
1464-
1465-
`seqera.executor.machineRequirement.machineFamilies`
1466-
: List of acceptable EC2 instance families, e.g. `['m5', 'c5', 'r5']`.
1459+
`seqera.executor.taskEnvironment`
1460+
: Custom environment variables to apply to all tasks submitted by the Seqera executor. These are merged with the Fusion environment variables, with Fusion variables taking precedence. For example: `taskEnvironment = [MY_VAR: 'value']`.
14671461

14681462
`seqera.executor.machineRequirement.diskAllocation`
14691463
: The disk allocation strategy. Can be `'task'` (default) for per-task EBS volumes, or `'node'` for per-node instance storage. When using `'node'` allocation, EBS-specific options (`diskType`, `diskIops`, `diskThroughputMiBps`, `diskEncrypted`) are not applicable.
14701464

1471-
`seqera.executor.machineRequirement.diskType`
1472-
: The EBS volume type for task scratch disk. Supported types: `'ebs/gp3'` (default), `'ebs/gp2'`, `'ebs/io1'`, `'ebs/io2'`, `'ebs/st1'`, `'ebs/sc1'`. Only applicable when `diskAllocation` is `'task'`.
1473-
1474-
`seqera.executor.machineRequirement.diskThroughputMiBps`
1475-
: The throughput in MiB/s for gp3 volumes (125-1000). Default: `325` (Fusion recommended). Only applicable when `diskAllocation` is `'task'`.
1465+
`seqera.executor.machineRequirement.diskEncrypted`
1466+
: Enable KMS encryption for the EBS volume (default: `false`). Only applicable when `diskAllocation` is `'task'`.
14761467

14771468
`seqera.executor.machineRequirement.diskIops`
14781469
: The IOPS for io1/io2/gp3 volumes. Required for io1/io2 volume types. Only applicable when `diskAllocation` is `'task'`.
14791470

1480-
`seqera.executor.machineRequirement.diskEncrypted`
1481-
: Enable KMS encryption for the EBS volume (default: `false`). Only applicable when `diskAllocation` is `'task'`.
1482-
14831471
`seqera.executor.machineRequirement.diskMountPath`
14841472
: The container path where the disk is mounted (default: `'/tmp'`). Applicable to all disk allocation strategies.
14851473

1486-
`seqera.executor.taskEnvironment`
1487-
: Custom environment variables to apply to all tasks submitted by the Seqera executor. These are merged with the Fusion environment variables, with Fusion variables taking precedence. For example: `taskEnvironment = [MY_VAR: 'value']`.
1474+
`seqera.executor.machineRequirement.diskThroughputMiBps`
1475+
: The throughput in MiB/s for gp3 volumes (125-1000). Default: `325` (Fusion recommended). Only applicable when `diskAllocation` is `'task'`.
1476+
1477+
`seqera.executor.machineRequirement.diskType`
1478+
: The EBS volume type for task scratch disk. Supported types: `'ebs/gp3'` (default), `'ebs/gp2'`, `'ebs/io1'`, `'ebs/io2'`, `'ebs/st1'`, `'ebs/sc1'`. Only applicable when `diskAllocation` is `'task'`.
1479+
1480+
`seqera.executor.machineRequirement.machineTypes`
1481+
: List of acceptable EC2 instance families. For example, `['m5', 'c5', 'r5']`.
1482+
1483+
`seqera.executor.machineRequirement.maxSpotAttempts`
1484+
: The maximum number of spot retry attempts before falling back to on-demand. Only used when `provisioning` is `'spot'` or `'spotFirst'`.
1485+
1486+
`seqera.executor.machineRequirement.provisioning`
1487+
: The instance provisioning mode. Can be `'spot'`, `'ondemand'`, or `'spotFirst'`.
14881488

14891489
`seqera.executor.retryPolicy.delay`
14901490
: The initial delay when a failing HTTP request is retried (default: `'450ms'`).

docs/reference/process.md

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -840,6 +840,37 @@ The above example produces:
840840
[4, D]
841841
```
842842

843+
(process-hints)=
844+
845+
### hints
846+
847+
The `hints` directive specifies executor-specific hints as key-value pairs. Each executor uses the hints it recognizes and ignores the rest. Hint values can be any raw value (i.e., numbers, strings, booleans, lists, and maps).
848+
849+
Unprefixed keys are available to **every** executor. Any executor that recognizes the key consumes it. Prefixing a key with an executor name (e.g., `awsbatch/...`) restricts the hint to that executor only. For example:
850+
851+
```nextflow
852+
process hello {
853+
hints consumableResources: ['my-license': 1]
854+
855+
script:
856+
"""
857+
your_command --here
858+
"""
859+
}
860+
```
861+
862+
To restrict a hint to a single executor, prefix the key with the executor name:
863+
864+
```nextflow
865+
hints 'awsbatch/consumableResources': ['my-license': 1]
866+
```
867+
868+
When the same hint is provided both unprefixed and with a matching executor prefix, the prefixed form takes precedence for that executor.
869+
870+
Calling `hints` multiple times in a process definition accumulates entries, with later calls overwriting entries for the same key. Setting `hints` via configuration (e.g., in `nextflow.config`) replaces the entire map.
871+
872+
See {ref}`executor-page` to see which hints are recognized by each executor.
873+
843874
(process-label)=
844875

845876
### label

0 commit comments

Comments
 (0)