Why should an orchestrator trigger transformation work on external compute rather than run it itself?
answer
- who calls for the work versus who does it
- the scheduler's memory is not a data plane
- one greedy task, many dead neighbours
- submit, poll, record the outcome
basics
~20 sOrchestrator workers are sized for coordination, not data. Pulling a dataset into a worker makes its memory the pipeline's ceiling, lets one heavy task starve co-located tasks, wastes the target engine's parallelism, and makes every retry replay the whole transfer.
solid answer
~50 sAn orchestrator's job is to decide *what* runs, *when*, and *in what order*, and to record the outcome. The moment a task loads a dataset into the worker process, the scheduler becomes a data plane it was never sized to be. Its memory caps the largest dataset the pipeline can handle; one oversized task can exhaust a shared worker and kill unrelated tasks running beside it; the target engine's parallelism goes unused while a single process does the work serially; a routine restart or deployment loses hours of in-flight progress; and every retry repeats the entire transfer instead of re-issuing a statement. The pattern that scales is submit-and-poll: the task issues an instruction to the system that owns the compute, waits on its status, and reports success or failure. Small control-plane work — issuing a statement, calling an API, moving kilobytes of metadata — legitimately runs in the worker; volume does not.
code
text · 3 linesanti-pattern: worker: SELECT * -> rows in memory -> loop -> INSERT back
pattern: worker: submit statement -> poll status -> record result
warehouse: reads, joins, aggregates, writesgo deeper
Recall that the orchestrator decides what runs and in what order, while the warehouse or processing engine does the actual data work — a task should ask for work, not perform it.
Explain the mechanics: worker memory becomes the pipeline's ceiling, co-located tasks die together under an out-of-memory kill, and the target's parallelism goes unused while one process loops over rows.
Show the operating consequences you have lived with — blast radius across unrelated pipelines, retries that replay whole transfers, deployments that cannot be done safely — and describe the submit-and-poll shape you would enforce.
Own the boundary as a platform rule: the control plane must be restartable at any moment, so no pipeline may make orchestration capacity a function of data volume, and reviews should reject designs that do.
## Control plane and data plane An orchestrator is a **control plane**. It holds the dependency graph, decides what is eligible to run, enforces concurrency limits, retries what failed, and records what happened. The systems that hold and process data — the warehouse, the processing cluster, the streaming engine — are the **data plane**. Keeping those roles separate is the single most consequential design habit in pipeline engineering, and violating it is the most common way a working pipeline becomes an unworkable one. The violation always looks reasonable at the time. A task reads a query result into memory, loops over it, and writes rows back. It works on a laptop against a small table. It works in production for six months. Then the table grows. ## What breaks, in the order you meet it **Memory becomes the ceiling.** The largest dataset the pipeline can process is now whatever fits in one worker process. That limit has nothing to do with the capability of the warehouse holding the data, and it moves only when someone pays for bigger scheduler infrastructure — infrastructure that sits mostly idle, because coordination needs almost nothing. **Failures spread sideways.** Orchestrator workers typically run several tasks concurrently. A task that allocates too much triggers the operating system's out-of-memory killer, which takes down the process and every unrelated task sharing it. A slow database load in one pipeline kills a report in another. The blast radius of a data problem becomes the whole scheduler. **The target's parallelism is wasted.** A warehouse asked to run an aggregation splits it across many workers and reads only the columns needed. The same logic implemented as a Python loop over fetched rows runs on one core, serially, having transferred every column across the network first. It is slower by orders of magnitude at exactly the workload the warehouse is best at. **Retries become expensive and unsafe.** If the work lives in the worker, a retry re-reads and re-processes everything from the start, and any rows already written may be written again. If the work lives in the target, the retry is one statement — and a statement that overwrites a partition or merges on a key is naturally idempotent. **Operations get coupled to data volume.** Orchestrator upgrades, worker restarts, node rotations and deployments all become risky, because they can kill hours of in-flight processing. A control plane should be restartable at any moment; one that holds work in progress is not. **Observability degrades.** When the warehouse executes the statement, its query history holds the plan, the bytes scanned, the duration and the cost, and the orchestrator holds the run record and the dependency edges. Each system reports what it knows. When the worker does the work, the only artefact is a task log, and diagnosing slowness means adding print statements. ## The pattern that scales: submit and poll A well-behaved transformation task does four things: submit an instruction to the system that owns the compute; record the identifier it returns; poll or wait for terminal status; translate that status into task success or failure, carrying back the error and a link to the external run. No rows pass through the worker. The task's memory usage is constant whether the table has a thousand rows or a trillion. This also fixes the semantics of a timeout. Cancelling a task that holds the work in-process leaves the target's state unknown; cancelling a submit-and-poll task can explicitly cancel the remote job, or deliberately leave it running and reattach on the next attempt. ## The legitimate exceptions This is a rule about volume, not about doing nothing. Perfectly reasonable work inside a worker: issuing SQL and waiting; calling an API to start a job; reading a manifest or a schema; passing small metadata such as a partition key or a row count between tasks; sending a notification; evaluating a branch condition. The heuristic is that anything crossing the worker should be small, bounded and known — configuration and control signals, not payload. The boundary case worth naming is the small file. Moving a few megabytes through a worker is fine and often simplest. What is not fine is a design whose correctness depends on the payload staying small, with no check and no alternative when it does not. If the volume is unbounded by nature, route it through a system built for volume from the start. ## Why interviewers ask this Because it separates people who have operated a scheduler from people who have only written tasks. The candidate who has been paged because one report's memory spike killed a night's worth of unrelated pipelines never designs it that way again — and can explain, concretely, what the orchestrator should have been doing instead.
- What work is it still acceptable to run inside an orchestrator worker?Bounded control-plane work: issuing a statement and waiting on it, calling an API to launch a job, reading a manifest or schema, passing small metadata such as a partition key or row count between tasks, evaluating a branch condition, sending notifications. The test is that whatever crosses the worker is small, bounded and known — configuration, not payload.
- How should a task behave when it times out waiting on an external job?Decide deliberately between cancelling the remote job and leaving it running. Cancel when partial work is unsafe or expensive to leave behind; leave it and reattach by job identifier on the next attempt when the work is long and idempotent. Either way record the external identifier, because a timed-out task that abandons an unknown remote state is the hardest failure to reason about.
- Does this change how you make a transformation task idempotent?Yes, and it helps. When the work is a single statement that overwrites a partition or merges on a key, a retry re-issues one idempotent instruction. When rows stream through the worker, a retry re-reads everything and may re-write rows already written, so idempotency has to be engineered separately with markers or staging tables.
A conductor does not play the instruments. They cue the sections, keep everyone in order and stop the piece when something goes wrong; an orchestrator that processes data is a conductor who put down the baton to play the cello part.
saying these in an interview costs you the question
- Loads whole query results into the scheduler worker
- Fixes memory errors by enlarging orchestrator workers
- Assumes a task crash only affects its own pipeline
- Retries a half-written in-process load with no idempotency
- Cannot say what the orchestrator should record instead