Google云端数据流-WriteToBigQuery:“ NoneType”对象没有属性“ __getitem __”

时间:2019-05-01 13:40:54

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

尝试运行GCP Cloud-Dataflow管道时遇到问题。

当使用“ DirectRunner”在本地运行时,管道可以工作,但是当尝试使用“ DataflowRunner”在数据流中运行时,管道将失败。

在管道上调用run()并出现上述错误消息时,它立即立即失败(相对于首先部署到GCP,然后在实际运行管道时失败)。

在对beam.io.WriteToBigQuery的调用中引发了异常:

(bq_rows 
| 'map_to_row' >> beam.Map(to_pred_row)
| 'write_to_table' >> beam.io.WriteToBigQuery(
    'my_dataset_name.my_table_name', 
    write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND,
    create_disposition=beam.io.BigQueryDisposition.CREATE_IF_NEEDED))

如果我用仅写入文件的内容替换管道中的最后一个节点:

(bq_rows 
| 'map_to_row' >> beam.Map(to_pred_row)
| 'debug_write_to_csv_2' >> beam.io.WriteToText(additional_opts.out_path, ".txt"))

然后一切都按预期工作,我得到了一个文本文件,其中包含我期望的所有记录。

如果我使用WriteToBigQuery()函数按原样运行所有内容,但又改回DirectRunner(并进行其他任何更改),则一切正常,新行将写入BQ表。

据我所知,流入的记录没有什么特别的 WriteToBigQuery节点。我已经将它们输出到本地和云中运行的文本文件中,以找出导致此错误的原因,但是两个输出看起来都是相同的(并且与目标表的模式匹配)。无论如何,运行流时事情似乎还远远不够以至于出现了意外的值或参数-正如我提到的有关此错误的内容每当我在管道上调用run()

我要去哪里错了?


更新:

这是相同行为的最小示例。创建了一个名为temp_e.words的表时,该表具有一个名为word的单(STRING,REQUIRED)列,我可以使用以下代码来重现该行为:

import apache_beam as beam
from google.cloud import storage as gcs
import shutil
from google.cloud import bigquery as bq
import datetime
import os
import json
import apache_beam as beam
from apache_beam.options.pipeline_options import (GoogleCloudOptions, 
                                                  StandardOptions)


def to_row(word):
  return {
      'word': word
  }

def run_pipeline(local_mode):

  PROJECT = 'searchlab-data-insights'
  REGION = 'us-central1'
  GCS_BUCKET_PATH = 'gs://temp-staging-e'

  timestamp = datetime.datetime.now().strftime('%y%m%d-%H%M%S')

  options = beam.pipeline.PipelineOptions([
      '--project', PROJECT
  ])

  if local_mode:
    RUNNER = 'DirectRunner'
  else:
    RUNNER = 'DataflowRunner'

  google_cloud_options = options.view_as(GoogleCloudOptions)
  google_cloud_options.project = PROJECT
  google_cloud_options.job_name = 'test-{}'.format(timestamp)
  google_cloud_options.staging_location = os.path.join(GCS_BUCKET_PATH, 'staging')
  google_cloud_options.temp_location = os.path.join(GCS_BUCKET_PATH, 'tmp')
  options.view_as(StandardOptions).runner = RUNNER

  p = beam.Pipeline(RUNNER, options=options)
  bq_rows = p | beam.Create(['words', 'to', 'store']) 

  (bq_rows 
    | 'map_to_row' >> beam.Map(to_row)
    | 'write_to_table' >> beam.io.WriteToBigQuery(
        'temp_e.words', 
        write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND,
        create_disposition=beam.io.BigQueryDisposition.CREATE_IF_NEEDED)
  )

  job = p.run()

  if local_mode:
    job.wait_until_finish()
    print "Done!"

现在运行run_pipeline(local_mode=True)会产生正确的结果,并附加行,而运行run_pipeline(local_mode=False)会立即触发错误。

生成的完整错误在这里:https://pastebin.com/xx8wwtXV

1 个答案:

答案 0 :(得分:1)

仅当未提供对beam.io.WriteToBigQuery的调用的架构时,才会出现此问题。看来DirectRunner可以使用现有的表架构工作,但DataflowRunner不能。

在没有更好答案的情况下,我们可以通过显式提供模式来解决它。

因此,例如,在上面的最小示例中,我们可以使用以下代码:

(bq_rows 
| 'map_to__row' >> beam.Map(to_row)
| 'write_to_table' >> beam.io.WriteToBigQuery(
    'temp_e.words',
    schema={"fields":[{"type":"STRING","name":"word","mode":"REQUIRED"}]}
    write_disposition=beam.io.BigQueryDisposition.WRITE_APPEND,
    create_disposition=beam.io.BigQueryDisposition.CREATE_IF_NEEDED)
)