ADR-0001: Dask threaded scheduler for pipeline execution

ADR-0001: Dask threaded scheduler for pipeline execution#

Field

Value

Kind

ADR

Status

Accepted

Decided

historical

Deciders

Antoine Berchet

Supersedes

Superseded by

Context#

CIF used to build its pipeline as a home-made dictionary-based tree, walked and executed by hand rather than through a real DAG (directed acyclic graph) engine. That tree became very slow to build as the number of transforms grew — construction cost scaled with the pipeline, not just its execution. A DAG became necessary both to accelerate tree building and to parallelize the pipeline’s embarrassingly parallel tasks, in particular in the GPU context: heavy lifting Fortran kernels run on the GPU while the CPU would otherwise sit idle waiting on them. Dask was chosen as the orchestrator.

With Dask in place, process-based execution (multiprocessing / a worker pool) was tried and turned out to be impractical in CIF’s context: tasks pass xarray / NetCDF-backed objects between stages, and Dask’s process-based schedulers must serialise (pickle) task inputs and outputs to move them across worker processes. Open NetCDF file handles — and related netCDF4 / HDF5 objects — are not reliably picklable, so process-based execution repeatedly broke when such objects crossed task boundaries. Threading, which shares one address space and needs no such serialisation, was the obvious choice.

Decision#

We use Dask’s threaded scheduler for CIF’s pipeline execution. A dask_mode config option (threads / synchronous / processes) still exists and processes is wired up with its own worker-logging initialisation, but threads is the scheduler CIF is designed and validated around; processes remains available rather than actively supported.

Consequences#

  • Tasks share one process and one address space: no per-task serialisation of large arrays, and no attempt to pickle NetCDF handles.

  • Parallelism is bounded by the GIL for pure-Python sections. This is acceptable because the heavy work — numpy kernels, NetCDF / HDF5 I/O, GPU-offloaded Fortran — releases the GIL.

  • Tasks and plugins must be thread-safe and must not rely on process isolation or per-process global state; shared module/global state is visible across concurrent tasks.

  • True multi-process / distributed scaling would require an explicit serialisation boundary (e.g. pass file paths, not open handles) to work around the pickling issue that made pooling impractical here. That is a one-way-door change and would need a new RFC, not an edit to this ADR.