简体   繁体   English

firebase function,计数器更新时间过长

[英]firebase function, counter takes too long to update

I have implemented a counter variable in my real time database, the users are able to call the function and upon 4 calls it should run some code.我已经在我的实时数据库中实现了一个计数器变量,用户可以调用 function 并且在 4 次调用时它应该运行一些代码。 The problem is that it may take too long to run and update the counter, so if the counter = 1 and two people call the function at a relative same time both of them will get问题是运行和更新计数器可能需要很长时间,所以如果计数器 = 1 并且两个人同时调用 function 他们两个都会得到

datasnapshot.child("counter").val() = 1 

and both of them will set他们都将设置

promises.push(admin.database().ref('game/' + matchkey + '/counter').set((counter + 1))) = 2

while it should be 3虽然应该是 3

how can I fix this?我怎样才能解决这个问题? (I guess by either making the update faster or forcing the function to wait for the previous one to end (not sure if possible), or something else that I am missing I am new to firebase & ts) (我想要么加快更新速度,要么强制 function 等待前一个结束(不确定是否可能),或者我错过的其他东西我是 firebase 和 ts 的新手)

this is my code:这是我的代码:

exports.functionTST = functions.https.onCall(async (data, context) => {
  const promises = [];
  JSON.parse(JSON.stringify(data), (key, value) => {
     parse values 
  });

  //set values
  promises.push(admin.database().ref('game/' + matchkey + '/'...).update({
      val1: val2,
    }))

    //counter
    admin.database().ref('game/' + matchkey).once('value')
      .then(datasnapshot => {
        let counter = datasnapshot.child("counter").val()
        if (counter === null) {
          promises.push(admin.database().ref('game/' + matchkey + '/counter').set(1))
        } else if (counter < 3) {
          promises.push(admin.database().ref('game/' + matchkey + '/counter').set((counter + 1)))
        } else if (counter == 3) {
          promises.push(admin.database().ref('game/' + matchkey + '/counter').remove())
          //do other stuff
         }
     });

    Promise.all(promises);
    return null
  }
});

Thanks !!谢谢 !!

First, let's fix your indentation.首先,让我们修复您的缩进。

exports.functionTST = functions.https.onCall(async (data, context) => {
  const promises = [];
  JSON.parse(JSON.stringify(data), (key, value) => {
     // parse values 
  });

  // set values
  promises.push(
    admin.database()
      .ref('game/' + matchkey + '/'...)
      .update({
        val1: val2,
      })
  );

  // counter
  admin.database()
    .ref('game/' + matchkey)
    .once('value')
    .then(datasnapshot => {
      let counter = datasnapshot.child("counter").val()
      if (counter === null) {
        promises.push(admin.database().ref('game/' + matchkey + '/counter').set(1))
      } else if (counter < 3) {
        promises.push(admin.database().ref('game/' + matchkey + '/counter').set((counter + 1)))
      } else if (counter == 3) {
        promises.push(admin.database().ref('game/' + matchkey + '/counter').remove())
        // do other stuff
      }
    });

  Promise.all(promises);
  return null
});

From this, we can now see a few issues.从这里,我们现在可以看到一些问题。

The most notable error, is that you aren't awaiting or returning the Promise chain properly.最值得注意的错误是您没有正确等待或返回Promise链。 When you don't wait for the promises to resolve, your function will get severely throttled and network requests (such as updating the database) can be skipped entirely.如果您不等待承诺解决,您的 function 将受到严重限制,并且可以完全跳过网络请求(例如更新数据库)。

Promises.all(promises)
return null; // return no data

should be应该

await Promises.all(promises) // <-- note addition of await here
return null; // return no data

or或者

return Promises.all(promises) // <-- note moving return here
  .then(() => null) // return no data

This next block of code does nothing useful.下一个代码块没有任何用处。 Were you meant to store the result of JSON.parse into a new variable?您是否打算将JSON.parse的结果存储到一个新变量中? Depending on what you are trying to achieve Object.entries(data) might be a better choice.根据您要实现的目标Object.entries(data)可能是更好的选择。

JSON.parse(JSON.stringify(data), (key, value) => {
  // parse values 
});

This next block of code spawns a floating Promise - you aren't returning it, nor are you storing it into an array like promises - so it will cause inconsistent behaviour (like never updating the counter).下一个代码块产生一个浮动的Promise - 你没有返回它,也没有将它存储到一个类似promises的数组中 - 所以它会导致不一致的行为(比如从不更新计数器)。

admin.database()
  .ref('game/' + matchkey)
  .once('value')
  .then(/* ... */)

The entirety of the code shown in this block should be replaced by a transaction operation instead to streamline the database updates and ensure that it is properly updated.此块中显示的全部代码应替换为事务操作,以简化数据库更新并确保正确更新。

// set values
promises.push(
  admin.database()
    .ref('game/' + matchkey + '/'...)
    .update({
      val1: val2,
    })
);

// counter
admin.database()
  .ref('game/' + matchkey)
  .once('value')
  .then(datasnapshot => {
    let counter = datasnapshot.child("counter").val()
    if (counter === null) {
      promises.push(admin.database().ref('game/' + matchkey + '/counter').set(1))
    } else if (counter < 3) {
      promises.push(admin.database().ref('game/' + matchkey + '/counter').set((counter + 1)))
    } else if (counter == 3) {
      promises.push(admin.database().ref('game/' + matchkey + '/counter').remove())
      //do other stuff
    }
  });

Promises.all(promises)
return null;

Adapting the transaction example and merging your other logic gives:调整事务示例并合并您的其他逻辑给出:

//counter
await admin.database() // <-- note addition of await here
  .ref('game/' + matchkey)
  .transaction((matchInfo) => {
    if (matchInfo) {
      // set updated values
      matchInfo[/* The '...' from .ref('game/' + matchkey + '/'...) */].val1 = val2;
      // e.g.
      // matchInfo.round1.val1 = val2;
      // OR
      // matchInfo.round1 = Object.assign(
      //   matchInfo.round1 || {}, // <-- creates the property when it is missing
      //   {
      //     val1: val2
      //   }
      // );

      // handle & update counter
      const counter = matchInfo.counter || 0;
      if (counter < 3) {
        matchInfo.counter = counter + 1;
      } else {
        // clear counter and do other stuff
        delete matchInfo.counter;
        // e.g.
        // matchInfo.winner = matchInfo.players[0]
      }
    }
    return matchInfo; // apply changes
  });

return null; // return no data to the caller

After implementing the above block, your function can become something similar to:实现上述块后,您的 function 可以变成类似于:

exports.functionTST = functions.https.onCall(async (data, context) => {
  // parse request body
  const matchkey = data.matchkey;
  /* ... */

  // update match information
  await admin.database() // <-- note addition of await here
    .ref('game/' + matchkey)
    .transaction((matchInfo) => {
      /* ... */
    })
  
  return null;

To ensure that the function returns appropriate errors, you should surround each part in try-catch blocks and throw an appropriate HttpsError so failures can be handled gracefully by the caller on the client side:为确保 function 返回适当的错误,您应该围绕 try-catch 块中的每个部分并抛出适当的HttpsError以便客户端的调用者可以优雅地处理故障:

exports.functionTST = functions.https.onCall(async (data, context) => {
  // parse request body
  try {
    const matchkey = data.matchkey;
    /* ... */
  } catch (err) {
    console.log('Unexpected error while handling request data: ', err);
    throw new functions.https.HttpsError('invalid-argument', 'Could not parse request data');
  }

  // update match information
  try {
    await admin.database() // <-- note addition of await here
      .ref('game/' + matchkey)
      .transaction((matchInfo) => {
        /* ... */
      })
  } catch (err) {
    console.log('Unexpected error while updating match information: ', err);
    throw new functions.https.HttpsError('internal', 'Could not update match information');
  }
  
  return null; // return no data to the caller
}

First run after deploy take time because the cold start happening, And consider using transaction to prevent race condition.部署后首次运行需要时间,因为冷启动发生,并考虑使用事务来防止竞争条件。

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

相关问题 ffmpeg lambda 花费的时间太长 - ffmpeg lambda takes too long 更新firebase云function数据库太慢 - update Database on firebase cloud function too slow 我的 firebase function 更新文档第一次执行需要 MINUTES - My firebase function update document takes MINUTES to execute the first time 为什么在没有兼容记录的表上进行内部连接需要太长时间? - Why Inner Join on table where is no compatible records - takes too long? 从 Composer 触发时,DataFlow 作业启动时间过长 - DataFlow Job Startup Takes Too Long When triggered from Composer Azure Stream 分析处理事件的时间太长 - Azure Stream Analytics takes too long to process the events Firebase Cloud Function CreateUser 上 Firestore 中的增量计数器 - Firebase Cloud Function increment counter in Firestore on CreateUser 我应该使用 Cloud Functions 还是客户端逻辑来更新 Firebase 中的计数器? - Shall I use Cloud Functions or client logic to update a counter in Firebase? Firebase authstate 需要很长时间才能在慢速连接上对用户进行身份验证 - React Native - Firebase authstate takes long to authenticate user on slow connection - React Native Firebase RTDB.update function 不起作用? - Firebase RTDB .update function not working?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM