Airflow ClickHouse Plugin

repository·master·Indexed 41 days ago

https://github.com/bryzgaloff/airflow-clickhouse-plugin

An Apache Airflow plugin for executing ClickHouse commands and queries. Version 1.8.0 provides two interaction methods: a high-performance 'clickhouse-driver' family (ClickHouseOperator, ClickHouseHook, ClickHouseSensor) using the native TCP protocol, and a Python DB API 2.0 family (including ClickHouseSQLExecuteQueryOperator and ClickHouseDbApiHook) for compatibility with common.sql provider patterns and OpenLineage integration.

Tokens
6.6K
Snippets
17
Records
23
Agent score
52%

What's inside Airflow ClickHouse Plugin

  1. What is the Python DB API 2.0 family?

    master

    The Python DB API 2.0 family uses clickhouse_driver.dbapi combined with apache-airflow-providers-common-sql. This family is ideal when porting Airflow pipelines from other SQL providers (like MySQL, Postgres, or BigQuery) to ClickHouse, as it provides a standardized interface.

    Included Operators:

    • ClickHouseSQLExecuteQueryOperator
    • ClickHouseSQLColumnCheckOperator
    • ClickHouseSQLTableCheckOperator
    • ClickHouseSQLCheckOperator
    • ClickHouseSQLValueCheckOperator
    • ClickHouseSQLIntervalCheckOperator
    • ClickHouseSQLThresholdCheckOperator
    • ClickHouseBranchSQLOperator

    Included Hook:

    • ClickHouseDbApiHook (supports OpenLineage)
  2. What is the `clickhouse-driver` family?

    master

    The clickhouse-driver family consists of ClickHouseOperator, ClickHouseHook, and ClickHouseSensor. These are based on the clickhouse_driver.Client.execute method and are recommended for new ClickHouse integrations in Airflow.

    Key Features:

    • Native Protocol: Uses the efficient native ClickHouse TCP protocol (does not support HTTP).
    • SQL Templating: Supports templated SQL queries and parameters.
    • Multiple Queries: Can execute multiple SQL queries in one operator; the result of the last query is pushed to XCom (controlled by do_xcom_push).
    • Custom Parameters: Supports advanced connection settings (timeouts, compression, secure) via the Airflow Connection.extra property.
  3. How to create an Airflow connection to ClickHouse

    master

    Since there is no dedicated ClickHouse connection type, create a new connection in Airflow using SQLite or any other SQL database type.

    Connection Mapping

    The following Airflow connection attributes are mapped to clickhouse_driver.Client arguments:

    Airflow Connection attributeClient.__init__ argument
    hosthost
    port (int)port
    schemadatabase
    loginuser
    passwordpassword
    extra**kwargs

    Important Notes

    • Database Overrides: The database argument in ClickHouseOperator, ClickHouseHook, or ClickHouseSensor will override the schema attribute defined in the Airflow connection.
    • Default Host: If host is not set, it defaults to localhost.
    • Default Connection ID: The plugin uses clickhouse_default as the default connection ID if none is specified.
  4. Install the Airflow ClickHouse Plugin

    master

    Install the base plugin with its core dependencies (apache-airflow and clickhouse-driver):

    pip install -U airflow-clickhouse-plugin

    To enable the Python DB API 2.0 family of operators (which provides a standardized interface compatible with common.sql providers like MySQL or Postgres), install with the common.sql extra:

    pip install -U airflow-clickhouse-plugin[common.sql]

    To enable OpenLineage integration with the ClickHouseDbApiHook, install with both extras:

    pip install -U airflow-clickhouse-plugin[common.sql,openlineage]
    pip install -U airflow-clickhouse-plugin
  5. Configure extra arguments and compression for ClickHouse connections

    master

    You can pass non-standard arguments (like timeouts, compression, or secure) to the clickhouse_driver.Client by using the Airflow connection extra field. The extra field must contain a JSON object. All properties in this object are passed as keyword arguments to the client.

    Secure Connections

    To use a secure connection, set the extra field to: {"secure": true}.

    Compression

    To use compression (e.g., lz4), you must first install the required Python packages:

    pip3 install clickhouse-cityhash lz4

    Then, set the compression parameter in the connection's extra field:

    {"compression": "lz4"}

    Alternatively, you can use a Connection URI format: clickhouse://login:password@host:port/?compression=lz4

  6. Run tests for the Airflow ClickHouse Plugin

    master

    Unit Tests

    Run unit tests using:

    python3 -m unittest discover -t tests -s unit

    Integration Tests

    Integration tests require a running ClickHouse server. You can use Docker to set up a local environment:

    1. Start ClickHouse:
      docker run -p 9000:9000 --ulimit nofile=262144:262144 -it clickhouse/clickhouse-server
    2. Run Tests: Use the AIRFLOW_CONN_CLICKHOUSE_DEFAULT environment variable to point to your local container:
      PYTHONPATH=src AIRFLOW_CONN_CLICKHOUSE_DEFAULT=clickhouse://localhost python3 -m unittest discover -t tests -s integration

    Run all tests (Unit & Integration)

    PYTHONPATH=src AIRFLOW_CONN_CLICKHOUSE_DEFAULT=clickhouse://localhost python3 -m unittest discover -s tests
    # Run all tests with ClickHouse connection defined
    PYTHONPATH=src AIRFLOW_CONN_CLICKHOUSE_DEFAULT=clickhouse://localhost python3 -m unittest discover -s tests
  7. Use DB API 2.0: ClickHouseSqlSensor and ClickHouseSQLExecuteQueryOperator

    master

    For compatibility with the Python DB API 2.0 standard, use the clickhouse_dbapi module. These classes are useful when you need to work with standard DB-API patterns.

    Key components:

    • ClickHouseSqlSensor: A sensor that uses hook_params to configure the connection (e.g., schema).
    • ClickHouseSQLExecuteQueryOperator: An operator for executing queries using the DB-API interface.
    from airflow import DAG
    from airflow_clickhouse_plugin.sensors.clickhouse_dbapi import ClickHouseSqlSensor
    from airflow_clickhouse_plugin.operators.clickhouse_dbapi import ClickHouseSQLExecuteQueryOperator
    from airflow.utils.dates import days_ago
    
    
    with DAG(
            dag_id='listen_warnings',
            start_date=days_ago(2),
    ) as dag:
        dag >> ClickHouseSqlSensor(
            task_id='poke_events_count',
            hook_params=dict(schema='monitor'),
            sql="SELECT count() FROM warnings WHERE eventDate = '{{ ds }}'",
            success=lambda cnt: cnt > 10000,
            conn_id=None,  # required by common.sql SqlSensor; use None for default
        ) >> ClickHouseSQLExecuteQueryOperator(
            task_id='create_alert',
            database='alerts',
            sql='''
                INSERT INTO events SELECT eventDate, count()
                FROM monitor.warnings WHERE eventDate = '{{ ds }}'
            ''',
        )
  8. Use ClickHouseOperator to execute SQL queries

    master

    The ClickHouseOperator allows you to execute one or multiple SQL statements. If multiple statements are provided in a list, the results of the last query are pushed to XCom.

    Key parameters:

    • sql: A string or a list of strings containing the SQL commands.
    • database: The ClickHouse database to use (overrides connection schema).
    • clickhouse_conn_id: The Airflow connection ID to use.
    • query_id: A templated string used to identify the query in ClickHouse logs.
    from airflow import DAG
    from airflow_clickhouse_plugin.operators.clickhouse import ClickHouseOperator
    from airflow.utils.dates import days_ago
    
    with DAG(
            dag_id='update_income_aggregate',
            start_date=days_ago(2),
    ) as dag:
        ClickHouseOperator(
            task_id='update_income_aggregate',
            database='default',
            sql=(
                '''
                    INSERT INTO aggregate
                    SELECT eventDt, sum(price * qty) AS income FROM sales
                    WHERE eventDt = '{{ ds }}' GROUP BY eventDt
                ''', '''
                    OPTIMIZE TABLE aggregate ON CLUSTER {{ var.value.cluster_name }}
                    PARTITION toDate('{{ execution_date.format('%Y-%m-01') }}')
                ''',
            ),
            query_id='{{ ti.dag_id }}-{{ ti.task_id }}-{{ ti.run_id }}-{{ ti.try_number }}',
            clickhouse_conn_id='clickhouse_test',
        )
  9. Use ClickHouseHook to interact with ClickHouse

    master

    The ClickHouseHook provides a programmatic interface to ClickHouse.

    Warning on INSERTs: When using ch_hook.execute(), do not use literal string forms like INSERT INTO table VALUES (1). Because of the native ClickHouse protocol requirements, clickhouse-driver requires that values for INSERT queries be provided via the parameters argument.

    from airflow import DAG
    from airflow_clickhouse_plugin.hooks.clickhouse import ClickHouseHook
    from airflow.providers.sqlite.hooks.sqlite import SqliteHook
    from airflow.operators.python import PythonOperator
    from airflow.utils.dates import days_ago
    
    
    def sqlite_to_clickhouse():
        sqlite_hook = SqliteHook()
        ch_hook = ClickHouseHook()
        records = sqlite_hook.get_records('SELECT * FROM some_sqlite_table')
        # Note: records should be passed via parameters in a real scenario
        ch_hook.execute('INSERT INTO some_ch_table VALUES', records)
    
    
    with DAG(
            dag_id='sqlite_to_clickhouse',
            start_date=days_ago(2),
    ) as dag:
        dag >> PythonOperator(
            task_id='sqlite_to_clickhouse',
            python_callable=sqlite_to_clickhouse,
        )
  10. Use ClickHouseSensor to monitor ClickHouse data

    master

    The ClickHouseSensor pokes a ClickHouse database to check if a condition is met based on a SQL query.

    Key parameters:

    • sql: The query to execute.
    • is_success: A lambda or function that takes the query result as input and returns True if the condition is met.
    • database: The ClickHouse database to query.
    from airflow import DAG
    from airflow_clickhouse_plugin.sensors.clickhouse import ClickHouseSensor
    from airflow_clickhouse_plugin.operators.clickhouse import ClickHouseOperator
    from airflow.utils.dates import days_ago
    
    
    with DAG(
            dag_id='listen_warnings',
            start_date=days_ago(2),
    ) as dag:
        dag >> ClickHouseSensor(
            task_id='poke_events_count',
            database='monitor',
            sql="SELECT count() FROM warnings WHERE eventDate = '{{ ds }}'",
            is_success=lambda cnt: cnt > 10000,
        ) >> ClickHouseOperator(
            task_id='create_alert',
            database='alerts',
            sql='''
                INSERT INTO events SELECT eventDate, count()
                FROM monitor.warnings WHERE eventDate = '{{ ds }}'
            ''',
        )
  11. Use ClickHouseSensor

    master

    Import ClickHouseSensor from airflow_clickhouse_plugin.sensors.clickhouse. It wraps ClickHouseHook.execute into an Airflow sensor to monitor ClickHouse states.

    Arguments:

    • Supports all arguments available to ClickHouseOperator.
    • is_success (callable): Accepts the return value of ClickHouseHook.execute. If truthy, the sensor succeeds. Default is bool (succeeds if the query returns records).
    • is_failure (callable): Accepts the return value of ClickHouseHook.execute. If truthy, the sensor raises an AirflowException. Default is None.
    from airflow_clickhouse_plugin.sensors.clickhouse import ClickHouseSensor
  12. Use ClickHouseHook

    master

    Import ClickHouseHook from airflow_clickhouse_plugin.hooks.clickhouse. It provides a direct interface to the ClickHouse driver.

    Methods:

    • execute(sql, ...): Wraps clickhouse_driver.Client.execute. Accepts a single query string or an iterable of strings. Returns the result of the last query.
    • get_conn(): Returns the underlying clickhouse_driver.Client instance.

    Constructor Arguments:

    • clickhouse_conn_id: Airflow connection ID (defaults to clickhouse_default).
    • database: Overrides the schema in the Airflow connection.
    from airflow_clickhouse_plugin.hooks.clickhouse import ClickHouseHook