通过事件触发功能多次更新 firestore 文档会在几次触发后覆盖或停止更新

问题描述 投票:0回答:1

我正在尝试在调整一组图像大小时更新文档,以显示进度并跟踪是否有任何图像无法转换。

我们在 dir 中几乎没有图像,其中 dir 是文档 ID。使用文档 ID,我们必须更新该文档。我们必须设置状态字段,状态包含已转换或转换失败信息的对象。 {图像:路径,处理:字符串}

看起来像 { ... 地位 : [ 文件名1:{...}, 文件名2:{...} ] }

但是当我上传 5 到 10 个图像时,它只在状态字段中设置前 4-5 个图像信息。在将最后一个图像信息写入状态之前,它会暂停(或看起来像是暂停或覆盖一些信息)。

虽然我尝试简单地用状态中的新数据替换旧数据,但这意味着只有单个对象会出现在此处并相应地发生更改。这在大多数情况下都有效。我见过罕见的情况,这也失败了,但大多数时候它都有效。

还尝试拥有对象数组,但也错过了一些条目。

async function updateDoc({filePath, metadata}) {
    const [docId, fileName] = filePath.split('/');
    
    const db = admin.firestore();
    const collectionRef = db.collection(COLLECTION_PACKS).doc(docId);
    const stickersRef = collectionRef.collection(COLLECTION_STICKERS);
  
    let attempts = 0;
    const maxAttempts = 5;

    while (attempts < maxAttempts) {
        try {
            await db.runTransaction(async (transaction) => {

                const updatedStatus = {};
                updatedStatus[fileName] = metadata;

                // Update the document with the new status and progress
                transaction.update(collectionRef, {
                    status: updatedStatus,
                });
            });
            console.log("Updated document for", filePath);
            break; // Exit the loop if successful
        } catch (error) {
            attempts++;
            console.error(`Error updating document (attempt ${attempts}):`, error);
            if (attempts >= maxAttempts) {
                throw error; // Re-throw the error after max attempts
            }
            await new Promise(resolve => setTimeout(resolve, 1000 * attempts)); // Exponential backoff
        }
    }
}

async function updateMeta({filePath, process, valid, error}){
    // console.log("updateMeta ",filePath)
    let metadata = { process, valid }
    if (error) { metadata.error = error; }
    await bucket.file(filePath).setMetadata({
        metadata: metadata
    })

    await updateDoc({ filePath, metadata})
}

无论是嵌套对象还是对象数组,两者都可以。

javascript node.js firebase google-cloud-firestore google-cloud-functions
1个回答
1
投票

不需要使用

db.runTransaction()
,因为您没有在更新中使用任何旧值,因此它只是添加了更多不必要的处理。

如果您的数据结构实际上是

{ ... status : { filename1:{...}, filename2:{...} } }
(请注意,我用大括号替换了方括号,我相信您的意思是对象而不是数组),那么您可以简单地更新嵌套路径,而无需使用事务。也无需重试:

// this is a docRef, not a colRef as in your example
const docRef = db.collection(COLLECTION_PACKS).doc(docId);
const update = {
  [`status.${fileName}`]: metadata,
};
await docRef.update(update);

====== 编辑:

进一步解释一下,当你说:

但是当我上传 5 到 10 个图像时,它只在状态字段中设置前 4-5 个图像信息。它在将最后一个图像信息写入状态之前暂停。

我不认为它会暂停。我相信你有竞争条件。请注意,您将覆盖每个文件上的整个

status
对象。这意味着如果文件按 1-2-3-4 的顺序触发,但它们完成处理 1-2-4-3,您最终将得到第三个文件的信息,而不是最后一个文件的信息。 这与您显示的代码不一致,但根据您所说的,如果您打算在更新之前获取整个
status
对象,那么这是可能的。

© www.soinside.com 2019 - 2024. All rights reserved.