如何编写一个具有与执行日期有关的动态任务的DAG?

时间:2019-07-09 16:04:30

标签: airflow

我们有一个DAG,可以从广告平台中提取一些数据。这些广告被组织成广告系列。我们的目标是为这些广告系列引入高级指标。为此,我们首先需要获取给定执行日期的活动广告系列列表-幸运的是,只要我们知道要查询的时间范围,广告平台的API就可以做到这一点。

当前,我们的DAG的结构是去获取这些广告系列,然后将它们存储在S3中,最后存储到Redshift中。然后,我们先查询Redshift,然后再设置后续任务,为每个广告系列提取数据。这是主要部分。我们也可以看一下S3,但是麻烦的是密钥是用ds宏的值进行模板化的。构造DAG本身时似乎没有办法知道该值。

我们当前的方法也不知道执行日期,因此它总是查询所有广告系列,即使这些广告系列在我们感兴趣的时间段内不活跃。

为使这一点更加具体,今天是DAG的样子:

DAG of Ad Platform Campaign Metrics

另一种方法是将所有内容汇总到一个运算符中,该运算符封装为获取当前执行日期的一组广告系列,然后为每个这些广告系列获取指标。我们避免了这种情况,因为这似乎无法通过每个广告系列通过单独的任务并行提取数据。

我们如何编写此DAG,以便我们通过动态查询广告活动的Redshift表来维持并行化,但是将广告活动正确地约束到执行日期?

2 个答案:

答案 0 :(得分:0)

我认为这是不可能的。 DAG只能以DAG的python定义所定义的一种配置进行渲染。您将无法根据执行日期来控制哪个版本的DAG渲染,因此,例如,您将无法回顾DAG过去应如何渲染。如果希望当前的DAG根据执行日期进行渲染,则可以在DAG的python定义中编写一些逻辑。

根据您安排Airflow工作的方式,您也许可以按照您的描述安排一个操作员,但是让那个操作员在Redshift上启动并行查询并在所有查询完成后终止。

答案 1 :(得分:0)

一个警告,为了节省时间,我将整理来自第三方的想法和代码示例。我将归功于这些资源,以便您可以查看上下文和文档。另外需要说明的是,我无法对此进行测试,但是我99%确信这可以工作。

整个操作中最棘手的部分是弄清楚如何处理可能已经结束并开始备份的广告系列。气流不会像DAG那样具有开始或停止日期的移动。移动停止日期可能会更好一些,移动dag的开始日期根本不起作用。就是说,如果某些广告系列得到了扩展,那么只要连续性没有间隔,您就应该可以更改结束日期。如果您的某个广告系列停滞了一段时间,然后又在两天的非活动时间之间进行了扩展,则可能需要弄清楚如何使这两个广告系列看起来像是独特的广告系列。

第一步

您将要创建一个Python脚本,该脚本将调用您的数据库并从广告系列中返回相关详细信息。假设它在MySQL中看起来像这样,来自PyMySQL pip package documentation的示例连接:

import pymysql.cursors

# Connect to the database
connection = pymysql.connect(host='localhost',
                             user='user',
                             password='passwd',
                             db='db',
                             charset='utf8mb4',
                             cursorclass=pymysql.cursors.DictCursor)

try:
    with connection.cursor() as cursor:
        # Create a new record
        sql = "INSERT INTO `users` (`email`, `password`) VALUES (%s, %s)"
        cursor.execute(sql, ('webmaster@python.org', 'very-secret'))

    # connection is not autocommit by default. So you must commit to save
    # your changes.
    connection.commit()

    with connection.cursor() as cursor:
        # Read a single record
        sql = "SELECT `id`, `password` FROM `users` WHERE `email`=%s"
        cursor.execute(sql, ('webmaster@python.org',))
        result = cursor.fetchall()
finally:
    connection.close()

第二步

您将要遍历该光标并动态创建与Astronomer.io中的示例类似的dag:

from datetime import datetime

from airflow import DAG

from airflow.operators.python_operator import PythonOperator


def create_dag(dag_id,
               schedule,
               dag_number,
               default_args):

    def hello_world_py(*args):
        print('Hello World')
        print('This is DAG: {}'.format(str(dag_number)))

    dag = DAG(dag_id,
              schedule_interval=schedule,
              default_args=default_args)

    with dag:
        t1 = PythonOperator(
            task_id='hello_world',
            python_callable=hello_world_py,
            dag_number=dag_number)

    return dag


# build a dag for each number in range(10)
for campaign in result:  # This is the pymysql result from above
    dag_id = 'hello_world_{}'.format(str(n))

    default_args = {'owner': 'airflow',
                    'start_date': datetime(2018, 1, 1)
                    }

    schedule = '@daily'

    dag_number = n

    globals()[dag_id] = create_dag(dag_id,
                                  schedule,
                                  dag_number,
                                  default_args)

如果将所有这些代码存储在一个文件中,则需要将其放入 dags 文件夹中。当新的活动显示在数据库中时,您将从中创建一个dag,并可以使用subdag架构运行完全相同的步骤/任务集,并从该MySQL数据库中提取参数。为了安全起见,并将最近的活动保留在dag列表中,我将使用日期缓冲区编写mysql查询。这样,您的列表中仍然有最近结束的dag。这些dag结束的那一天,您应该填充dag的end_date参数。