从 Google Cloud Functions 调用 BigQuery 的随机 `ApiError: Invalid Credentials`

Random `ApiError: Invalid Credentials` calling BigQuery from Google Cloud Functions

我的函数每小时调用 15 次(每 4 分钟一次),并通过 startQuery 运行查询。大约 30 分钟后随机发生"无效凭据"错误。它发生得越来越频繁,直到所有调用都失败。

此查询从数据集中的表中读取数据,并通过选项 destinationwriteDisposition=WRITE_TRUNCATE 将结果保存到位于另一个数据集中的表中。
这两个数据集位于欧盟。

重新部署该功能可暂时消除问题。

gcloud beta functions describe my-function 的调用表明它使用 App Engine 默认服务帐户:[email protected].

以下是错误详情:

1
2
3
4
5
6
7
8
9
10
11
ApiError: Invalid Credentials
at Object.parseHttpRespBody (/user_code/node_modules/@google-cloud/bigquery/node_modules/@google-cloud/common/src/util.js:192:30)
at Object.handleResp (/user_code/node_modules/@google-cloud/bigquery/node_modules/@google-cloud/common/src/util.js:132:18)
at /user_code/node_modules/@google-cloud/bigquery/node_modules/@google-cloud/common/src/util.js:465:12
at Request.onResponse [as _callback] (/user_code/node_modules/@google-cloud/bigquery/node_modules/retry-request/index.js:160:7)
at Request.self.callback (/user_code/node_modules/@google-cloud/bigquery/node_modules/request/request.js:188:22)
at emitTwo (events.js:106:13)
at Request.emit (events.js:191:7)
at Request. (/user_code/node_modules/@google-cloud/bigquery/node_modules/request/request.js:1171:10)
at emitOne (events.js:96:13)
at Request.emit (events.js:188:7)

编辑

代码,剥离:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
const bigquery = require('@google-cloud/bigquery')();

const destinationDataset = bigquery.dataset('destinationDataset');
const destinationTable = dataset.table('destinationTable');

exports.aggregate = function aggregate (event) {
  const message = event.data;
  const attributes = message.attributes;

  let job;
  let destinationTable;
  return Promise.resolve()
    .then(() => {
      if (attributes.partition == null) {
        throw new Error('Partition time not provided. Make sure you have a"partitionTime" attribute in your message');
      }

      const query = 'SELECT ... FROM sourceTable WHERE _PARTITIONTIME = TIMESTAMP(@partitionTime)'; // The dataset is specified in the job options below

      // Query options list: https://cloud.google.com/bigquery/docs/reference/v2/jobs/query
      // and: https://googlecloudplatform.github.io/google-cloud-node/#/docs/bigquery/0.9.6/bigquery?method=startQuery
      const options = {
        destination: destinationTable,
        writeDisposition: 'WRITE_TRUNCATE',
        query: query,
        defaultDataset: { datasetId: 'sourceDataset' },
        timeoutMs: 540000, // 9 minutes, same timeout as the Cloud Function
        useLegacySql: false,
        parameterMode: 'NAMED',
        queryParameters: [{
          name: 'partitionTime',
          parameterType: { type: 'STRING' },
          parameterValue: { value: attributes.partition }
        }]
      };

      return bigquery.startQuery(options);
    })
    .then((results) => {
      job = results[0];
      console.log(`BigQuery job ${job.id} started, generating data in ${destinationTable.dataset.id}.${destinationTable.id}.`);
      return job.promise();
    })
    .then((results) => {
      // Get the job's status
      return job.getMetadata();
    })
    .then((metadata) => {
      // Check the job's status for errors
      const errors = metadata[0].status.errors;
      if (errors && errors.length > 0) {
        throw errors;
      }
    })
    // Only if a destination table is given
    .then(() => {
      console.log(`BigQuery job ${job.id} completed, data generated in ${destinationTable.dataset.id}.${destinationTable.id}.`);
    })
    .catch((err) => {
      console.log(`Job failed for ${inspect(attributes)}: ${inspect(err)}`);
      return Promise.reject(err);
    });
};

你会注意到我在初始化 bigquery 对象时没有给出任何选项:require('@google-cloud/bigquery')().

我是否应该创建一个角色为 BigQuery 作业用户的服务帐户,并使用 RuntimeConfig API 来避免将凭据推送到 git 源?

问题仍然是为什么我会随机收到此错误。现在查看函数日志,在 CEST 午夜到凌晨 4 点之间的每个调用中都发生了错误,然后在凌晨 5:36 之前的三分之一调用中发生了错误。从那时起(4 小时前)它就没有发生过一次。

编辑 2

这显示了失败调用与成功调用相比的频率。所有错误(绿色)都是"无效凭据"错误。在这 7 天里,绝对没有任何事情发生:没有部署、没有更改配置、没有在 BigQuery 中摆弄。

frequency


我的解决方法是使用 GCE 中的 VM 并运行 nodejs 应用程序来package函数,例如

1
2
3
4
5
6
7
const pubsub = require('@google-cloud/pubsub')();
const topic = pubsub.topic('...');
const sub = topic.subscribe('...', { ...options });
sub.on('message', msg => {
   // the same cloud function does all bigquery data loading and querying stuff...
   callthesamecloudfunction({ data: msg });
});

取决于 GCF 中使用哪种触发器,package器可能不同;

好的方面是package器足够简单,它已经在我们的生产环境中运行了几个月,没有任何问题;

我的结论是 GCloud 在 Beta 中运行许多服务,并没有真正为生产做好准备;我会远离 GCF,或者可能会在 6 个月后重新访问。

编辑:请参阅 https://issuetracker.google.com/issues/66695033 GCloud 团队声称已修复但我还没有时间测试;我的package方式只需要一个云虚拟机,而且非常便宜。