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=….