Why
| All-purpose cluster for jobs | Job cluster / serverless |
|---|---|
| Higher DBU rate | Lower rate (job compute) or pay per use (serverless) |
| Jobs compete with people's notebooks | Isolated per run |
| Library and config drift by hand | Defined in the job, versioned |
| Runs 24/7 if auto-termination is off | Exists only during the run |
1. Find jobs on all-purpose clusters
SELECT j.job_id, j.name, t.task_key, t.existing_cluster_id
FROM system.lakeflow.job_tasks t
JOIN system.lakeflow.jobs j USING (workspace_id, job_id)
WHERE t.existing_cluster_id IS NOT NULL AND t.delete_time IS NULL
QUALIFY ROW_NUMBER() OVER (PARTITION BY t.job_id, t.task_key ORDER BY t.change_time DESC) = 1;
Column availability in system tables changes over time — or list jobs with the CLI and check existing_cluster_id per task.
2. Choose the target per job
| Job | Target |
|---|---|
| SQL/Python/notebook tasks without custom native libraries | Serverless jobs — fast start, no cluster tuning |
| Needs init scripts, specific node types, GPUs, custom Spark configs | Job cluster |
| Many short tasks in one job | Shared job cluster across tasks (job_cluster_key) |
3. Job cluster definition (bundle)
resources:
jobs:
sales_daily:
job_clusters:
- job_cluster_key: main
new_cluster:
spark_version: 15.4.x-scala2.12
node_type_id: Standard_E8ds_v5
autoscale: { min_workers: 2, max_workers: 8 }
policy_id: ${var.job_policy_id}
data_security_mode: SINGLE_USER
tasks:
- task_key: load
job_cluster_key: main
notebook_task: { notebook_path: ../src/load_sales.py }
libraries: [ { whl: /Volumes/ops/libs/wheels/sales_lib-2.1.0-py3-none-any.whl } ]
timeout_seconds: 7200
max_concurrent_runs: 1
4. Serverless version
tasks:
- task_key: load
notebook_task: { notebook_path: ../src/load_sales.py }
environment_key: default
environments:
- environment_key: default
spec:
client: "1"
dependencies: [ "/Volumes/ops/libs/wheels/sales_lib-2.1.0-py3-none-any.whl", "openpyxl==3.1.5" ]
Serverless jobs don't support everything (for example init scripts, some Spark configs, RDD APIs, certain languages). Check limitations and test each job.
5. Things that break during the move
- Code relying on state left on the interactive cluster (cached tables, temp views, files on local disk, libraries installed by someone weeks ago).
spark.confvalues set in the cluster UI — move them into the job/cluster definition.- Startup time: job clusters take minutes to start; chain tasks on one job cluster or use pools/serverless.
6. Compare and switch
Run the job on the new compute in a test schedule; compare runtime, DBUs (from system.billing.usage by job_id) and output. Then switch the production schedule and remove the all-purpose cluster's job access.
Tip
A cluster policy for job clusters (node types, max workers, tags) keeps the new setup from drifting into the old one.
Checklist
- Inventory of jobs using all-purpose clusters
- Target chosen per job; job specs in bundles
- Hidden dependencies on interactive clusters removed
- Timeouts, concurrency, policies set
- Cost/runtime compared; all-purpose clusters reserved for people