简体   繁体   English

Google Cloud Function:删除 firestore 文档并插入 SQL 查询的结果导致超时

[英]Google Cloud Function : Delete firestore docs and insert the result of a SQL Query leads to timeout

I have a cloud function that is triggered by a crontab to compute statistics and insert the results in Firestore.我有一个由 crontab 触发的云函数来计算统计信息并将结果插入 Firestore。

My cloud function delete the previous stats in firestore and then execute a CloudSQL query (MySQL) and batch insert the result in firestore.我的云函数删除了 firestore 中的先前统计信息,然后执行 CloudSQL 查询(MySQL)并将结果批量插入 firestore。

The MySQL Query respond nearly instantly. MySQL 查询几乎立即响应。

The execution takes the maximum of 9 minutes to execute which is not possible.执行最多需要 9 分钟才能执行,这是不可能的。

I think I may have an issue with the chaining of promise.我想我可能对 promise 的链接有问题。 The MySQL lib is not supporting promise natively and I'm new to this. MySQL 库本身不支持承诺,我是新手。

The full code is here : https://github.com/dev-mansonthomas/RedCrossQuestCloudFunctions/blob/master/RCQ/ULQueteurStatsPerYear/index.js完整代码在这里: https : //github.com/dev-mansonthomas/RedCrossQuestCloudFunctions/blob/master/RCQ/ULQueteurStatsPerYear/index.js

'use strict';
const mysql     = require('mysql');

const {Firestore} = require('@google-cloud/firestore');
const firestore   = new Firestore ({projectId:process.env.TARGET_PROJECT_ID});
const settings    = {timestampsInSnapshots: true};
firestore.settings(settings);

const fsCollectionName = 'ul_queteur_stats_per_year';


const connectionName = process.env.INSTANCE_CONNECTION_NAME || null;
const dbUser         = process.env.SQL_USER                 || null;
const dbPassword     = process.env.SQL_PASSWORD             || null;
const dbName         = process.env.SQL_DB_NAME              || null;

const mysqlConfig = {
  connectionLimit : 1,
  user            : dbUser,
  password        : dbPassword,
  database        : dbName,
};
if (process.env.NODE_ENV === 'production') {
  mysqlConfig.socketPath = `/cloudsql/${connectionName}`;
}

// Connection pools reuse connections between invocations,
// and handle dropped or expired connections automatically.
let mysqlPool;

const queryStr = ['the big SQL query'].join('\n');

exports.ULQueteurStatsPerYear = (event, context) => {

  const pubsubMessage = event.data;
  const parsedObject  = JSON.parse(Buffer.from(pubsubMessage, 'base64').toString());
  const ul_id         = parsedObject.id;
  // Initialize the pool lazily, in case SQL access isnt needed for this
  // GCF instance. Doing so minimizes the number of active SQL connections,
  // which helps keep your GCF instances under SQL connection limits.
  if (!mysqlPool)
  {
    mysqlPool = mysql.createPool(mysqlConfig);
  }

  //delete current stats of the UL
  let deleteCollection = function(path)
  {
    console.log("removing documents on collection '"+path+"' for ul_id="+ul_id);
    // Get a new write batch
    let batch = firestore.batch();

    return firestore.collection(path).listDocuments().then(val => {
      val.map((val) => {
        if(val.ul_id === ul_id)
        {
          batch.delete(val)
        }

      });

      return batch.commit();
    });
  };


  //then inserting new one
  return deleteCollection("ULQueteurStatsPerYear").then(
    ()=>
    {
      return new Promise((resolve, reject) => {
        mysqlPool.query(
          queryStr,
          [ul_id],
          (err, results) => {
            if (err)
            {
              console.error(err);
              reject(err);
            }
            else
            {
              if(results !== undefined && Array.isArray(results) && results.length >= 1)
              {
                const batch       = firestore.batch();
                const collection  = firestore.collection(fsCollectionName);
                let i = 0;
                results.forEach(
                  (row) =>
                  {
                    //console.log("ULQueteurStatsPerYear : inserting row for UL "+ul_id+" "+JSON.stringify(row));
                    const docRef = collection.doc();
                    //otherwise we get this error from firestore : Firestore doesn’t support JavaScript objects with custom prototypes (i.e. objects that were created via the “new” operator)
                    batch.set(docRef, JSON.parse(JSON.stringify(row)));
                  });

                return batch.commit().then(() => {

                  let logMessage = "ULQueteurStatsPerYear for UL='"+parsedObject.name+"'("+ul_id+") : "+i+" rows inserted";

                  console.log(logMessage);
                  resolve(logMessage);
                });
              }
            }
          });
      });

    });
};

The issue in my code was我的代码中的问题是

if(results !== undefined && Array.isArray(results) && results.length >= 1)

on the 'else' part of this condition, I wasn't calling either 'resolve' or 'reject' functions from the promise.在此条件的“其他”部分,我没有从承诺中调用“解决”或“拒绝”函数。

Which explains why it ended in timeout.这解释了为什么它以超时结束。

(and the fact the query returned no row, was because I used a named parameter while mysql only support '?'). (事实上​​,查询没有返回任何行,是因为我使用了一个命名参数,而 mysql 只支持 '?')。

I'm not sure if it helped, but I've also change the way I return a promise.我不确定它是否有帮助,但我也改变了我回报承诺的方式。

I've switch to :我已经切换到:

 return new Promise((resolve, reject) => {

    deleteCollection(fsCollectionName).then(
      ()=>
      {
        console.log("running query for UL : "+ul_id);
        mysqlPool.query(
          queryStr,

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM