【问题标题】:How can I write to Big Query using a runtime value provider in Apache Beam?如何使用 Apache Beam 中的运行时值提供程序写入 Big Query?
【发布时间】:2020-03-31 17:28:28
【问题描述】:

编辑:我使用 beam.io.WriteToBigQuery 并启用了接收器实验选项,使其工作。我实际上有它,但我的问题是我试图从包装在 str() 中的两个变量(数据集 + 表)“构建”完整的表引用。这是将整个值提供程序参数数据作为字符串而不是调用 get() 方法来获取值。

操作

我正在尝试生成一个数据流模板,然后从 GCP 云函数调用。(作为参考,我的数据流作业应该读取一个包含一堆文件名的文件,然后从 GCS 读取所有这些文件并写入到 BQ)。 因此,我需要以这样的方式编写它,以便我可以使用运行时值提供程序来传递 BigQuery 数据集/表。

我的帖子底部是我目前的代码,省略了一些与问题无关的内容。 请特别注意 BQ_flexible_writer(beam.DoFn) - 这就是我尝试“自定义” beam.io.WriteToBigQuery 的地方,以便它接受运行时值提供程序。

我的模板生成良好,当我在不提供运行时变量(依赖于默认值)的情况下测试运行管道时,它会成功,并且我在控制台中查看作业时看到添加的行。但是,在检查 BigQuery 时没有数据(三次检查日志中的数据集/表名称是否正确)。不确定它的去向或我可以添加哪些日志记录以了解元素发生了什么?

有什么想法吗?或者关于如何使用运行时变量写入 BigQuery 的建议?我什至可以按照我在 DoFn 中包含它的方式调用 beam.io.WriteToBigQuery,还是必须获取 beam.io.WriteToBigQuery 背后的实际代码并使用它?

#=========================================================

class BQ_flexible_writer(beam.DoFn):
    def __init__(self, dataset, table):
        self.dataset = dataset
        self.table = table

    def process(self, element):
        dataset_res = self.dataset.get()
        table_res = self.table.get()
        logging.info('Writing to table: {}.{}'.format(dataset_res,table_res))
        beam.io.WriteToBigQuery(
        #dataset= runtime_options.dataset,
        table = str(dataset_res) + '.' + str(table_res), 
        schema = SCHEMA_ADFImpression,
        project = str(PROJECT_ID), #options.display_data()['project'],
        create_disposition = beam.io.BigQueryDisposition.CREATE_IF_NEEDED,  #'CREATE_IF_NEEDED',#create if does not exist.
        write_disposition = beam.io.BigQueryDisposition.WRITE_APPEND    #'WRITE_APPEND' #add to existing rows,partitoning
        )
# https://cloud.google.com/dataflow/docs/guides/templates/creating-templates#valueprovider
class FileIterator(beam.DoFn):
    def __init__(self, files_bucket):
        self.files_bucket = files_bucket

    def process(self, element):
        files = pd.read_csv(str(element), header=None).values[0].tolist()
        bucket = self.files_bucket.get()
        files = [str(bucket) + '/' + file for file in files]
        logging.info('Files list is: {}'.format(files))
        return files

# https://stackoverflow.com/questions/58240058/ways-of-using-value-provider-parameter-in-python-apache-beam   
class OutputValueProviderFn(beam.DoFn):
    def __init__(self, vp):
        self.vp = vp

    def process(self, unused_elm):
        yield self.vp.get()


class RuntimeOptions(PipelineOptions):
    @classmethod
    def _add_argparse_args(cls, parser):

        parser.add_value_provider_argument(
          '--dataset',
          default='EDITED FOR PRIVACY',
          help='BQ dataset to write to',
          type=str)

        parser.add_value_provider_argument(
          '--table',
          default='EDITED FOR PRIVACY',
          required=False,
          help='BQ table to write to',
          type=str)

        parser.add_value_provider_argument(
          '--filename',
          default='EDITED FOR PRIVACY',
          help='Filename of batch file',
          type=str)

        parser.add_value_provider_argument(
          '--batch_bucket',
          default='EDITED FOR PRIVACY',
          help='Bucket for batch file',
          type=str)

        #parser.add_value_provider_argument(
        #   '--bq_schema',
          #default='gs://dataflow-samples/shakespeare/kinglear.txt',
        #  help='Schema to specify for BQ')

        #parser.add_value_provider_argument(
        #   '--schema_list',
          #default='gs://dataflow-samples/shakespeare/kinglear.txt',
        #  help='Schema in list for processing')

        parser.add_value_provider_argument(
          '--files_bucket',
          default='EDITED FOR PRIVACY',
          help='Bucket where the raw files are',
          type=str)

        parser.add_value_provider_argument(
          '--complete_batch',
          default='EDITED FOR PRIVACY',
          help='Bucket where the raw files are',
          type=str)
#=========================================================

def run():
    #====================================
    # TODO PUT AS PARAMETERS 
    #====================================
    JOB_NAME_READING = 'adf-reading'
    JOB_NAME_PROCESSING = 'adf-'

    job_name = '{}-batch--{}'.format(JOB_NAME_PROCESSING,_millis())

    pipeline_options_batch = PipelineOptions()

    runtime_options = pipeline_options_batch.view_as(RuntimeOptions)

    setup_options = pipeline_options_batch.view_as(SetupOptions)
    setup_options.setup_file  = './setup.py'
    google_cloud_options = pipeline_options_batch.view_as(GoogleCloudOptions)
    google_cloud_options.project = PROJECT_ID
    google_cloud_options.job_name = job_name
    google_cloud_options.region = 'europe-west1'
    google_cloud_options.staging_location = GCS_STAGING_LOCATION
    google_cloud_options.temp_location = GCS_TMP_LOCATION


    #pipeline_options_batch.view_as(StandardOptions).runner = 'DirectRunner'

    # # If datflow runner [BEGIN]
    pipeline_options_batch.view_as(StandardOptions).runner = 'DataflowRunner'
    pipeline_options_batch.view_as(WorkerOptions).autoscaling_algorithm = 'THROUGHPUT_BASED'

    #pipeline_options_batch.view_as(WorkerOptions).machine_type = 'n1-standard-96' #'n1-highmem-32' #' 
    pipeline_options_batch.view_as(WorkerOptions).max_num_workers = 10
    #  [END]

    pipeline_options_batch.view_as(SetupOptions).save_main_session = True
    #Needed this in order to pass table to BQ at runtime
    pipeline_options_batch.view_as(DebugOptions).experiments = ['use_beam_bq_sink']


    with beam.Pipeline(options=pipeline_options_batch) as pipeline_2:

        try:

            final_data = (
            pipeline_2
            |'Create empty PCollection' >> beam.Create([None])
            |'Get accepted batch file 1/2:{}'.format(OutputValueProviderFn(runtime_options.complete_batch)) >> beam.ParDo(OutputValueProviderFn(runtime_options.complete_batch))
            |'Get accepted batch file 2/2:{}'.format(OutputValueProviderFn(runtime_options.complete_batch)) >> beam.ParDo(FileIterator(runtime_options.files_bucket))
            |'Read all files' >> beam.io.ReadAllFromText(skip_header_lines=1)
            |'Process all files' >> beam.ParDo(ProcessCSV(),COLUMNS_SCHEMA_0)
            |'Format all files' >> beam.ParDo(AdfDict())
            #|'WriteToBigQuery_{}'.format('test'+str(_millis())) >> beam.io.WriteToBigQuery(
            #        #dataset= runtime_options.dataset,
            #        table = str(runtime_options.dataset) + '.' + str(runtime_options.table), 
            #        schema = SCHEMA_ADFImpression,
            #        project = pipeline_options_batch.view_as(GoogleCloudOptions).project, #options.display_data()['project'],
            #        create_disposition = beam.io.BigQueryDisposition.CREATE_IF_NEEDED,  #'CREATE_IF_NEEDED',#create if does not exist.
            #        write_disposition = beam.io.BigQueryDisposition.WRITE_APPEND    #'WRITE_APPEND' #add to existing rows,partitoning
            #        )
            |'WriteToBigQuery' >> beam.ParDo(BQ_flexible_writer(runtime_options.dataset,runtime_options.table))
            )
        except Exception as exception:
            logging.error(exception)
            pass

【问题讨论】:

  • 您在 Beam 管道中取得了成功,但您的数据集/表中没有数据?您如何在 bigquery 中执行检查?去预览标签?您知道工作流开始时的数据集/表吗?还是只在过程的中间?
  • 我在尝试使用 ParDo 函数中的 WriteToBigQuery 时遇到了同样的问题。如果您找到了解决方案,请告诉我。我猜 WriteToBigQuery 会期望 Pcollection 而不是行明智的方法,这就是它无法从 ParDo fn 执行的原因。
  • @BhaskarBhuyan 是的,下面的 Chamikara 确认它不起作用。你不能在 ParDo 之后添加 BQ I/O 适配器吗?

标签: python google-cloud-platform runtime google-cloud-dataflow apache-beam


【解决方案1】:

请使用以下附加选项运行它。

--experiment=use_beam_bq_sink

如果没有这个,Dataflow 当前会使用不支持 ValueProviders 的本机版本覆盖 BigQuery 接收器。

另外,请注意,不支持将数据集设置为运行时参数。请尝试将表参数指定为整个表引用(DATASET.TABLE 或 PROJECT:DATASET.TABLE)。

【讨论】:

  • 谢谢。我已经有了这个,就在我的管道开始之前。我看到它列在控制台数据流作业的实验变量中。你能确认我的代码是否正常吗?
  • 似乎您正试图直接从 DoFn.process() 方法调用转换。这是行不通的。 Beam WriteToBigQuery 转换(只要您使用上述实验)将允许在参数“table”中指定完整的表引用(包括数据集)作为运行时值提供程序。所以你应该可以直接使用它。
  • 最初就是这样,请查看管道中已注释掉的部分。我收到有关尝试从错误的上下文访问运行时变量的错误。我明天再试一次,然后将实际错误发布给您,并显示一个显示实验选项的作业屏幕。
  • 可能是因为您尝试将数据集设置为运行时参数。请注意,这不受支持。尝试将表参数指定为整个表引用(DATASET.TABLE 或 PROJECT:DATASET.TABLE)。
  • 我已经这样做了(从两个值提供者构建完整的表引用),但我想我会尝试提供一个。尝试生成模板时,我得到:ERROR:root:Expected a table reference (PROJECT:DATASET.TABLE or DATASET.TABLE) instead of RuntimeValueProvider(option: comp_table, type: str, default_value: 'Test.Test')。还有其他想法吗?似乎尽管使用了实验性功能 WriteToBigQuery 只是不接受运行时值提供程序?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-04-29
  • 2022-11-17
  • 1970-01-01
  • 2021-10-10
  • 2014-11-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多