← Learning Hub Lesson 9 Duration: ~14 min Topic: Production Operations

Workflows & Orchestration

This lesson wires everything together: ingestion, Silver, features, ML inference, and Gold — orchestrated as a single production Workflow. By the end, you'll know how to structure task dependencies, handle failures gracefully, configure retries, and set up alerting for your weather-to-power pipeline.

The Complete Pipeline as a Workflow

Weather-to-Power Workflow — Task Dependency Graph Ingest ECMWF Ingest GFS Ingest ICON Ingest Telemetry Bronze → Silver (weather) Bronze → Silver (telemetry) Compute Features ML Inference Write Gold + Notify

Workflow Configuration (JSON)

Databricks Workflows are defined as JSON (or via UI/Terraform). Here's the structure for your pipeline:[1]

{
  "name": "weather-to-power-pipeline",
  "schedule": {
    "quartz_cron_expression": "0 0 5,11,17,23 * * ?",
    "timezone_id": "UTC"
  },
  "tasks": [
    {
      "task_key": "ingest_ecmwf",
      "notebook_task": { "notebook_path": "/pipelines/ingest_ecmwf" },
      "job_cluster_key": "ingest_cluster",
      "timeout_seconds": 1800,
      "retry_on_timeout": true,
      "max_retries": 2
    },
    {
      "task_key": "ingest_gfs",
      "notebook_task": { "notebook_path": "/pipelines/ingest_gfs" },
      "job_cluster_key": "ingest_cluster",
      "max_retries": 2
    },
    {
      "task_key": "ingest_icon",
      "notebook_task": { "notebook_path": "/pipelines/ingest_icon" },
      "job_cluster_key": "ingest_cluster",
      "max_retries": 2
    },
    {
      "task_key": "ingest_telemetry",
      "notebook_task": { "notebook_path": "/pipelines/ingest_telemetry" },
      "job_cluster_key": "ingest_cluster",
      "max_retries": 2
    },
    {
      "task_key": "silver_weather",
      "depends_on": [
        {"task_key": "ingest_ecmwf"},
        {"task_key": "ingest_gfs"},
        {"task_key": "ingest_icon"}
      ],
      "notebook_task": { "notebook_path": "/pipelines/bronze_to_silver_weather" },
      "job_cluster_key": "transform_cluster"
    },
    {
      "task_key": "silver_telemetry",
      "depends_on": [{"task_key": "ingest_telemetry"}],
      "notebook_task": { "notebook_path": "/pipelines/bronze_to_silver_telemetry" },
      "job_cluster_key": "transform_cluster"
    },
    {
      "task_key": "compute_features",
      "depends_on": [
        {"task_key": "silver_weather"},
        {"task_key": "silver_telemetry"}
      ],
      "notebook_task": { "notebook_path": "/pipelines/compute_features" },
      "job_cluster_key": "transform_cluster"
    },
    {
      "task_key": "ml_inference",
      "depends_on": [{"task_key": "compute_features"}],
      "notebook_task": { "notebook_path": "/pipelines/ml_inference" },
      "job_cluster_key": "ml_cluster"
    },
    {
      "task_key": "write_gold",
      "depends_on": [{"task_key": "ml_inference"}],
      "notebook_task": { "notebook_path": "/pipelines/write_gold" },
      "job_cluster_key": "transform_cluster"
    }
  ],
  "job_clusters": [
    {
      "job_cluster_key": "ingest_cluster",
      "new_cluster": {
        "num_workers": 0,
        "spark_version": "14.3.x-scala2.12",
        "node_type_id": "Standard_E4s_v3",
        "init_scripts": [{"dbfs": {"destination": "dbfs:/init/install_cfgrib.sh"}}]
      }
    },
    {
      "job_cluster_key": "transform_cluster",
      "new_cluster": {
        "num_workers": 2,
        "spark_version": "14.3.x-scala2.12",
        "node_type_id": "Standard_D4s_v3"
      }
    },
    {
      "job_cluster_key": "ml_cluster",
      "new_cluster": {
        "num_workers": 1,
        "spark_version": "14.3.x-ml-scala2.12",
        "node_type_id": "Standard_E8s_v3"
      }
    }
  ],
  "email_notifications": {
    "on_failure": ["team@company.com"],
    "on_duration_warning_threshold_exceeded": ["team@company.com"]
  },
  "health": {
    "rules": [
      {"metric": "RUN_DURATION_SECONDS", "op": "GREATER_THAN", "value": 3600}
    ]
  }
}

Key Design Decisions

1. Cluster reuse across tasks

Tasks that share a job_cluster_key reuse the same cluster — it stays alive between tasks. This saves startup time (~3–5 min per cluster) but means you pay for idle time between sequential tasks.

StrategyWhen
Shared cluster (same job_cluster_key)Sequential tasks with same library requirements
Separate clustersDifferent runtimes needed (e.g., ML vs standard) or heavy parallelism
Single-node (num_workers: 0)I/O-bound tasks like API downloads and GRIB parsing

2. Dependency modes

// Default: ALL upstream must succeed
"depends_on": [{"task_key": "ingest_ecmwf"}, {"task_key": "ingest_gfs"}]

// If one source is optional (e.g., ICON is nice-to-have):
// Use "run_if" condition or separate the optional path
"run_if": "ALL_SUCCESS"  // default
"run_if": "AT_LEAST_ONE_SUCCESS"  // if partial data is acceptable
Partial Success Strategy

Weather sources have different reliability. ECMWF might be critical (pipeline fails without it), while ICON is supplementary. Design dependencies so that: (1) ECMWF failure blocks the pipeline, (2) ICON failure logs a warning but allows the pipeline to continue with ECMWF+GFS data only.

3. Retry policies

{
  "max_retries": 2,            // total 3 attempts
  "min_retry_interval_millis": 60000,  // wait 1 min between retries
  "retry_on_timeout": true     // also retry if task hits timeout
}

Retries make sense for transient failures (API timeout, spot instance preemption). They don't make sense for logic errors (bad code) or data quality failures (should alert, not retry).

Scheduling Strategy

Your pipeline schedule must align with weather model availability:

ModelInit TimesAvailable ByYour Trigger
ECMWF00, 06, 12, 18 UTCinit + 5–6h05, 11, 17, 23 UTC
GFS00, 06, 12, 18 UTCinit + 3.5–4h(same trigger, runs first)
ICON00, 06, 12, 18 UTCinit + 2–3h(same trigger, runs first)
# Cron: 4× daily, 5 hours after model init
# "0 0 5,11,17,23 * * ?" = at 05:00, 11:00, 17:00, 23:00 UTC
"quartz_cron_expression": "0 0 5,11,17,23 * * ?"

Monitoring & Alerting

Built-in Databricks monitoring

Custom pipeline-level checks (final task)

# Final task in workflow: validate pipeline output
from datetime import datetime, timedelta, timezone

def validate_pipeline_output():
    """Assert that Gold table has fresh data after pipeline completes."""
    
    latest_gold = spark.sql("""
        SELECT MAX(produced_at) as latest
        FROM prod.gold.forecast_ts
    """).collect()[0]["latest"]
    
    threshold = datetime.now(timezone.utc) - timedelta(hours=2)
    assert latest_gold >= threshold, \
        f"Gold data stale! Latest: {latest_gold}, expected >= {threshold}"
    
    # Check all parks have forecasts
    parks_with_forecasts = spark.sql("""
        SELECT COUNT(DISTINCT asset_id)
        FROM prod.gold.forecast_ts
        WHERE produced_at > current_timestamp() - INTERVAL 2 HOURS
          AND asset_type = 'wind_park'
    """).collect()[0][0]
    
    expected_parks = spark.sql("""
        SELECT COUNT(*) FROM prod.silver.dim_assets
        WHERE asset_type = 'wind_park'
    """).collect()[0][0]
    
    assert parks_with_forecasts == expected_parks, \
        f"Missing parks! Got {parks_with_forecasts}/{expected_parks}"
    
    print(f"✓ Pipeline validation passed: {parks_with_forecasts} parks forecasted")

Infrastructure as Code

Don't configure Workflows manually in the UI. Use infrastructure-as-code for reproducibility:

ToolWhenDatabricks Support
Databricks Asset Bundles (DABs)Standard choice for Databricks-native projectsFirst-party, YAML-based, includes CI/CD templates
TerraformMulti-cloud, existing Terraform ecosystemOfficial provider: databricks/databricks
REST API + scriptsQuick automation, custom toolingFull Jobs API
Recommendation: Databricks Asset Bundles

DABs are the Databricks-native way to package workflows, notebooks, and config. They support dev/staging/prod environments, integrate with CI/CD (GitHub Actions, Azure DevOps), and handle deployment promotion. Start here unless you already have a Terraform-first organisation.

Check Your Understanding

The Silver weather task depends on all three ingestion tasks (ECMWF, GFS, ICON). ICON ingestion fails. What should happen?

Correct. Not all weather sources are equally critical. If ICON is supplementary, separate it from the critical path or use conditional logic. Silver can produce a valid forecast from ECMWF+GFS alone. Alert on the ICON failure but don't block the pipeline.
If ICON is supplementary (nice-to-have, not essential), it shouldn't block the critical path. Configure dependencies so that Silver requires ECMWF (critical) but treats ICON as optional. Alert on failure, don't block.

Why use separate job clusters for ingestion (single-node) vs. transform (multi-node)?

Right. GRIB parsing (cfgrib/xarray) runs in the driver process — extra workers don't help. Paying for idle workers wastes money. Transforms (geo-join across millions of rows, window functions) benefit from distributed Spark — that's where multi-node pays off.
The answer is workload characteristics. cfgrib/xarray run on the driver (single-threaded Python) — workers sit idle. But Spark transforms (joins, aggregations, window functions) scale linearly with workers. Match cluster size to workload type.

Your pipeline runs at 05:00 UTC daily. Historically it completes in 25 minutes. Today it took 90 minutes. What mechanism detects this?

Correct. The health rule "metric": "RUN_DURATION_SECONDS", "op": "GREATER_THAN", "value": 3600 triggers when the workflow exceeds 1 hour. This catches performance regressions, unexpectedly large data volumes, or resource contention — even when the pipeline eventually succeeds.
Health rules are the answer. They monitor aggregate workflow metrics (duration, failure rate) and alert on deviations. Even a "successful" run that takes 3× longer than normal indicates a problem (data volume spike, cluster undersizing, API slowness).

Primary Source

📖 Recommended Reading

Databricks Workflows documentation — covers task types, dependencies, cluster configuration, scheduling, and monitoring. Also see Databricks Asset Bundles for infrastructure-as-code deployment.

What's Next

The pipeline is fully orchestrated. The final lesson covers Cost, Performance & Operations — sizing clusters efficiently, controlling spend, optimising Delta tables, and operational runbooks for your production system.


Questions about multi-workflow setups, triggering pipelines from external events, or CI/CD integration? Ask your teacher.

Rechtliches: Impressum