намагаючись створити динамічні піддані з батьківського дага на основі масиву імен файлів


10

Я намагаюся перемістити файли s3 з відра, що не видаляє (тобто я не можу видалити файли) в GCS за допомогою повітряного потоку. Я не можу гарантувати, що нові файли будуть там щодня, але я повинен перевіряти наявність нових файлів щодня.

моя проблема - динамічне створення підданих. Якщо є файли ARE, мені потрібні піддані. Якщо файлів НЕ, мені не потрібні піддані. Моя проблема - в налаштуваннях вгору / вниз за течією. У моєму коді він виявляє файли, але не відштовхує піддані, як це належить. Я щось пропускаю.

ось мій код:

from airflow import models
from  airflow.utils.helpers import chain
from airflow.providers.amazon.aws.hooks.s3 import S3Hook
from airflow.operators.python_operator import PythonOperator, BranchPythonOperator
from airflow.operators.dummy_operator import DummyOperator
from airflow.operators.subdag_operator import SubDagOperator
from airflow.contrib.operators.s3_to_gcs_operator import S3ToGoogleCloudStorageOperator
from airflow.utils import dates
from airflow.models import Variable
import logging

args = {
    'owner': 'Airflow',
    'start_date': dates.days_ago(1),
    'email': ['sinistersparrow1701@gmail.com'],
    'email_on_failure': True,
    'email_on_success': True,
}

bucket = 'mybucket'
prefix = 'myprefix/'
LastBDEXDate = int(Variable.get("last_publish_date"))
maxdate = LastBDEXDate
files = []

parent_dag = models.DAG(
    dag_id='My_Ingestion',
    default_args=args,
    schedule_interval='@daily',
    catchup=False
)

def Check_For_Files(**kwargs):
    s3 = S3Hook(aws_conn_id='S3_BOX')
    s3.get_conn()
    bucket = bucket
    LastBDEXDate = int(Variable.get("last_publish_date"))
    maxdate = LastBDEXDate
    files = s3.list_keys(bucket_name=bucket, prefix='myprefix/file')
    for file in files:
        print(file)
        print(file.split("_")[-2])
        print(file.split("_")[-2][-8:])  ##proves I can see a date in the file name is ok.
        maxdate = maxdate if maxdate > int(file.split("_")[-2][-8:]) else int(file.split("_")[-2][-8:])
    if maxdate > LastBDEXDate:
        return 'Start_Process'
    return 'finished'

def create_subdag(dag_parent, dag_id_child_prefix, file_name):
    # dag params
    dag_id_child = '%s.%s' % (dag_parent.dag_id, dag_id_child_prefix)

    # dag
    subdag = models.DAG(dag_id=dag_id_child,
              default_args=args,
              schedule_interval=None)

    # operators
    s3_to_gcs_op = S3ToGoogleCloudStorageOperator(
        task_id=dag_id_child,
        bucket=bucket,
        prefix=file_name,
        dest_gcs_conn_id='GCP_Account',
        dest_gcs='gs://my_files/To_Process/',
        replace=False,
        gzip=True,
        dag=subdag)


    return subdag

def create_subdag_operator(dag_parent, filename, index):
    tid_subdag = 'file_{}'.format(index)
    subdag = create_subdag(dag_parent, tid_subdag, filename)
    sd_op = SubDagOperator(task_id=tid_subdag, dag=dag_parent, subdag=subdag)
    return sd_op

def create_subdag_operators(dag_parent, file_list):
    subdags = [create_subdag_operator(dag_parent, file, file_list.index(file)) for file in file_list]
    # chain subdag-operators together
    chain(*subdags)
    return subdags

check_for_files = BranchPythonOperator(
    task_id='Check_for_s3_Files',
    provide_context=True,
    python_callable=Check_For_Files,
    dag=parent_dag
)

finished = DummyOperator(
    task_id='finished',
    dag=parent_dag
)

decision_to_continue = DummyOperator(
    task_id='Start_Process',
    dag=parent_dag
)

if len(files) > 0:
    subdag_ops = create_subdag_operators(parent_dag, files)
    check_for_files >> decision_to_continue >> subdag_ops[0] >> subdag_ops[-1] >> finished


check_for_files >> finished

Яка робота виконується в основі цих DAGS - це sparkзавдання чи якийсь pythonсценарій, і що ви використовуєте для його виконання, livyабо якийсь інший метод
ashwin agrawal

Вибачте, я не розумію питання. ви можете, будь ласка, перезавантажити?
arcee123

Я маю на увазі, що ви використовуєте лише прості сценарії python і не використовуєте жодне завдання іскри?
ahwin agrawal

Так. прості оператори, які за замовчуванням є в потоці повітря. Я хочу додати існуючих операторів з динамічною швидкістю, заснованої на маркованих файлах у S3, я хочу заглибитись у GCS.
arcee123

Чому filesпорожній список?
Oluwafemi Sule

Відповіді:


3

Нижче наведено рекомендований спосіб створення динамічного DAG або sub-DAG у потоці повітря, хоча є й інші способи, але, мабуть, це в значній мірі стосується вашої проблеми.

По-перше, створіть файл, (yaml/csv)який включає список усіх s3файлів та місць, у вашому випадку ви написали функцію зберігання їх у списку, я б сказав, зберігайте їх в окремому yamlфайлі та завантажуйте його під час виконання у потоці повітряного потоку, а потім створюйте DAGs.

Нижче наведено зразок yamlфайлу: dynamicDagConfigFile.yaml

job: dynamic-dag
bucket_name: 'bucket-name'
prefix: 'bucket-prefix'
S3Files:
    - File1: 'S3Loc1'
    - File2: 'S3Loc2'
    - File3: 'S3Loc3'

Ви можете змінити свою Check_For_Filesфункцію, щоб зберегти їх у yamlфайлі.

Тепер ми можемо перейти до динамічного створення даг:

Спочатку визначте дві задачі за допомогою операторів фіктивних робіт, тобтозапуск і кінець. Такі завдання - це ті, на яких ми збираємось будувати свої DAGзавдання, динамічно створюючи завдання між ними:

start = DummyOperator(
    task_id='start',
    dag=dag
)

end = DummyOperator(
    task_id='end',
    dag=dag)

Динамічний DAG: Ми будемо використовувати PythonOperatorsв потоці повітря. Функція повинна отримувати в якості аргументів ідентифікатор завдання; функцію python, яку слід виконати, тобто python_callable для оператора Python; і набір аргументів, який слід використовувати під час виконання.

Включіть аргумент task id. Отже, ми можемо обмінюватися даними між завданнями, що створюються динамічним способом, наприклад, через XCOM.

Ви можете вказати свою функцію функціонування в рамках цього динамічного дага s3_to_gcs_op.

def createDynamicDAG(task_id, callableFunction, args):
    task = PythonOperator(
        task_id = task_id,
        provide_context=True,
        #Eval is used since the callableFunction var is of type string
        #while the python_callable argument for PythonOperators only receives objects of type callable not strings.
        python_callable = eval(callableFunction),
        op_kwargs = args,
        xcom_push = True,
        dag = dag,
    )
    return task

Нарешті, виходячи з місця, наявного у файлі yaml, ви можете створити динамічні даги, спочатку прочитайте yamlфайл, як показано нижче, і створіть динамічний даг:

with open('/usr/local/airflow/dags/config_files/dynamicDagConfigFile.yaml') as f:
    # use safe_load instead to load the YAML file
    configFile = yaml.safe_load(f)

    #Extract file list
    S3Files = configFile['S3Files']

    #In this loop tasks are created for each table defined in the YAML file
    for S3File in S3Files:
        for S3File, fieldName in S3File.items():

            #Remember task id is provided in order to exchange data among tasks generated in dynamic way.
            get_s3_files = createDynamicDAG('{}-getS3Data'.format(S3File), 
                                            'getS3Data', 
                                            {}) #your configs here.

            #Second step is upload S3 to GCS
            upload_s3_toGCS = createDynamicDAG('{}-uploadDataS3ToGCS'.format(S3File), 'uploadDataS3ToGCS', {'previous_task_id':'{}-'})

#write your configs again here like S3 bucket name prefix extra or read from yaml file, and other GCS config.

Остаточне визначення DAG:

Ідея така

#once tasks are generated they should linked with the
#dummy operators generated in the start and end tasks. 
start >> get_s3_files
get_s3_files >> upload_s3_toGCS
upload_s3_toGCS >> end

Повний код повітряного потоку для того, щоб:

import yaml
import airflow
from airflow import DAG
from datetime import datetime, timedelta, time
from airflow.operators.python_operator import PythonOperator
from airflow.operators.dummy_operator import DummyOperator

start = DummyOperator(
    task_id='start',
    dag=dag
)


def createDynamicDAG(task_id, callableFunction, args):
    task = PythonOperator(
        task_id = task_id,
        provide_context=True,
        #Eval is used since the callableFunction var is of type string
        #while the python_callable argument for PythonOperators only receives objects of type callable not strings.
        python_callable = eval(callableFunction),
        op_kwargs = args,
        xcom_push = True,
        dag = dag,
    )
    return task


end = DummyOperator(
    task_id='end',
    dag=dag)



with open('/usr/local/airflow/dags/config_files/dynamicDagConfigFile.yaml') as f:
    configFile = yaml.safe_load(f)

    #Extract file list
    S3Files = configFile['S3Files']

    #In this loop tasks are created for each table defined in the YAML file
    for S3File in S3Files:
        for S3File, fieldName in S3File.items():

            #Remember task id is provided in order to exchange data among tasks generated in dynamic way.
            get_s3_files = createDynamicDAG('{}-getS3Data'.format(S3File), 
                                            'getS3Data', 
                                            {}) #your configs here.

            #Second step is upload S3 to GCS
            upload_s3_toGCS = createDynamicDAG('{}-uploadDataS3ToGCS'.format(S3File), 'uploadDataS3ToGCS', {'previous_task_id':'{}-'})

#write your configs again here like S3 bucket name prefix extra or read from yaml file, and other GCS config.


start >> get_s3_files
get_s3_files >> upload_s3_toGCS
upload_s3_toGCS >> end

Дуже дякую. тож однією з проблем у мене було те, що трапиться, якщо нових файлів немає? Одна з проблем, з якою я стикаюся, полягає в тому, що в цьому місці завжди будуть файли, але не гарантовано НОВІ файли, які потрібно витягнути, а це означає, що розділ upload_s3_toGCSне буде існувати та помилка в потоці повітря.
arcee123

Вирішити проблему можна, видаливши файли з yamlфайлу, коли всі ці файли завантажуються в GCS, таким чином у ньому будуть присутні лише нові файли yaml. І якщо немає нових файлів, yamlфайл буде порожнім і не буде створено динамічного дага. Ось чому yamlфайл є набагато кращим варіантом порівняно зі збереженням файлів у списку.
ahwin agrawal

Цей yamlфайл також допоможе підтримувати ведення журналу файлів s3 таким чином, якщо припустимо, що деякі файли s3 не можуть бути завантажені в GCS, тоді ви також можете підтримувати прапор, відповідний цьому файлу, а потім повторити їх під час наступного запуску DAG.
ahwin agrawal

І якщо немає нових файлів, ви можете поставити ifумову перед DAG, яка перевірить наявність нових файлів у yamlфайлах, якщо є нові файли, виконайте це, інакше пропустіть його.
ahwin agrawal

Проблема тут полягає в тому, що встановлюються нижні потоки. якщо потоки потоків встановлені без фактичних завдань (оскільки файлів не існує), це помилка.
arcee123
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.