English version

← Learning Hub Lektion 9 Dauer: ca. 14 Min. Thema: Produktivbetrieb

Workflows & Orchestrierung

Diese Lektion verdrahtet alles miteinander: Ingestion, Silver, Features, ML-Inferenz und Gold — orchestriert als ein einziger produktiver Workflow. Am Ende weißt du, wie du Task-Abhängigkeiten strukturierst, Fehler sauber abfängst, Retries konfigurierst und das Alerting für deine Wetter-zu-Strom-Pipeline aufsetzt.

Die komplette Pipeline als Workflow

Wetter-zu-Strom-Workflow — Abhängigkeiten der Tasks ECMWF einlesen GFS einlesen ICON einlesen Telemetrie einlesen Bronze → Silver (Wetter) Bronze → Silver (Telemetrie) Features berechnen ML-Inferenz Gold schreiben + melden

Workflow-Konfiguration (JSON)

Databricks Workflows werden als JSON definiert (alternativ über die UI oder Terraform). So sieht die Struktur für deine Pipeline aus:[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}
    ]
  }
}

Zentrale Entwurfsentscheidungen

1. Cluster über Tasks hinweg wiederverwenden

Tasks mit demselben job_cluster_key nutzen denselben Cluster — er bleibt zwischen den Tasks am Leben. Das spart Startzeit (ca. 3–5 Min. pro Cluster), bedeutet aber, dass du die Leerlaufzeit zwischen aufeinanderfolgenden Tasks bezahlst.

StrategieWann
Gemeinsamer Cluster (gleicher job_cluster_key)Sequenzielle Tasks mit denselben Bibliotheksanforderungen
Getrennte ClusterAndere Runtime nötig (z. B. ML vs. Standard) oder starke Parallelität
Single-Node (num_workers: 0)I/O-lastige Tasks wie API-Downloads und GRIB-Parsing

2. Arten von Abhängigkeiten

// Standard: ALLE Vorgänger müssen erfolgreich sein
"depends_on": [{"task_key": "ingest_ecmwf"}, {"task_key": "ingest_gfs"}]

// Wenn eine Quelle optional ist (z. B. ICON als Nice-to-have):
// "run_if"-Bedingung nutzen oder den optionalen Pfad abtrennen
"run_if": "ALL_SUCCESS"  // Standard
"run_if": "AT_LEAST_ONE_SUCCESS"  // wenn Teildaten akzeptabel sind
Strategie für Teilerfolge

Wetterquellen sind unterschiedlich zuverlässig. ECMWF ist womöglich kritisch (ohne diese Quelle scheitert die Pipeline), während ICON nur ergänzt. Entwirf die Abhängigkeiten so, dass: (1) ein ECMWF-Ausfall die Pipeline blockiert, (2) ein ICON-Ausfall eine Warnung protokolliert, die Pipeline aber allein mit ECMWF- und GFS-Daten weiterläuft.

3. Retry-Strategien

{
  "max_retries": 2,            // insgesamt 3 Versuche
  "min_retry_interval_millis": 60000,  // 1 Min. Pause zwischen den Versuchen
  "retry_on_timeout": true     // auch bei Timeout erneut versuchen
}

Retries sind bei vorübergehenden Störungen sinnvoll (API-Timeout, verdrängte Spot-Instanz). Sie sind es nicht bei Logikfehlern (fehlerhafter Code) oder bei Datenqualitätsproblemen — die gehören gemeldet, nicht wiederholt.

Strategie für den Zeitplan

Der Zeitplan deiner Pipeline muss zur Verfügbarkeit der Wettermodelle passen:

ModellInitialisierungszeitenVerfügbar abDein Trigger
ECMWF00, 06, 12, 18 UTCInit + 5–6 h05, 11, 17, 23 UTC
GFS00, 06, 12, 18 UTCInit + 3,5–4 h(gleicher Trigger, läuft früher)
ICON00, 06, 12, 18 UTCInit + 2–3 h(gleicher Trigger, läuft früher)
# Cron: 4× täglich, 5 Stunden nach der Modell-Initialisierung
# "0 0 5,11,17,23 * * ?" = um 05:00, 11:00, 17:00 und 23:00 UTC
"quartz_cron_expression": "0 0 5,11,17,23 * * ?"

Monitoring & Alerting

Eingebautes Databricks-Monitoring

Eigene Prüfungen auf Pipeline-Ebene (letzter Task)

# Letzter Task im Workflow: Ausgabe der Pipeline validieren
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}"
    
    # Prüfen, ob alle Parks Prognosen haben
    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

Konfiguriere Workflows nicht von Hand in der UI. Nutze Infrastructure as Code, damit alles reproduzierbar bleibt:

WerkzeugWannDatabricks-Unterstützung
Databricks Asset Bundles (DABs)Standardwahl für Databricks-native ProjekteFirst-Party, YAML-basiert, inklusive CI/CD-Vorlagen
TerraformMulti-Cloud, bestehendes Terraform-ÖkosystemOffizieller Provider: databricks/databricks
REST API + SkripteSchnelle Automatisierung, eigenes ToolingVollständige Jobs-API
Empfehlung: Databricks Asset Bundles

DABs sind der Databricks-eigene Weg, Workflows, Notebooks und Konfiguration zu paketieren. Sie unterstützen Dev-/Staging-/Prod-Umgebungen, integrieren sich in CI/CD (GitHub Actions, Azure DevOps) und regeln die Promotion zwischen Umgebungen. Starte hier — es sei denn, deine Organisation ist bereits durchgehend Terraform-getrieben.

Prüfe dein Verständnis

Der Silver-Task für Wetter hängt von allen drei Ingestion-Tasks ab (ECMWF, GFS, ICON). Die ICON-Ingestion scheitert. Was sollte passieren?

Richtig. Nicht alle Wetterquellen sind gleich kritisch. Wenn ICON nur ergänzt, nimm es aus dem kritischen Pfad oder arbeite mit bedingter Logik. Silver kann allein aus ECMWF und GFS eine valide Prognose erzeugen. Melde den ICON-Ausfall, aber blockiere die Pipeline nicht.
Wenn ICON nur ergänzend ist (Nice-to-have, nicht essenziell), darf es den kritischen Pfad nicht blockieren. Konfiguriere die Abhängigkeiten so, dass Silver ECMWF zwingend braucht, ICON aber als optional behandelt. Melden statt blockieren.

Warum getrennte Job-Cluster für Ingestion (Single-Node) und Transformation (Multi-Node)?

Genau. Das GRIB-Parsing (cfgrib/xarray) läuft im Driver-Prozess — zusätzliche Worker bringen nichts. Für Worker im Leerlauf zu bezahlen, ist verschwendetes Geld. Transformationen (Geo-Join über Millionen Zeilen, Fensterfunktionen) profitieren dagegen von verteiltem Spark — dort zahlt sich Multi-Node aus.
Die Antwort ist das Lastprofil. cfgrib/xarray laufen im Driver (single-threaded Python) — die Worker langweilen sich. Spark-Transformationen (Joins, Aggregationen, Fensterfunktionen) skalieren dagegen nahezu linear mit der Anzahl Worker. Passe die Clustergröße an den Workload-Typ an.

Deine Pipeline läuft täglich um 05:00 UTC. Bisher war sie nach 25 Minuten fertig. Heute brauchte sie 90 Minuten. Welcher Mechanismus erkennt das?

Richtig. Die Health-Rule "metric": "RUN_DURATION_SECONDS", "op": "GREATER_THAN", "value": 3600 löst aus, sobald der Workflow eine Stunde überschreitet. So fängst du Performance-Regressionen, unerwartet große Datenmengen oder Ressourcenengpässe ab — selbst dann, wenn die Pipeline am Ende erfolgreich durchläuft.
Health-Rules sind die Antwort. Sie überwachen aggregierte Workflow-Metriken (Laufzeit, Fehlerquote) und melden Abweichungen. Auch ein „erfolgreicher“ Lauf, der dreimal so lange dauert wie üblich, ist ein Warnsignal (Datenmengensprung, zu kleiner Cluster, träge API).

Primärquelle

📖 Empfohlene Lektüre

Dokumentation zu Databricks Workflows — behandelt Task-Typen, Abhängigkeiten, Cluster-Konfiguration, Zeitpläne und Monitoring. Sieh dir außerdem Databricks Asset Bundles für Deployments per Infrastructure as Code an.

Wie geht es weiter

Die Pipeline ist vollständig orchestriert. Die letzte Lektion behandelt Kosten, Performance & Betrieb — Cluster effizient dimensionieren, Ausgaben steuern, Delta-Tabellen optimieren und Runbooks für den Produktivbetrieb schreiben.


Fragen zu Setups mit mehreren Workflows, zum Auslösen von Pipelines durch externe Events oder zur CI/CD-Integration? Frag deinen Lehrer.

Rechtliches: Impressum