简体   繁体   English

Firestore 事务在云功能中使用时表现出意外行为

[英]Firestore transactions showing unexpected behaviour when used in cloud functions

I am writing an app that features an inventory in which users can reserve products.我正在编写一个应用程序,其中包含用户可以预订产品的库存。 I want to ensure that 2 users cannot simultaneously reserve a product at the same time, for this, I intend on using transactions.我想确保两个用户不能同时预订一个产品,为此,我打算使用交易。 When using transactions from the Firebase SDK, everything works as intended, but I am getting unexpected behavior when using transactions from a callable cloud function.使用 Firebase SDK 中的事务时,一切都按预期工作,但在使用可调用云 function 中的事务时出现意外行为。 To simulate the use case where 2 users happen to reserve the same product, I use setTimeout in my cloud function to halt the function for 3 seconds.为了模拟两个用户碰巧预订相同产品的用例,我在我的云 function 中使用 setTimeout 将 function 暂停 3 秒。 I am launching this function from 2 different clients with different user contexts.我正在从具有不同用户上下文的 2 个不同客户端启动此 function。

export const reserveProduct = functions.https.onCall(async (data,context) => {

 function testTimeout(){

     return new Promise((resolve,reject) => {
         setTimeout(()=> {
           
           return resolve(true)
        },3000)
       })

 }

if(!context.auth){
    return {
        error: `You must be logged in to reserve products`
    }
}else{


    const productRef = admin.firestore().collection('products').doc(data.productID)
    const userRef = admin.firestore().collection('users').doc(context.auth.uid)

    return admin.firestore().runTransaction((transaction) => {
        return transaction.get(productRef).then(async(doc) => {
            if(doc.get('status') == 'reserved'){
                throw "Document already reserved!"
            }else{
                console.log("Product not reserved, reserving now!")
            }

            await testTimeout()

            transaction.update(productRef, {status: 'reserved'});
            transaction.update(userRef, {reserved: admin.firestore.FieldValue.arrayUnion(data.productID)})

            
        })
    }).then(() => {
        console.log("Transaction Successfully committed !")
    }).catch((error) => {
      
        throw "Transaction failed, product already reserved"
    })

    


}

After running this function call from 2 different clients simultaneously, The function call from my first client returns successfully as expected, but only after roughly 35s (which is way too long for the simplicity of the transaction).在同时从 2 个不同的客户端运行此 function 调用后,来自我的第一个客户端的 function 调用按预期成功返回,但仅在大约 35 秒后才返回(这对于事务的简单性来说太长了)。 However, the second function call times out without returning any value.但是,第二个 function 调用超时,没有返回任何值。 I have not seen any documentation explicitly stating the use of transactions in callable cloud functions, nor should it be affected when used within the emulator.我没有看到任何文档明确说明在可调用的云函数中使用事务,在模拟器中使用它也不应该受到影响。

I am expecting to simply get a return value for whichever function call is able to modify the data first, and catch the error from the function which has retried and validated the reserved state.我期望简单地获得任何 function 调用能够首先修改数据的返回值,并从 function 中捕获错误,该 function 已重试并验证了保留的 Z9ED39E2EA931586B576A985A694E

Any help would be appreciated, thanks!任何帮助将不胜感激,谢谢!

One major difference between the two places is in the way the SDKs used handle transactions:这两个地方之间的一个主要区别在于 SDK 处理交易的方式:

  • The client-side SDKs use an optimistic compare-and-set approach for transactions, meaning that they pass the values you read in the transaction with the data you're writing.客户端 SDK 对事务使用乐观的比较和设置方法,这意味着它们将您在事务中读取的值与您正在写入的数据一起传递。 The server then only writes the new data if the documents you read haven't been updated.然后,如果您读取的文档尚未更新,服务器只会写入新数据。

  • The server-side SDKs (used in your Cloud Function) use a more traditional pessimistic approach for transactions, and place a lock on each document that you read in the transaction.服务器端 SDK(在您的 Cloud Function 中使用)对事务使用更传统的悲观方法,并对您在事务中读取的每个文档进行锁定。

You can read more about database contention in the SDKs in the documentation.您可以在文档中的 SDK 中阅读有关数据库争用的更多信息。

While I'm not exactly certain how this is affecting your code, I suspect it is relevant to the difference in behavior you're seeing between the client-side and server-side implementations.虽然我不确定这会如何影响您的代码,但我怀疑这与您在客户端和服务器端实现之间看到的行为差异有关。

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

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