Replace sequential blocking API calls with a bounded thread pool while preserving each result's input association and return order. Define worker limits, timeout and failure semantics, cancellation, cleanup, client safety, and tests for out-of-order completion.
## Parallelize Blocking API Calls with a Thread Pool
An existing function loops over a list of inputs and calls a blocking API once per input. Replace the sequential loop with a fixed-size thread pool while preserving the result associated with every input.
Assume calls are independent and the API client is safe to use concurrently. Define a function such as:
```text
call_all(inputs, max_workers) -> results
```
Return results in the same order as `inputs`, even when requests finish in a different order. Explain the failure behavior you choose and provide code or precise pseudocode that can be executed and tested.
### Constraints & Assumptions
- `max_workers` is positive and no more than the number of calls that the dependency permits concurrently.
- An individual call can succeed, raise an error, or exceed its timeout.
- The caller may cancel the overall operation.
- Do not hold a shared lock while waiting for the external API.
- State whether one failure cancels the batch or is returned as a per-input result.
### Part 1 — Submit Work and Preserve Ordering
Show how the pool is created, how tasks are submitted, and how completion is mapped back to input positions.
#### What This Part Should Cover
- A bounded number of worker threads rather than one thread per input.
- An index or future mapping that separates completion order from output order.
- Handling of empty input and a worker count larger than the input size.
- Guaranteed pool shutdown on success and failure.
```hint Keep identity outside completion order
Store the input position with each submitted task so a fast later call does not move ahead in the returned array.
```
### Part 2 — Handle Timeouts, Errors, and Cancellation
Define the observable outcome when one call fails or hangs. Explain how queued work, running work, and completed work are treated when the caller cancels.
#### What This Part Should Cover
- Per-call timeout behavior and structured error reporting.
- The difference between cancelling queued work and interrupting a running blocking call.
- Cleanup in a `finally` or scope-managed path.
- No retry unless retry safety and idempotency are established.
```hint A future is not the remote request
Cancelling work that has not started is different from stopping a network call already executing in a worker.
```
### What a Strong Answer Covers
- Provides runnable control flow with a fixed concurrency bound and deterministic result order.
- Makes failure and cancellation semantics explicit rather than losing worker exceptions.
- Shuts down the executor without leaking threads.
- Distinguishes thread-pool parallelism for blocking I/O from CPU-bound scaling.
### Follow-up Questions
1. How would you stream completed results if input-order return were no longer required?
2. What changes if the API client is not thread-safe?
3. How would a dependency rate limit affect `max_workers` and retries?
4. Why might a process pool be preferable for CPU-bound work but unnecessary here?
Quick Answer: Replace sequential blocking API calls with a bounded thread pool while preserving each result's input association and return order. Define worker limits, timeout and failure semantics, cancellation, cleanup, client safety, and tests for out-of-order completion.
An existing function loops over a list of inputs and calls a blocking API once per input. Replace the sequential loop with a fixed-size thread pool while preserving the result associated with every input.
Assume calls are independent and the API client is safe to use concurrently. Define a function such as:
call_all(inputs, max_workers) -> results
Return results in the same order as inputs, even when requests finish in a different order. Explain the failure behavior you choose and provide code or precise pseudocode that can be executed and tested.
Constraints & Assumptions
max_workers
is positive and no more than the number of calls that the dependency permits concurrently.
An individual call can succeed, raise an error, or exceed its timeout.
The caller may cancel the overall operation.
Do not hold a shared lock while waiting for the external API.
State whether one failure cancels the batch or is returned as a per-input result.
Part 1 — Submit Work and Preserve Ordering
Show how the pool is created, how tasks are submitted, and how completion is mapped back to input positions.
What This Part Should Cover Guidance
A bounded number of worker threads rather than one thread per input.
An index or future mapping that separates completion order from output order.
Handling of empty input and a worker count larger than the input size.
Guaranteed pool shutdown on success and failure.
Part 2 — Handle Timeouts, Errors, and Cancellation
Define the observable outcome when one call fails or hangs. Explain how queued work, running work, and completed work are treated when the caller cancels.
What This Part Should Cover Guidance
Per-call timeout behavior and structured error reporting.
The difference between cancelling queued work and interrupting a running blocking call.
Cleanup in a
finally
or scope-managed path.
No retry unless retry safety and idempotency are established.
What a Strong Answer Covers Guidance
Provides runnable control flow with a fixed concurrency bound and deterministic result order.
Makes failure and cancellation semantics explicit rather than losing worker exceptions.
Shuts down the executor without leaking threads.
Distinguishes thread-pool parallelism for blocking I/O from CPU-bound scaling.
Follow-up Questions Guidance
How would you stream completed results if input-order return were no longer required?
What changes if the API client is not thread-safe?
How would a dependency rate limit affect
max_workers
and retries?
Why might a process pool be preferable for CPU-bound work but unnecessary here?