如何在Apache Beam中动态跟踪状态?

时间:2019-05-22 17:33:45

标签: python python-2.7 google-cloud-dataflow apache-beam dataflow

我正在用Apache Beam编写数据验证脚本。每当新文件上传到Google Cloud Storage时,该脚本都会从PubSub接收消息,下载文件,并对文件进行一系列预定义的测试。 在这些测试结束时,我需要通过电子邮件发送所有未通过测试的行的日志。

为了不多次发送电子邮件,我做了一些阅读,并相信我可以使用Beam中的状态和计时器构造发送一次电子邮件。但是,每个文件都会有不同数量的错误,因此,如何设置文件发送之前发送X个元素,而每个元素都是一个错误,而不是硬编码。

我尝试使用带有COUNT_STATE的DoFn来计数传递给它的元素,但是关于该元素是Pcollection而不是K,V元组,我遇到了另一个错误。

这是管道代码:

with beam.Pipeline(options=pipeline_options) as p:
    # Read Lines from data
    validation = (p
                | "Read Element From PubSub" >> beam.io.ReadFromPubSub (topic=known_args.input_topic)
                | 'Filter Messages' >> beam.ParDo(FilterMessageDoFn(known_args.project, t_options.dataset_id))
                | 'After filter' >> beam.ParDo(DebugFn("DATA VALIDATION: PROCESSING FILE...", show_trace))
                | 'Generate Schemas' >> beam.ParDo(GetSchemaFn(known_args.project, t_options.validation_home_path))
                | 'After GetSschema' >> beam.ParDo(DebugFn("DATA VALIDATION: After OBTAINING SCHEMA...", show_trace))
                | 'Validate' >> beam.ParDo(ValidateFn(known_args.project)).with_outputs(
                ValidateFn.TAG_VALIDATION_GLOBAL_FAILURE,
                ValidateFn.TAG_VALIDATION_CONTENT_FAILURE,
                ValidateFn.TAG_VALIDATION_CONTENT_SUCCESS,
                main='lines')

to_be_joined = ([validation[ValidateFn.TAG_VALIDATION_GLOBAL_FAILURE], 
                validation[ValidateFn.TAG_VALIDATION_CONTENT_FAILURE]]
               | "Group By Key" >> beam.Flatten()
               | 'Persist Global Errors to Big Query' >> beam.ParDo(PersistErrorsFn(known_args.project))
               | 'Debug Errors' >> beam.ParDo(DebugFn("DATA VALIDATION: VALIDATION ERRORS", show_trace))
               | 'Save Global Errors' >> beam.io.WriteToBigQuery('data_management.validation_errors',                                                                 
              project=known_args.project,
              schema=TABLE_SCHEMA, 
              create_disposition=beam.io.BigQueryDisposition.CREATE_IF_NEEDED,
              write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND
)

基本上,我想在写入BigQuery之前插入一个步骤,以发送一封电子邮件,该电子邮件仅在收到VALIDATION_GLOBAL_FAILURE + VALIDATION_CONTENT_FAILURE错误数时发送。

谢谢!

1 个答案:

答案 0 :(得分:1)

这个想法是,您想要对两个包含验证失败的CoGroupByKey执行PCollections,然后应用DoFn,将您的电子邮件发送逻辑应用于结果。

目前尚不清楚管道中的类型是什么,但我将假设ValidateFn(file name, validation error)ValidateFn.TAG_VALIDATION_GLOBAL_FAILURE输出ValidateFn.TAG_VALIDATION_CONTENT_FAILURE元组。

class SendEmail(beam.DoFn):
  def process(self, element):
    file_name = element[0]
    iterable_of_global_failures = element[1].get(ValidateFn.TAG_VALIDATION_GLOBAL_FAILURE)
    iterable_of_content_failures = element[1].get(ValidateFn.TAG_VALIDATION_CONTENT_FAILURE)
    ... format and send e-mail if iterables satisfy requirements ...


# create a dict containing the tag to PCollection mapping for what we want to group together.
validation = (p
              | "Read Element From PubSub" >> beam.io.ReadFromPubSub (topic=known_args.input_topic)
              | 'WindowInto' >> beam.WindowInto(FixedWindows(1))
              | ...

validation_errors = {key: validation[key] for key in [ValidateFn.TAG_VALIDATION_GLOBAL_FAILURE, ValidateFn.TAG_VALIDATION_CONTENT_FAILURE]}

(validation_errors
 | 'CoGroupByKey' >> beam.CoGroupByKey()
 | 'Send Email' >> beam.ParDo(SendEmail())

由于来自PubsubIO的每个输入记录都代表文件名,并且随后将其扩展为所有相关记录,因此这些记录将共享该文件所属的PubsubIO消息的相同时间戳。这使我们可以在分组时使用非常小的窗口大小,从而导致较小的组和更好的性能。指定WindowInto是必要的,这样我们就不会使用GlobalWindow,因为CoGroupByKey将永远不会触发输出的发生。您可以了解有关流式传输,窗口化和触发[12]的更多信息。