【问题标题】:How to move transaction code inside another function如何在另一个函数中移动事务代码
【发布时间】:2022-01-11 04:25:12
【问题描述】:

我正在尝试使用事务重写我的 firebase 函数。我现在遇到的问题是,我应该返回交易承诺吗?例如,我想使用一个具有事务参数的函数。但是这个函数不应该立即执行事务,而是返回它的 Promise。

这是我目前的功能:

export const dbUserDocsOnCreated = functions
    .region(constants.region)
    .firestore
    .document("users/{id}")
    .onCreate((snapshot, context) => doOnCreatedUserDocs(db, stripe, snapshot, context));


export async function doOnCreatedUserDocs(
    db: FirebaseFirestore.Firestore, 
    stripe: Stripe, 
    snapshot: functions.firestore.QueryDocumentSnapshot, 
    context: functions.EventContext
) {
    try {
        const transactionPromise = db.runTransaction(async (t) => {
            const snapshotData: FirebaseFirestore.DocumentData = (await t.get(snapshot.ref)).data() as FirebaseFirestore.DocumentData
            
            // IF onCreate() has already been called once.
            if (!isDataNullOrEmpty(snapshotData.eventID)) {
                return Promise.all([null])
            }

            const createdUserNr: string = await getIdTransactionally(t, db, constants.collectionUsers)
            const createdStripeUser: Stripe.Response<Stripe.Customer> = await createStripeUser(stripe, snapshotData, firebaseUserEmail, currentUserNr, snapshot.id, idempotencyKey)
            
            const updateUserNrPromise: Promise<FirebaseFirestore.Transaction> = incrementIdTransactionally(t, db,  constants.collectionUsers)
            const updateUserDocsPromise: Promise<FirebaseFirestore.Transaction> = updateUserDocsTransactionally(t, snapshot.ref, createdUserNr, idempotencyKey)

            return Promise.all([
                updateUserDocsPromise,
                updateUserNrPromise,
            ])
        })

        return Promise.all([
            transactionPromise
        ])
    } catch (err) {
        // CATCHING
    }
}

处理事务的使用函数

export async function incrementIdTransactionally(
    transaction: FirebaseFirestore.Transaction, 
    db: FirebaseFirestore.Firestore, 
    doc: string
): Promise<FirebaseFirestore.Transaction> {
    const incrementor: FirebaseFirestore.FieldValue = FirebaseFirestore.FieldValue.increment(1);
    const collectionReference: FirebaseFirestore.DocumentReference<FirebaseFirestore.DocumentData> = db.collection(constants.collectionAutoIds).doc(doc)

    return transaction.update(collectionReference, { id: incrementor })
}

async function updateUserDocsTransactionally(
    transaction: FirebaseFirestore.Transaction, 
    ref: FirebaseFirestore.DocumentReference, 
    generatedUserNr: String,
    eventId: string,
): Promise<FirebaseFirestore.Transaction> {
    return transaction.set(ref, { 
        userNr: generatedUserNr, 
        eventId: eventId,
        optionalAddress: FirebaseFirestore.FieldValue.delete() 
    }, { merge: true })
}

function createStripeUser(
    stripe: Stripe, 
    data: FirebaseFirestore.DocumentData, 
    eMail: string, 
    usernr: string, 
    uid: string,
    idempotencKey: string,
): Promise<Stripe.Response<Stripe.Customer>> {
    return stripe.customers.create({
        name: data.fullName,
        email: eMail,
        description: "Created by Firebase Cloud-Functions",
        metadata: { uuid: uid, userNr: usernr },
        preferred_locales: ["de-DE"],
    }, { idempotencyKey: idempotencKey })
}

【问题讨论】:

  • 您似乎正试图根据您正在运行的主函数内部调用的辅助函数在单个事务中执行多个操作。您是否检查过 this question 如何执行多项操作是否有帮助?如果不是,它与您尝试实施的有什么不同?
  • @ErnestoContrerasPinon 您提供的问题正是我想要的。我的问题唯一不同的是,我如何将事务操作移动到另一个函数中。例如,在您提供的问题中,如何将t.set(userDoc, { "userId" : userId }) 移动到另一个函数中。这个函数应该返回一个承诺吗??

标签: javascript firebase google-cloud-firestore transactions google-cloud-functions


【解决方案1】:

在查看previous thread 和 SDK 参考后,看起来set()update()delete() operations 的事务不返回承诺,但它们只是返回相同的 Transaction 实例用于交易链:

返回事务:这个事务实例。用于链接方法调用。

这与get() method 不同,它确实返回Promise&lt;DocumentSnapshot&gt;。我测试了一个简单的事务,分为两个函数,没有发现明显的问题:


function fireTransaction(){
    return fireDB.runTransaction((tr) => {
        const myDoc = fireDB.collection("users").doc("testUser1");
        return tr.get(myDoc).then(tDoc => {
            setUserAgeTR(tr, tDoc.ref)
        })
    })
    .then(res => console.log("Completed"))
    .catch(err => console.log("Failed Transaction: ", err))
}

function setUserAgeTR(tr, docRef){
    return tr.update(docRef, {userAge: 9999}) //Requires DocumentReference, can be obtained from 'ref'
}

根据 SDK 文档,由于您使用的是 TypeScript,因此您的辅助函数的返回类型应为 Transaction

【讨论】:

  • 调用setUserAgeTR 会导致即时更新写入,对吗?有可能推迟这个吗?就像承诺一样?
  • 每个操作仍然在 runTransaction() 方法中进行,这是根据 SDK doc 的承诺。它将完全按照general documentation 中的描述工作,因为完整的代码仍然是一个必须提交或失败的事务。我建议查看该页面以获取有关此内容的更多信息。
猜你喜欢
  • 1970-01-01
  • 2019-11-24
  • 2018-04-09
  • 1970-01-01
  • 1970-01-01
  • 2015-03-22
  • 1970-01-01
  • 2017-05-14
  • 2016-02-17
相关资源
最近更新 更多