Jump to content

Connect SuperML | Leeroopedia MCP: Equip your AI agents with best practices, code verification, and debugging knowledge. Powered by Leeroo — building Organizational Superintelligence. Contact us at founders@leeroo.com.

Principle:Spotify Luigi Database Data Loading

From Leeroopedia


Knowledge Sources Spotify Luigi Repository
Domains Pipeline_Orchestration, Database, ETL
Last Updated 2026-02-10 00:00 GMT

Overview

Database Data Loading is the process of transferring rows of processed pipeline data from an in-memory or file-based representation into a relational database table, using bulk insertion mechanisms within a single transactional boundary.

Description

After a pipeline task has transformed its input data and declared the target tables schema, the final step in the database ingestion workflow is the actual data loading -- the physical transfer of rows into the database. This principle governs how that transfer is organized, optimized, and made resilient.

Effective database data loading addresses several concerns:

  • Row generation: The task produces rows of data through a generator method (commonly named rows()) that yields tuples or lists. Each tuples elements correspond to the columns declared in the schema. This lazy generation avoids holding the entire dataset in memory.
  • Value mapping and escaping: Before rows are written, individual column values may need transformation -- escaping special characters (tabs, newlines, backslashes), converting None to database-specific NULL representations, or casting types. A map_column() function handles these per-value transformations.
  • Bulk transfer mechanism: Rather than issuing one INSERT per row, efficient data loading uses bulk mechanisms. PostgreSQLs COPY FROM STDIN command reads tab-separated data from a file-like object, achieving dramatically higher throughput. MySQL uses batched INSERT with executemany(). SQLAlchemy uses chunked inserts with configurable chunk_size.
  • Temporary staging: For file-based bulk copy (e.g., PostgreSQL COPY), the pipeline writes all rows to a temporary file first, then streams the file to the database. This allows progress logging and ensures the data source is seekable.
  • Pre-copy and post-copy hooks: The loading process supports extensible hooks that run within the same transaction, immediately before and after the data transfer. The init_copy() hook can truncate old data or add metadata columns, while post_copy() can cleanse, deduplicate, or transform data in a staging table before final insertion.
  • Transactional integrity: The entire sequence -- init_copy, data copy, post_copy, and completion marking -- executes within a single database transaction. If any step fails, the transaction is rolled back, leaving the database in its prior consistent state.
  • Automatic table creation retry: If the initial copy attempt fails because the target table does not exist, the framework resets the connection, creates the table, and retries the copy -- all transparently within the run() method.

Usage

Use Database Data Loading when:

  • Your pipeline task has produced tabular data that needs to be persisted in a relational database.
  • You need high-throughput bulk insertion rather than row-by-row INSERT statements.
  • You want transactional guarantees so that partial loads do not leave the database in an inconsistent state.
  • You need pre-copy or post-copy processing (e.g., truncating a rolling window of data, deduplication, or metadata enrichment) to execute atomically with the data load.
  • You want the framework to handle table creation automatically when the target table does not yet exist.

Theoretical Basis

Database Data Loading implements the Template Method pattern, where the run() method defines the invariant algorithm skeleton, and subclasses override specific steps (like rows(), copy(), init_copy(), post_copy()) to customize behavior.

The algorithm proceeds as follows:

  1. Validate configuration: Verify that table and columns are specified; raise an exception if not.
  2. Establish connection: Call self.output().connect() to obtain a database connection from the target.
  3. Generate and stage data: Iterate over self.rows(), apply map_column() to each value, join values with the column separator, and write each row to a temporary file (for file-based bulk copy) or accumulate into batches (for batch insert).
  4. Begin copy loop (up to 2 attempts):
    1. Open a cursor on the connection.
    2. Execute self.init_copy(connection) for pre-copy operations (e.g., adding metadata columns, truncating old data).
    3. Execute self.copy(cursor, file) to perform the bulk data transfer. For PostgreSQL, this issues a COPY ... FROM STDIN command. For MySQL, this calls cursor.executemany() in batches. For SQLAlchemy, this inserts rows in chunks via conn.execute(ins, rows).
    4. Execute self.post_copy(connection) for post-copy operations.
    5. If metadata columns are enabled, execute self.post_copy_metacolumns(cursor).
  5. Handle table-not-found error: If the first attempt raises a "table does not exist" error, reset the connection, call self.create_table(connection), and retry from step 4.
  6. Mark completion: Call self.output().touch(connection) to record successful completion in the marker table (within the same transaction).
  7. Commit and clean up: Commit the transaction, close the connection, and close the temporary file.

This design ensures that the entire load operation is atomic: either all data is loaded and marked complete, or none of it is.

Related Pages

Page Connections

Double-click a node to navigate. Hold to expand connections.
Principle
Implementation
Heuristic
Environment