DatabricksZero to Advanced
Course/Level 8 · Small Project: Build It and Run It with Databricks Jobs

Module 27: Run the Project with Databricks Jobs

Run the project as a Databricks job: tasks, parameters, schedule, retries, repair runs, job as code, CI deploy.

7 hands-on2 with caveats9 topics
Topics in this module
  1. Create a Job from the Project
  2. Tasks and Dependencies
  3. Parameters and Dynamic Values
  4. Schedules and Triggers
  5. Retries, Timeouts and Notifications
  6. Running and Monitoring
  7. Repair Runs and Backfills
  8. Defining the Job as Code
  9. Deploying the Job from CI

Run the orders_pipeline wheel from Module 26 as a scheduled, monitored Databricks job. You already know what a job is (Module 10): this module applies it to the project.

  • Needs the wheel from Module 26 topic 7 in /Volumes/<catalog>/dbx_project/artifacts/
  • The UI path (topics 1 to 7) works on Free Edition: serverless compute, up to 5 concurrent tasks
  • Topics 8 and 9 (job as code, deploy from CI) need the CLI and a token: unconfirmed on Free Edition, so read them as reference if you can't create one
  • Use a 2025 date in tests, for example 2025-03-15: the practice data covers 2025
27.01Hands-on on Free Edition

Create a Job from the Project

By the end: create the first task in the Jobs UI from the wheel.

Steps:

  1. Sidebar Jobs & Pipelines → Create → Job. Name it orders-pipeline
  2. Task name load_silver. Type Python wheel
  3. Package name orders_pipeline. Entry point load_silver
  4. Compute: Serverless. Under Environment and libraries, add the wheel path /Volumes/<catalog>/dbx_project/artifacts/orders_pipeline-0.1.0-py3-none-any.whl
  5. Parameters, keyword: catalog = your catalog, schema = dbx_project, run_date = 2025-03-15
  6. Save, then Run now
Python
# check the result in a notebook
spark.sql("SELECT count(*) FROM dbx_project.silver_orders").show()
  • Wheel tasks send parameters as command-line arguments. If the run fails with an argument error, open the run output and compare it with parse_args (Module 26.5)
  • Dependencies for a wheel on serverless go in the environment, not a cluster library list
  • Use a service principal as run-as for production (Module 17). For learning, your own identity is fine
27.02Hands-on on Free Edition

Tasks and Dependencies

By the end: add the other tasks and wire dependencies.

load_silvercheck_qualitybuild_gold
A task starts only when the tasks it depends on have succeeded.
Task Entry point Depends on
load_silver load_silver none
check_quality check_quality load_silver
build_gold build_gold check_quality
  • Add task each time, same wheel, a different entry point. Set Depends on in the task panel
  • A failing check_quality raises (Module 26.9), so build_gold is skipped. Gold never sees bad silver
  • By default a task needs all upstream tasks to succeed. Run if changes that: at least one succeeded, none failed, at least one failed, all done
  • A cleanup or notify task with "at least one failed" is the pattern for a failure hook

Try it: run with a bad quantity row injected into silver_orders after load_silver. See build_gold skipped.

  • Tasks with no dependencies run in parallel (limited to 5 at once on Free Edition)
27.03Hands-on on Free Edition

Parameters and Dynamic Values

By the end: pass job parameters to every task with dynamic values.

Where What
Job parameters catalog, schema, run_date, days: set once for the whole job
Task parameters Reference them: run_date = {{job.parameters.run_date}}
Dynamic values Filled in at run time
Dynamic value Gives
{{job.parameters.run_date}} A job parameter
{{job.start_time.iso_date}} Date the run started (UTC)
{{job.id}} / {{job.run_id}} Ids for logging
{{tasks.<task>.values.<key>}} A task value from an earlier task

Edit each task's parameters to the references:

Text
catalog   = {{job.parameters.catalog}}
schema    = {{job.parameters.schema}}
run_date  = {{job.parameters.run_date}}
days      = {{job.parameters.days}}
  • Set the job parameter run_date empty for scheduled runs: the code uses today (UTC). Backfills and reruns pass an explicit date
  • Parameters are strings: --days is converted by argparse (type=int)
  • If a job parameter and a task parameter have the same key, the job parameter wins
  • Run now with different settings overrides parameters for one run
27.04Hands-on on Free Edition

Schedules and Triggers

By the end: schedule the job and know the other triggers.

Schedule: job page → Schedules & Triggers → Add trigger → Scheduled. Pick a time, a timezone, save.

Trigger Runs when Notes
Scheduled Quartz cron or a simple picker 0 30 6 * * ? = 06:30 every day
File arrival New file in a volume or external location Limits apply: check the docs
Table update A source table changes Good for gold jobs that follow silver
Continuous Always, restarts after each run Not for batch projects
  • Use an explicit timezone. UTC avoids daylight-saving surprises
  • Pause the schedule instead of deleting it while debugging
  • Set max concurrent runs = 1 so a slow run is not overlapped by the next
  • Free Edition: scheduled jobs work. Continuous and large file-arrival setups: check what your workspace allows
27.05Hands-on on Free Edition

Retries, Timeouts and Notifications

By the end: make the job survive failures and tell someone.

Setting Where Suggestion
Retries Per task 2 retries with a delay for load_silver. None for check_quality: a bad row doesn't fix itself
Timeout Per task or job About 2× the normal run time
Duration warning Job notifications Alert if a run is much slower than usual
Failure email Job → Notifications Always set this
Queue Job setting Let a run wait instead of being skipped
  • Retry only for transient causes (network, a busy service). A retry that repeats a code error wastes compute
  • Retries are safe only if the task is idempotent: this project overwrites or replaceWheres, so yes
  • Add yourself to failure notifications. A job that fails silently is worse than no job
  • Email is the simplest on Free Edition. Slack or webhook destinations need to be set up first
27.06Hands-on on Free Edition

Running and Monitoring

By the end: read a run: the matrix, the logs and the history.

Where to look:

Need Open
Did the last runs pass? Job page → Runs tab
Which task failed? The run's graph / matrix view
Why? Click the failed task → output / error. Your log.info lines show here
Which parameters and commit ran? Run details (the task shows the Git commit when a Git source is used)
How long and how much? Duration per task, and query/compute details
SQL
%sql
-- optional: every job run in one table (system tables; availability varies)
SELECT job_id, period_start_time, result_state
FROM system.lakeflow.job_run_timeline
ORDER BY period_start_time DESC
LIMIT 20
  • Run history is kept about 60 days: copy what you need longer into your own ops_run_log (Module 16)
  • Failure in logs of one task, not of the job. Start from the red box
  • Compare a slow run with a fast one: same parameters? same data volume?
27.07Hands-on on Free Edition

Repair Runs and Backfills

By the end: repair a failed run and backfill past dates.

Repair run:

  • Run page → Repair run. It reruns failed and skipped tasks plus their dependents. Successful tasks are not rerun
  • It applies the current job settings: fix the cause first (parameter, wheel version, data)
  • Repair is for jobs with 2 or more tasks. For a one-task job use Run now
  • Databricks does not make tasks idempotent. Your tasks must be: ours overwrite, so a repair can't duplicate

Backfill: run the job for past dates.

Step Detail
Job → Backfill (or Run now with different settings) Pick the date range
Parameter override run_date = {{backfill.iso_datetime}} or an explicit date
Limit A backfill creates up to 100 runs

Try it: run for 2025-03-10 through 2025-03-12, then check:

SQL
%sql
SELECT order_date, orders, revenue
FROM dbx_project.gold_daily_revenue
WHERE order_date BETWEEN '2025-03-10' AND '2025-03-12'
ORDER BY order_date
  • days defaults to 7 so each run rewrites a window. Late data inside that window is picked up automatically
27.08Hands-on with caveats

Defining the Job as Code

By the end: define the job as code in a bundle.

YAML
# databricks.yml
bundle:
  name: orders-pipeline

variables:
  catalog:
    description: Unity Catalog catalog
  schema:
    default: dbx_project

artifacts:
  orders_wheel:
    type: whl
    build: python -m build --wheel
    path: .

resources:
  jobs:
    orders_pipeline:
      name: orders-pipeline-${bundle.target}
      max_concurrent_runs: 1
      parameters:
        - { name: catalog,  default: "${var.catalog}" }
        - { name: schema,   default: "${var.schema}" }
        - { name: run_date, default: "" }
        - { name: days,     default: "7" }
      schedule:
        quartz_cron_expression: "0 30 6 * * ?"
        timezone_id: UTC
        pause_status: PAUSED
      email_notifications:
        on_failure: ["you@example.com"]
      environments:
        - environment_key: default
          spec:
            environment_version: "3"              # copy the current value from `databricks bundle init`
            dependencies:
              - ./dist/*.whl
      tasks:
        - task_key: load_silver
          environment_key: default
          max_retries: 2
          python_wheel_task:
            package_name: orders_pipeline
            entry_point: load_silver
            named_parameters: { catalog: "{{job.parameters.catalog}}", schema: "{{job.parameters.schema}}", run_date: "{{job.parameters.run_date}}", days: "{{job.parameters.days}}" }
        - task_key: check_quality
          depends_on: [{ task_key: load_silver }]
          environment_key: default
          python_wheel_task:
            package_name: orders_pipeline
            entry_point: check_quality
            named_parameters: { catalog: "{{job.parameters.catalog}}", schema: "{{job.parameters.schema}}", run_date: "{{job.parameters.run_date}}", days: "{{job.parameters.days}}" }
        - task_key: build_gold
          depends_on: [{ task_key: check_quality }]
          environment_key: default
          python_wheel_task:
            package_name: orders_pipeline
            entry_point: build_gold
            named_parameters: { catalog: "{{job.parameters.catalog}}", schema: "{{job.parameters.schema}}", run_date: "{{job.parameters.run_date}}", days: "{{job.parameters.days}}" }

targets:
  dev:
    mode: development
    default: true
    variables:
      catalog: workspace            # use the result of SELECT current_catalog()
  prod:
    mode: production
    variables:
      catalog: prod_catalog
    workspace:
      host: https://<prod-workspace-url>
Shell
databricks bundle validate -t dev
databricks bundle deploy -t dev
databricks bundle run orders_pipeline -t dev
  • Development mode prefixes the job name and pauses its schedule: unpause only in prod
  • The wheel is built by the artifacts step and uploaded on deploy: you don't upload it by hand
  • Check environment_version and the wheel dependencies path against a template from databricks bundle init: they change between versions
  • Production mode also wants run_as and permissions: add a service principal (Module 15)
  • Edit in the UI only to experiment. The repo is the source of truth

Free Edition

Bundles and the CLI aren't documented for Free Edition. If you can create a token (Settings → Developer), try topic 8 in dev. Otherwise keep the UI job from topics 1 to 7.

27.09Hands-on with caveats

Deploying the Job from CI

By the end: deploy the job from CI on every merge.

Pull requestTestsMergeBuild wheelDeploy job
The job in prod is whatever `main` last deployed.
YAML
# .github/workflows/deploy.yml
name: deploy
on:
  push:
    branches: [main]
concurrency: deploy-prod
jobs:
  deploy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v5
        with: { python-version: "3.11" }
      - run: pip install build pytest && pip install -e .
      - run: pytest tests/unit
      - uses: databricks/setup-cli@main
      - run: databricks bundle validate -t prod
        env:
          DATABRICKS_HOST: ${{ vars.DATABRICKS_HOST }}
          DATABRICKS_TOKEN: ${{ secrets.SP_TOKEN }}
      - run: databricks bundle deploy -t prod
        env:
          DATABRICKS_HOST: ${{ vars.DATABRICKS_HOST }}
          DATABRICKS_TOKEN: ${{ secrets.SP_TOKEN }}
  • CI uses a service principal token, stored in the CI secrets. Not your personal token
  • Run unit tests without Databricks first (local Spark): they need no secrets
  • Bump version in pyproject.toml per release so the new wheel has a new name
  • Rollback: deploy the previous git tag
  • A smoke run after deploy (databricks bundle run ... -t dev) catches a broken wheel before prod
  • Module 28 triggers this same job from Airflow without changing it
Recap

What you can do now

  • One job, three wheel tasks, dependencies in a line
  • Parameters at job level, passed to tasks with {{job.parameters.x}}
  • Retry what is transient, notify on failure, keep tasks idempotent
  • Repair reruns failed tasks; backfill reruns dates
  • The job definition and the deploy live in Git