DatabricksZero to Advanced

Run your work on a schedule, in order, and know when it breaks. Lakeflow Jobs is the built-in orchestrator: a job is a set of tasks, run by a trigger.

  • Product name: Lakeflow Jobs (earlier "Databricks Workflows" / "Jobs")
  • Free Edition: serverless compute only, and a cap of 5 concurrent job tasks per account. Keep demo jobs small
  • Create the three small notebooks below once. Topics 2–13 reuse them as tasks

ingest notebook: loads a batch for a given date:

Python
from pyspark.sql import functions as F

dbutils.widgets.text("run_date", "2025-01-01")
run_date = dbutils.widgets.get("run_date")

catalog = spark.sql("SELECT current_catalog()").first()[0]
df = (spark.table(f"{catalog}.dbx_course.orders")
           .filter(F.to_date("order_ts") == run_date))
df.write.mode("overwrite").saveAsTable(f"{catalog}.dbx_course.job_orders_stage")

n = df.count()
dbutils.jobs.taskValues.set(key="row_count", value=n)
print(f"{run_date}: {n} rows")

transform notebook: merges the staged rows into a target:

Python
catalog = spark.sql("SELECT current_catalog()").first()[0]
spark.sql(f"CREATE TABLE IF NOT EXISTS {catalog}.dbx_course.job_orders_final AS "
          f"SELECT * FROM {catalog}.dbx_course.job_orders_stage WHERE 1 = 0")
spark.sql(f"""
  MERGE INTO {catalog}.dbx_course.job_orders_final t
  USING {catalog}.dbx_course.job_orders_stage s ON t.order_id = s.order_id
  WHEN MATCHED THEN UPDATE SET *
  WHEN NOT MATCHED THEN INSERT *
""")

report notebook: a last step that prints a summary:

Python
catalog = spark.sql("SELECT current_catalog()").first()[0]
print(spark.table(f"{catalog}.dbx_course.job_orders_final").count())
10.01Concept

Introduction to Lakeflow Jobs

By the end: say what Lakeflow Jobs is and how it relates to pipelines.

Triggerschedule, file, tableJobTasksnotebook, script, pipeline, SQLResultstables, logs, alerts
A job is the unit you schedule, run and monitor.
  • Job = tasks + dependencies + trigger + parameters + notifications
  • Task = one unit of work: notebook, Python script, wheel, SQL, pipeline, dbt, and more
  • Trigger = what starts the job: a schedule, a file, a table update, or manual
  • Jobs orchestrate. A pipeline (Module 9) is something a job can run as one task
Lakeflow Jobs Pipeline
Does Runs and orders tasks Maintains datasets declaratively
Fits Any workload and mixed steps Layered ETL tables

Remember

Jobs decide when and in what order. Tasks do the actual work.

10.02Hands-on on Free Edition

Jobs and Tasks

By the end: create a job with a single notebook task and run it.

Create it:

  1. Jobs & Pipelines → Create → Job
  2. Task name ingest. Type Notebook. Pick the ingest notebook
  3. Compute: Serverless (the only option on Free Edition)
  4. Run now, then open the run
Part Where
Task graph Tasks tab
Job settings (trigger, parameters, notifications) Job details pane
Run history Runs tab
  • Each run has an ID, a state and a duration. A task's output and logs are one click away
  • Name tasks for what they do (ingest, transform): names are used in references like {{tasks.ingest.values.row_count}}
  • Develop in the notebook, test interactively, then run it as a job. Behaviour should match

Interactive for building, jobs for running

Anything that must run regularly runs as a job, not as a notebook you start by hand.

10.03Hands-on on Free Edition

Task Dependencies and DAGs

By the end: chain tasks into a DAG with dependencies.

ingesttransformreport
Each arrow is a dependency: a task starts when the ones before it finish.

Add tasks: Add task, then Depends on → pick the upstream task.

Dependency choice Result
One upstream Runs after it
Several upstream Runs after all of them (default)
No dependency Starts immediately, in parallel
  • The tasks form a DAG: directed, no cycles
  • Tasks with no link between them run in parallel (within the compute and the 5-task cap)
  • Write tasks so each one can run again safely: MERGE and overwrite, not blind appends
10.04Hands-on on Free Edition

Scheduling and Triggers

By the end: start a job automatically with schedules and event triggers.

Add a trigger: Job details → Add trigger.

Trigger Starts a run when
Scheduled A time is reached (simple or cron)
File arrival New files land in a monitored Unity Catalog location
Table update A source table changes
Continuous The last run ends: the job keeps running
None You press Run now, or an API or Airflow calls it
  • Cron uses Quartz syntax: 0 0 6 * * ? = every day at 06:00
  • Pick a time zone. In a daylight-saving zone, hourly jobs can skip or double at the change. Use UTC for strict hourly timing
  • Scheduling is not low-latency: runs can start a few minutes late
  • Pausing a trigger stops new runs. Active runs finish
  • Default max concurrent runs is 1: a run is skipped (or queued, if queueing is on) while another is active

Free Edition

Schedules and manual runs are fine. File arrival and table triggers depend on Unity Catalog storage locations, which Free Edition restricts. Check Add trigger to see what is offered.

10.05Hands-on on Free Edition

Notebook and Python Tasks

By the end: choose the right task type for notebooks and Python scripts.

Task type Use for Parameters arrive as
Notebook Exploration-friendly steps Widgets: dbutils.widgets.get("name")
Python script A .py file in a workspace or Git folder Command-line arguments
Python wheel Packaged code with an entry point Keyword arguments
SQL Queries, dashboards, alerts Named parameters :name
Pipeline Run a Lakeflow pipeline Pipeline parameters
Python
# Python script task: read arguments from the command line
import sys
run_date = sys.argv[1]
  • Notebook tasks are quickest to start. Script and wheel tasks are better for tested, versioned code (Module 26)
  • Source can be the workspace or a Git repository and branch/tag (Module 15, 26)
  • Dependencies for serverless tasks go in the task's environment
  • A notebook run as a job needs defaults only for interactive use: the default can hide a missing job parameter
10.06Hands-on on Free Edition

Pipeline Tasks

By the end: run a pipeline as a task in a job.

  • Add task → Pipeline → choose your pipeline (Module 9)
  • A pipeline task starts an update and waits for it. It succeeds or fails with the update
  • Put it between other tasks: ingest files → pipeline → report
land_filesPipeline updatebronze to goldnotify
The job handles the steps around the pipeline.
  • Pass values with the task's Parameters box using dynamic references (next topic)
  • Full refresh is an option on the task. Leave it off for routine runs
  • Job-run pipelines are retried and restarted on recoverable errors, unlike interactive UI runs
  • Pipeline tasks are not parameterised per backfill slice: for history use a one-time append flow (Module 9.7)

Schedule the job, not the pipeline

Trigger the job and let the pipeline be one task. You get ordering, alerts and retries in one place.

10.07Hands-on on Free Edition

Job Parameters and Dynamic Values

By the end: pass parameters into tasks and between tasks with dynamic values.

Job parameters, set under Job details → Parameters:

Key Default
run_date {{job.start_time.iso_date}}

Reference them in a task's parameters:

Syntax Gives
{{job.parameters.run_date}} A job parameter
{{job.id}}, {{job.run_id}}, {{job.name}} Job and run identity
{{job.start_time.iso_date}} Run start date (UTC)
{{job.trigger.type}} periodic, one_time, file_arrival, table, continuous...
{{task.name}}, {{task.run_id}} The current task
{{tasks.ingest.values.row_count}} A value set by another task
{{tasks.ingest.result_state}} success, failed, ...

Task values: set in one task, read by another:

Python
# in ingest
dbutils.jobs.taskValues.set(key="row_count", value=n)
  • Give the transform task the parameter rows = {{tasks.ingest.values.row_count}}, then read it with dbutils.widgets.get("rows")
  • Databricks recommends the reference syntax over taskValues.get: renaming a task won't break it
  • A task value must be JSON and at most 48 KiB
  • Parameters are strings in widgets: cast them
  • A job parameter wins over a task parameter with the same key

The date is a parameter

Pass the run date in, don't compute it inside the notebook. Then a rerun or backfill is just another value.

10.08Hands-on on Free Edition

Conditional Execution

By the end: branch a job with if/else and run-if conditions.

If/else task: runs one branch based on a condition:

  1. Add task → If/else condition named has_rows
  2. Condition: {{tasks.ingest.values.row_count}} > 0
  3. Set transform to depend on has_rows (true)
ingesthas_rowsrow_count > 0truetransform
With zero rows, the true branch is skipped.
  • Try it: today's date has no orders, so the true branch is skipped. Run with run_date = a 2025 date and it runs
  • Operators: ==, !=, >, >=, <, <=
  • == and != compare strings (12.0 == 12 is false). The others compare numbers
  • Values must be number, string or boolean. A boolean true becomes the string "true"

Run if (set per task) reacts to upstream status:

Run if Runs when
All succeeded (default) Every upstream task succeeded
At least one succeeded Some upstream task succeeded
None failed No upstream failure
All done Upstream finished, whatever the outcome
At least one failed Something upstream failed
All failed Everything upstream failed
  • Use At least one failed or All done for cleanup and alert tasks
  • A skipped (Excluded) upstream counts as success for run-if
10.09Hands-on on Free Edition

Retries, Timeouts, and Notifications

By the end: make jobs resilient with retries and timeouts, and tell people when they fail.

Per task: Retries (how many, wait between attempts, retry on timeout) and Timeout.

Per job: Timeout under run duration, an expected-duration warning, and Notifications.

Setting Use
Task retries Transient failures: network, quota, a busy cluster
Task timeout Stop a task that hangs
Job timeout Cap a whole run: status becomes Timed Out
Duration warning Alert when slow, without stopping
Notifications Start, success, failure, duration warning
  • Notify by email. Slack, Teams, PagerDuty and webhooks need an admin-set destination
  • Job-level notifications are not sent while a failed task is retrying. Use task-level ones for every failure, or mute until the last retry
  • Set max concurrent runs deliberately. Extra runs are skipped, or queued (up to 48 hours) when queueing is on

Retries re-run the whole task

A retried task starts from the beginning. If it wrote partial output, it can duplicate it. Idempotent writes (MERGE, overwrite) make retries safe.

Free Edition

Email works. System destinations for Slack or webhooks may not be available.

10.10Hands-on on Free Edition

Parallel Task Execution

By the end: run independent tasks and loops in parallel.

ingestclean_ordersparalleljoin_report
Branches with no link between them run at the same time.
  • Several tasks that depend on the same upstream task start together
  • A downstream task with several dependencies waits for all (default) or follows its Run if rule
  • For each repeats a task over a list:
JSON
[{"table": "orders"}, {"table": "customers"}, {"table": "products"}]
  • Nested task parameter: {{input.table}}. The list can come from a task value or a job parameter
  • Concurrency controls how many iterations run at once (default 1)
  • A For each cannot contain another For each
  • Free Edition limit: 5 concurrent tasks in the account. More wait

Parallel where it's independent

Fan out per table or per date, then join. Don't chain tasks that don't depend on each other.

10.11Hands-on on Free Edition

Job Monitoring

By the end: watch runs, find the failing task and read its output.

  • Jobs & Pipelines → Runs: all recent runs, filter by status, job, time
  • Matrix view on a job: one column per run, one cell per task. Green success, red failure, pink skipped, yellow waiting to retry
  • Run details: Graph, Timeline and List views. Click a task for output, logs, code and metrics
  • The Timeline shows slow tasks and where branches overlap
  • A run's result depends on its leaf tasks (those with nothing after them)
SQL
-- account-wide run records, if you can read the lakeflow system schema
SHOW TABLES IN system.lakeflow;
  • Run history is kept for up to 60 days: export what you need to keep
  • Add your own logging with print/logging, and write audit rows (run id, counts) to a Delta table

System tables

system.lakeflow access depends on your workspace. It is not confirmed on Free Edition.

10.12Hands-on on Free Edition

Failure Recovery

By the end: repair a failed run without starting over.

  • Open the failed run → Repair run
  • Only failed tasks and the ones downstream of them run again. Successful tasks are kept
  • You can change parameters in the repair dialog. Settings changes you made first also apply
  • Works for jobs with two or more tasks. For a single task, just Run now
  • The run keeps one record, with a column for each repair in the matrix view
ingestok, kepttransformfailedreportskipped
Repair reruns `transform` and `report`, not `ingest`.
  • Practice: make transform fail (rename the staging table), run, fix it, Repair run
  • Repair reruns each task from its start: tasks must be idempotent

Disabled tasks

Repair decides by each task's run state, not by whether it is disabled.

10.13Hands-on on Free Edition

Backfills and Reruns

By the end: rerun past dates with a backfill and keep reruns safe.

Run backfill: next to Run now → Run backfill.

Step Value
Date range 2025-01-01 to 2025-01-10
Interval 1 day
Parameter override run_date = {{backfill.iso_datetime}}
  • Databricks creates one run per interval, labelled Backfill
  • Max 100 runs per backfill: use a bigger interval or several backfills
  • Always runs the whole job: you can't pick tasks
  • Raise max concurrent runs to run slices in parallel. The default is 1
  • The job must read the date parameter and process only that slice
Python
# use the date in the task, never "today"
run_date = dbutils.widgets.get("run_date")[:10]     # keep YYYY-MM-DD
  • Use MERGE or overwrite-by-range (Module 8.13) so that the same slice twice gives the same table

Pipelines

Pipeline tasks aren't parameterised per slice and can't run concurrently. Backfill pipelines with a one-time append flow.

10.14Concept

Airflow Integration

By the end: decide how Airflow fits with Lakeflow Jobs.

Airflowschedules and orders tasksruns Databricks jobs through the providerLakeflow Jobsruns the Spark workbuilt-in triggers and monitoring
  • Two ways to combine them: Airflow triggers an existing Databricks job, or Airflow submits one-time runs
  • If everything lives in Databricks, Lakeflow Jobs alone is simpler
  • Use Airflow when a company standard or cross-system dependencies need it
  • Jobs appear in the Databricks Runs tab even when Airflow started them
  • Keep the schedule and the retries in one layer
  • The hands-on version is Module 28

Remember

One scheduler per job. Airflow or Lakeflow's own trigger: not both.

Recap

What you can do now

  • Jobs, tasks, DAGs, triggers and task types
  • Parameters, task values, dynamic references
  • If/else, run-if, retries, timeouts, notifications
  • Monitoring, repair runs and backfills
  • Where Airflow fits