@@ -168,29 +168,25 @@ data:
168168 self.openlineage_inputs = openlineage_inputs or []
169169 self.openlineage_outputs = openlineage_outputs or []
170170
171- def _link_spark_run_to_this_task(self, context):
172- # CLEAN-LINEAGE KNOB #1: hand the Spark job this task's OpenLineage run identity via
173- # `spark.openlineage.parent*`. That makes Spark report its own run as a CHILD of this Airflow
174- # task (so the real Spark job is reachable from the DAG in Marquez) and, crucially, gives the
175- # Spark run a real parent name instead of the `unknown` OpenLineage falls back to when it
176- # can't resolve one at listener startup.
177- try:
178- from airflow.providers.openlineage.plugins.macros import (
179- lineage_job_name,
180- lineage_job_namespace,
181- lineage_run_id,
182- )
183-
184- ti = context["task_instance"]
185- conf = self.application_file["spec"]["sparkConf"]
186- conf["spark.openlineage.parentJobNamespace"] = lineage_job_namespace()
187- conf["spark.openlineage.parentJobName"] = lineage_job_name(ti)
188- conf["spark.openlineage.parentRunId"] = lineage_run_id(ti)
189- except Exception as e: # never fail the task just because linking didn't work
190- self.log.warning("Could not set OpenLineage parent run on the SparkApplication: %s", e)
171+ # CLEAN-LINEAGE KNOB #1: do NOT hand the Spark job this task's OpenLineage run identity via
172+ # `spark.openlineage.parent*`. It is tempting (it would nest the Spark run under this Airflow
173+ # task), but it actively BREAKS the Spark lineage in Marquez 0.51.x (explained below).
174+ #
175+ # Why: Spark correctly emits ONE application job named `orders_by_nation` (bare) carrying a
176+ # parent facet, and its SQL children reference that same bare parent. But Marquez renames any
177+ # job that has a parent facet to `{parentJobName}.{jobName}` UNLESS the name already starts
178+ # with the parent. The SQL jobs (`orders_by_nation.<cmd>.<table>`) already do, so they're left
179+ # alone; the application job (`orders_by_nation`) does not start with its parent
180+ # (`openlineage_demo.spark_orders_by_nation`), so Marquez stores it as
181+ # `openlineage_demo.spark_orders_by_nation.orders_by_nation` while the children still point at
182+ # bare `orders_by_nation` -> the one run splits into two app nodes and the chain breaks.
183+ #
184+ # Marquez draws only job<->dataset edges (never job->job parent edges), so the parent facet buys
185+ # no visible edge anyway. Airflow<->Spark is stitched via shared DATASET identities instead, in
186+ # get_openlineage_facets_on_complete (KNOB #2). `spark.openlineage.appName` (KNOB #3, in the
187+ # SparkApplication) still pins a stable, non-`unknown` job name.
191188
192189 def execute(self, context: "Context"):
193- self._link_spark_run_to_this_task(context)
194190 hook = KubernetesHook(conn_id=self.kubernetes_conn_id)
195191 name = self.application_file["metadata"]["name"]
196192 self.log.info("Creating SparkApplication %s ...", name)
@@ -282,8 +278,9 @@ data:
282278 "spark.openlineage.transport.type": "http",
283279 "spark.openlineage.transport.url": "http://marquez:5000",
284280 "spark.openlineage.namespace": "spark-openlineage-demo",
285- # parentJobNamespace/parentJobName/parentRunId are injected at task runtime by the
286- # operator (see _link_spark_run_to_this_task) so they carry this run's real identity.
281+ # NB: spark.openlineage.parent* is deliberately NOT set here or at runtime — see
282+ # CLEAN-LINEAGE KNOB #1 above (it triggers a Marquez job-rename that splits the
283+ # Spark app node).
287284 },
288285 "volumes": [
289286 {"name": "script", "configMap": {"name": "spark-openlineage-job"}},
0 commit comments