Katkat

Debug Missing Output from a Python Multiprocessing Pool

SpacexSoftware Engineer · Technical Screen

Last updated

Diagnose and fix issues in a Python multiprocessing script where asynchronous workers fail to produce output, addressing pool lifecycle management, exception propagation, and centralized file I/O.

Problem Description

A telemetry ingestion script processes batches of raw sensor readings using Python's multiprocessing.Pool. However, the engineers notice that the final output file is frequently empty, the script occasionally terminates without writing all records, and errors in worker tasks silently disappear without failing the job.

Below is the buggy implementation provided by a colleague:

import json
from multiprocessing import Pool


def parse_chunk(chunk):
    processed = []
    for record in chunk:
        if record.get("corrupt"):
            raise ValueError(f"Invalid sensor packet: {record['id']}")
        processed.append({"id": record["id"], "calibrated": record["val"] * 1.5})
    return processed


def run_pipeline(chunks, output_path):
    pool = Pool(processes=4)
    results = []

    for chunk in chunks:
        # Asynchronous dispatch
        pool.apply_async(parse_chunk, args=(chunk,))

    # Attempting cleanup
    pool.join()
    pool.close()

    with open(output_path, "w") as f:
        json.dump(results, f)

Identify all defects preventing this script from reliably producing output and implement a corrected version of run_pipeline(chunks, output_path).

Requirements

  1. Result Collection & Lifecycle Order: Correctly store and wait on task execution. pool.close() must be called before pool.join(), or use an equivalent safe context manager. Collect the return value of each chunk.
  2. Exception Propagation: If any chunk contains corrupted data (raising a ValueError), the error must surface to the caller rather than failing silently.
  3. Centralized I/O: Workers should only perform compute. The parent process must aggregate all parsed records and write a single JSON array to output_path.
  4. Process Guarding: Ensure module structure is safe for platforms where process spawning is the default (e.g., macOS and Windows).

Function Signature

def run_pipeline(chunks: list[list[dict]], output_path: str) -> int:
    """Processes chunks of telemetry in parallel, writes aggregated records to output_path,

    and returns the total count of written records.
    """
    pass

Example

chunks = [
    [{"id": 1, "val": 10.0}, {"id": 2, "val": 20.0}],
    [{"id": 3, "val": 30.0}],
]
output_path = "calibrated_telemetry.json"

total_records = run_pipeline(chunks, output_path)
# Output in calibrated_telemetry.json:
# [
#   {"id": 1, "calibrated": 15.0},
#   {"id": 2, "calibrated": 30.0},
#   {"id": 3, "calibrated": 45.0}
# ]
# Returns: 3

Edge Cases

  • chunks is empty: Write an empty JSON list [] to output_path and return 0.
  • Worker raises an exception: The parent process must re-raise the exception, and the output file should not be written.
  • Single chunk containing zero records: Correctly handled without errors.

Hints

Hint 1

`pool.apply_async()` returns an `AsyncResult` object; you must hold references to these objects and call `.get()` to retrieve the result and bubble up worker exceptions.

Hint 2

`pool.join()` waits for workers to finish, but workers will not finish until you tell the pool no more tasks are coming via `pool.close()`.

Hint 3

Using `with Pool(...) as pool:` or `pool.map()` / `pool.imap()` often avoids manual `close()` and `join()` ordering issues while simplifying result gathering.

Follow-up questions

  • How would you modify the pipeline if `chunks` is a stream containing millions of items and storing all intermediate results in memory would trigger an OOM error?
  • If one worker task crashes due to a SIGSEGV or out-of-memory kill rather than a standard Python exception, how does `multiprocessing.Pool` behave and how can you handle it?
  • What are the trade-offs between using `multiprocessing.Pool.imap` versus `concurrent.futures.ProcessPoolExecutor` in modern Python?