Anti-Pattern: ETL on All-Purpose Because It’s Easier to Debug
Estimated read: 3 min
Here’s how the anti-pattern actually happens, and it’s rarely negligence: an engineer builds a new pipeline by attaching a notebook to an all-purpose cluster, because that’s the natural way to iterate – run a cell, see the output, fix a bug, run it again, all in seconds. The pipeline gets scheduled once it works. Nobody goes back and changes the compute, because the notebook is already pointed at a specific cluster ID, that cluster is already warm, and touching a working production pipeline to save on infrastructure cost feels like unnecessary risk for a benefit nobody’s tracking closely. Eighteen months later, a dozen scheduled ETL jobs are quietly running nightly on always-on all-purpose clusters, at the higher interactive-compute rate, for work that has no interactive user attached to it at 2 a.m.
The habit traces straight back to legacy ETL tooling: a Talend or Informatica job ran on a fixed server whose cost didn’t change based on whether a human was watching it, so there was never a reason to distinguish “compute for debugging” from “compute for the scheduled run” – they were the same box either way. Databricks charges differently for the two, which is exactly the arbitrage from two lectures back, and this anti-pattern is what happens when that distinction never gets applied to work that was built before anyone was thinking about it.
The real objection engineers raise isn’t inertia, though – it’s debuggability. When a scheduled job fails on jobs compute, the cluster that ran it has already terminated; there’s no notebook to attach to and poke at interactively. That objection is solvable without paying the all-purpose rate for every successful run:
- Develop on all-purpose, deploy on jobs compute. Keep the interactive cluster for the iterate-and-fix workflow during development; point the scheduled job definition at a jobs-compute cluster spec, not the developer’s existing cluster.
- Use the job run’s retained output for debugging, not a live cluster. A completed or failed job run keeps its driver logs, Spark UI, and task output accessible after the ephemeral cluster terminates – most failure diagnosis (a stack trace, a bad partition, a schema mismatch) doesn’t actually require an attached, running cluster.
- Use Repair Run for iteration on a real failure. Rather than re-running an entire multi-task job from scratch on an all-purpose cluster to reproduce one failing task, Lakeflow Jobs can repair-and-rerun just the failed task with the same parameters, on fresh jobs compute.
- Enforce it with the cluster policy from the last lecture, so this isn’t a discipline problem for every individual pipeline author to solve correctly – a service principal that can only create job-type clusters can’t accidentally regress into this anti-pattern.
The dollar impact is the reason this lecture exists: moving a scheduled pipeline from an all-purpose cluster to jobs compute of the same instance type, with no code change and no loss of debuggability once the workflow above is in place, is a roughly 2.7x reduction in that pipeline’s compute billing – not from doing less work, but from paying the correct rate for unattended work instead of the interactive rate it never needed.
“It’s easier to debug on all-purpose” is true for the ten minutes an engineer is actively fixing something and false for the other 719 hours in a month the cluster would otherwise sit there running the same nightly job. Solve for the ten minutes with retained job-run logs and Repair Run, not by leaving production compute permanently priced for interactive debugging.
| ← Previous: The Photon Spill | Next: Check Your Knowledge → | |:—|—:|