Skip to content

← Parallel Python with Process Pools step 17 of 24

Medium Framework

Plan Worker Recycling for a Process Pool

A library in your workers keeps a little memory after every task, and after a day the machine runs out. Worker recycling limits the damage. With max_tasks_per_child=100, ProcessPoolExecutor replaces each worker after 100 tasks, and the new worker starts without the leaked memory. Each replacement costs one process start.

Recycling also changes the start method. Without an mp_context, a recycling pool uses spawn, usually the slowest method to start, even on Linux. And recycling cannot use fork, so asking for both raises ValueError. Pass the context yourself:

import multiprocessing as mp
from concurrent.futures import ProcessPoolExecutor

pool = ProcessPoolExecutor(
    max_tasks_per_child=100,
    mp_context=mp.get_context("forkserver"),
)
pool.shutdown()

The grader cannot start processes, so you write a planner that picks the method and estimates the cost.

What to write

solve(*, platform, tasks_per_child, mp_context, task_ms) returns (chosen_method, warning_codes, overhead_percent). These are a StartMethod, a tuple of strings in order, and a float rounded to two decimal places.

  • tasks_per_child is a positive integer, or None for no recycling.
  • mp_context is the method the caller asked for, or None.
  • task_ms is the time one task takes in milliseconds. It is above zero.

Use the starter's made-up START_COST_MS and its BUDGET_PCT = 1.0.

Platform Methods it offers Default Recycling choice
win32 spawn spawn spawn
darwin spawn, fork, forkserver spawn forkserver
Any other spawn, fork, forkserver forkserver forkserver

Without recycling, use mp_context if it names a method the platform offers. Otherwise use the platform's default. Return no warnings and 0.0.

With recycling, apply the first rule that matches:

  1. mp_context is None. Use the recycling choice and add "implicit-context-downgrades-to-spawn".
  2. mp_context is "fork". Use the recycling choice and add "fork-incompatible-with-recycling". This rule applies even on Windows.
  3. mp_context is not a method name. Use the recycling choice and add "unknown-context".
  4. The platform does not offer the method, which happens only for "forkserver" on win32. Use the recycling choice and add "unsupported-context".
  5. Otherwise use the requested method, with no context warning.

The implicit-context-downgrades-to-spawn warning means Python would fall back to spawn. The planner returns the method you should pass instead. It adds the warning on Windows too.

Then work out the startup time as a percentage of a worker's task time:

overhead_percent = round(100 * START_COST_MS[chosen_method] / (tasks_per_child * task_ms), 2)

If the rounded value is greater than BUDGET_PCT, add "recycle-overhead-exceeds-budget" after any context warning. A value equal to the budget gets no warning. For forkserver, 100 tasks and 10 ms per task, the result is 0.14.

Write a helper that turns str | None into StartMethod | None, and check what the platform offers separately. Your code must pass mypy --strict.