tentando di creare sotto-sotto-valute dinamiche da un genitore basato su un array di nomi di file


10

Sto cercando di spostare i file s3 da un bucket "non eliminazione" (ovvero non posso eliminare i file) in GCS utilizzando il flusso d'aria. Non posso essere certo che i nuovi file saranno presenti tutti i giorni, ma devo verificare la presenza di nuovi file ogni giorno.

il mio problema è la creazione dinamica di sotto-valute. Se ci sono file, ho bisogno di subdags. Se NON ci sono file, non ho bisogno di sotto-tag. Il mio problema sono le impostazioni upstream / downstream. Nel mio codice, rileva i file, ma non dà il via ai sottomarini come dovrebbero. Mi manca qualcosa.

ecco il mio codice:

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

Che tipo di lavoro viene eseguito nel backend di questi DAGS sono questi sparklavori o alcuni pythonscript e cosa stai usando per eseguirlo come livyo in qualche altro metodo
ashwin agrawal

Mi dispiace, non capisco la domanda. puoi riformulare per favore?
arcee123,

Voglio dire, stai usando solo semplici script Python e non stai usando alcun processo Spark, giusto?
Ashwin Agrawal,

Sì. operatori semplici che sono predefiniti nel flusso d'aria. Voglio aggiungere operatori esistenti a una velocità dinamica basata su file contrassegnati in S3. Voglio importare in GCS.
arcee123,

Perché è filesun elenco vuoto?
Oluwafemi Sule,

Risposte:


3

Di seguito è riportato il modo consigliato per creare un DAG dinamico o un sub-DAG nel flusso d'aria, anche se ci sono anche altri modi, ma immagino che questo sarebbe ampiamente applicabile al tuo problema.

Innanzitutto, crea un file (yaml/csv)che includa l'elenco di tutti i s3file e posizioni, nel tuo caso hai scritto una funzione per memorizzarli nell'elenco, direi di memorizzarli in un yamlfile separato e caricarlo in fase di esecuzione in airflow env e quindi creare DAG.

Di seguito è riportato un yamlfile di esempio : dynamicDagConfigFile.yaml

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

È possibile modificare la Check_For_Filesfunzione per memorizzarli in un yamlfile.

Ora possiamo passare alla creazione dinamica di dag:

Definire innanzitutto due attività utilizzando operatori fittizi, quindi l'attività iniziale e quella finale. Tali compiti sono quelli su cui costruiremo i nostri DAGcreando dinamicamente compiti tra loro:

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

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

DAG dinamico: useremo PythonOperatorsnel flusso d'aria. La funzione dovrebbe ricevere come argomenti l'id dell'attività; una funzione python da eseguire, ovvero il python_callable per l'operatore Python; e una serie di arg da usare durante l'esecuzione.

Includere un argomento il task id. Pertanto, possiamo scambiare dati tra compiti generati in modo dinamico, ad esempio tramite XCOM.

È possibile specificare la funzione operativa all'interno di questo dag dinamico come 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

Infine in base alla posizione presente nel file yaml è possibile creare dags dinamici, leggere prima il yamlfile come di seguito e creare dag dinamico:

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.

Definizione finale del DAG:

L'idea è quella

#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

Codice del flusso d'aria completo nell'ordine:

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

Grazie mille. quindi uno dei problemi che ho avuto è stato cosa succede se non ci sono nuovi file? uno dei problemi che ho di fronte, è che ci saranno sempre file in questo posto, ma non NUOVI file garantiti da estrarre, il che significa che la sezione upload_s3_toGCSnon esisterà e che si verificherà un errore nel flusso d'aria.
arcee123,

È possibile risolvere il problema rimuovendo i file dal yamlfile una volta che tutti questi file sono stati caricati in GCS, in questo modo solo i nuovi file saranno presenti nel yamlfile. E nel caso in cui non ci siano nuovi file, il yamlfile sarà vuoto e non verrà creato alcun dag dinamico. Questo è il motivo per cui il yamlfile è un'opzione molto migliore rispetto alla memorizzazione di file in un elenco.
Ashwin Agrawal,

Il yamlfile aiuterà anche a mantenere la registrazione dei file s3 in un modo, se supponiamo che alcuni dei file s3 non possano essere caricati su GCS, quindi puoi anche mantenere un flag corrispondente a quel file e quindi riprovare alla successiva esecuzione del DAG.
Ashwin Agrawal,

E se non ci sono nuovi file puoi mettere una ifcondizione davanti al DAG che controllerà la presenza di nuovi file nei yamlfile se ci sono nuovi file eseguilo altrimenti salta.
Ashwin Agrawal,

il problema qui è che i downstream sono impostati. se i downstream sono impostati senza i lavori effettivi (perché non esistono file), si verificherà un errore.
arcee123,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.