OpenLineage
OpenLineage is an open standard for lineage metadata collection.
The OpenLineage technology plugin is a Hop lineage sink: it subscribes to the in-engine
lineage observation hub and maps Hop’s neutral lineage events
to spec-correct OpenLineage RunEvent JSON, which it posts to any OpenLineage-compatible collector.
The primary target is Marquez.
Events are generated by the spec’s official openlineage-java client, delivered asynchronously off
the pipeline thread, retried on transient HTTP failures, and (by default) buffered losslessly under
back-pressure.
Enabling the sink
The lineage hub is disabled by default. Enable it and select the OpenLineage sink with these engine-scoped variables (see the lineage hub for the hub-wide variables):
| Variable | Default | Description |
|---|---|---|
|
|
Set to |
|
(empty) |
Set to |
The sink itself reads the following variables when it initializes:
| Variable | Default | Description |
|---|---|---|
|
(required) |
Collector URL, e.g. |
|
|
OpenLineage job namespace (the producing Hop environment). |
|
(plugin URL) |
OpenLineage |
|
|
Parse |
|
|
|
|
(empty) |
Optional bearer token sent as |
|
|
HTTP connect timeout. |
|
|
HTTP read/response timeout. |
|
|
Async outbound queue capacity. |
|
|
Retries per event on transient HTTP failure (exponential backoff). |
|
|
Initial retry backoff; doubles each attempt. |
|
|
What to do when the outbound buffer is full. |
|
|
Under |
|
|
Maximum time to drain the queue on shutdown. |
|
|
If > 0, periodically log |
|
(empty) |
Trust/key store for HTTPS collectors with a custom CA/cert. |
|
(empty) |
Store password (used with the trust store path). |
|
|
Trust store type (e.g. |
Events are buffered in memory and delivered by a single ordered worker thread; on a hard crash
undelivered events are lost. There is no proxy setting — route via the JVM -Dhttps.proxyHost
properties if needed.
Event mapping
| Hop lineage event | OpenLineage output |
|---|---|
|
|
|
|
|
|
|
Skipped (internal row shapes between transforms, not physical datasets). |
|
Skipped. |
The run id is the executing pipeline/workflow log-channel id; file I/O is correlated to the parent
pipeline run so inputs/outputs appear on the pipeline job in Marquez. Transform and action run
events carry a ParentRunFacet so their jobs attach to the parent pipeline/workflow run in the
lineage graph rather than appearing as disconnected jobs.
Database lineage
RELATIONAL_IO events record the relational tables a transform reads from or writes to. The physical
dataset is identified by a database namespace (e.g. postgres://host:5432) and a fully-qualified
database.schema.table name (see Dataset identity), so a Hop write and, for example, a dbt read
of the same table land on one node in the lineage graph.
The following transforms emit relational lineage:
-
Reads — Table Input (its
SELECTis parsed to recover the source tables and column lineage). -
Writes — Table Output, Insert / Update, Update, Dimension Lookup/Update, Combination Lookup/Update, and the database bulk loaders (CrateDB, MonetDB, MySQL, Oracle, PostgreSQL, Vertica).
-
Deletes — Delete reports the affected table only. A delete produces no columns, so the event carries neither a column schema nor column lineage.
-
Reads and writes — Execute SQL (its statements are parsed for both input and output tables).
SQL parsing
For transforms that only provide a SQL statement (Table Input, Execute SQL), the sink recovers the
tables — and, for an INSERT … SELECT, the column-level lineage — by parsing the statement with the
OpenLineage SQL parser. Parsing is on by default and controlled by HOP_LINEAGE_OPENLINEAGE_SQL_PARSE.
Tables named without a schema or catalog are qualified using the connection’s default database and
preferred schema, so a bare SELECT … FROM orders shares one identity with a schema-qualified write
of the same table.
Column-level lineage
The sink emits an OpenLineage columnLineage facet on the output dataset in two cases:
-
SQL-derived — an
INSERT … SELECT(Execute SQL) maps each target column to the source columns the parser resolves it from. -
Stream path — a relational write transform whose columns are traced back through the pipeline stream to the read (or lookup) that produced each field, giving
target.column ← source.columnedges. Covered writers: Table Output, Insert / Update, Update, Dimension Lookup/Update (natural keys and argument-bound attributes), Combination Lookup/Update (keys), and the database bulk loaders. Generated columns — a dimension’s technical key, version and validity dates — have no stream source and carry no column lineage.
Writes that replace their target (a truncate, or the target of an overwrite) additionally carry a
lifecycleStateChange facet.
Dataset identity
The (namespace, name) rules the sink uses to identify datasets — and the naming another producer
must match to stitch into Hop’s lineage graph — are a stable, versioned contract documented in
OpenLineage dataset identity. Read it before
integrating a second lineage producer against the same collector.
Verifying
-
Set
HOP_LINEAGE_ENABLED=Y,HOP_LINEAGE_SINK_IDS=openlineage, andHOP_LINEAGE_OPENLINEAGE_URL. -
Run a pipeline that reads a file (e.g. a CSV input).
-
Open the Marquez UI and confirm a job run (
START/COMPLETE) and at least one file dataset. -
For database lineage, run a pipeline that reads and writes a table (e.g. Table Input → Table Output) and confirm the input/output table datasets — and, for an
INSERT … SELECT, thecolumnLineagefacet — in Marquez.
On startup the sink logs OpenLineage lineage sink initialized: url=…, namespace=….