【问题标题】:Handle multiple Promise Failures inside a Cloud Task Handler在 Cloud Task Handler 中处理多个 Promise 失败
【发布时间】:2021-11-29 14:31:03
【问题描述】:

我已经设法让 Cloud Task 正常工作。我对下面的代码没有任何问题,当在特定时间调用任务时,所有内容都会被删除。我无法解决的问题是如果任何 Promise 失败该怎么办,以及如何重试它们,因为任务已成功调用,但任务中的部分或全部 Promise 可能会失败。

我有 5 个承诺

1- 运行扇出/原子更新以从两个不同的数据库引用中删除一些值

2- 运行事务以减少用户路径上的属性

3- 运行事务以减少总帖子路径中的属性

4- 从存储中删除视频

5- 从存储中删除缩略图

据我了解,两个事务都将自行重试 (29x),但其他三个 Promise 我不知道如何处理。我一直在阅读不同的帖子并查看 Youtube 视频,但我无法理解这一点。除非我误解了Promise.all,否则这似乎不是一个可行的选择,因为it's all or nothing,当一个失败时,不会执行任何其他操作

const cors = require('cors')({origin: true});
const admin = require('firebase-admin');
admin.initializeApp({ ... });

exports.fireCloudTaskAtSpecificTime = functions.https.onRequest((request, response) => {

    return cors(request, response, () => {

        let userId = request.body.userId;
        let postId = request.body.postId;
        let videoId = request.body.videoId;
        let thumbnailId = request.body.thumbnailId;

        var updates = {};
        updates[`/posts/${postId}`] = null;
        updates[`/user-postIds/${userId}/${postId}`] = null;

        const runTransactionOnUserPostCt = admin.database().ref('users').child(userId).child('postCt');
        const runTransactionOnTotalPostCt = admin.database().ref('total-posts-count').child('postCt');

        const videoIdPath = 'videos/' + userId + '/' + videoId;
        const thumbnailIdPath = 'thumbnails/' + userId + '/' + thumbnailId;

        admin.database().ref('posts').child(postId).once('value', snap => {

            if (snap.exists()) {

                // 1. fan-out
                const root = admin.database().ref();
                return root.update(updates)
                .then(() => {

                    // 2. first Transaction
                    return runTransactionOnUserPostCt.set(admin.database.ServerValue.increment(-1));
                })
                .then(() => {

                    // 3. second Transaction
                    return runTransactionOnTotalPostCt.set(admin.database.ServerValue.increment(-1));
                })
                .then(() => {

                    // 4. delete video from Storage
                    const videoBucket = admin.storage().bucket();
                    return videoBucket.file(videoIdPath).delete()
                })
                .then(() => {

                    // 5. delete thumbnail from Storage
                    const thumbnailBucket = admin.storage().bucket();
                    return thumbnailBucket.file(thumbnailIdPath).delete()
                })
                .then(() => {

                    response.status(200).send('Ok - Everything Deleted');
                })
                .catch((error) => {

                    console.log('ERROR - Something Failed: ', error); // not sure what to do here or how to determine which one(s) failed
                });

            } else {

                response.status(200).send('Ok - Post No Longer Exists'); // user already deleted it
            }

        .catch((error) => {
            
            response.status(300).send('Task failed', error); // Only the HTTP code 2XX (from 200 to 299) are considered as a task completion and stops the retries. All other return code are considered as a failure and imply a retry.
        });
    });
});

【问题讨论】:

  • 这对于 Google Workflows 来说可能是一个好故事...cloud.google.com/workflows 他们提供了步骤的顺序执行,如果某个步骤出现问题,似乎可以让您更正并继续。另一个想法可能是 Cloud Composer,它再次允许将工作分解为要执行的活动的 DAG。另一个想法可能是您希望所有步骤最终都执行而不考虑顺序......所以触发 5 个任务而不是一个。最后,考虑在 DataStore 中手动记录状态并自己处理成功和失败的情况。
  • @Kolban 感谢您的回复。我将看看前两个选项。触发 5 个任务而不是一个任务是一个有趣的选项 - 您是否有任何 SO 链接显示一个云任务同时触发多个处理程序?还是您的意思是 5 个单独的任务,每个任务都有自己的处理程序。这是一个有趣的想法,但似乎很昂贵。您是什么意思“在 DataStore 中手动记录状态”?你有任何关于那个的链接吗,这对我来说是新的
  • 你好 Lance ...我确实是指 5 个独立的任务并行启动。如果结果是所有任务都必须完成并且它们是独立的,那么必须启动 5 个任务并且必须完成 5 个任务,如果其中任何一个任务失败,您就会知道并且可以重试它们。关于使用数据存储的最后一个概念......我想象你有一个应用程序,其中包含 5 个函数的代码,这些函数一个接一个地运行。当一个成功时,你更新一个数据库记录,说它成功了。如果一个失败了,整个 GCP 任务就失败了,当它重新启动时,它知道它已经做了什么。
  • 您好,感谢您的帮助。我一直在思考你所说的关于 5 个独立任务的内容,这很有意义。虽然有 1 个问题,但我环顾四周,我认为不可能一次启动 5 个单独的任务。我将对您的数据存储想法进行更多研究。感谢您的帮助!

标签: firebase-realtime-database promise google-cloud-functions


【解决方案1】:

根据您问题的 cmets 添加: 您还可以尝试使用 Promise.allSettled() 方法,该方法返回一个在所有给定的承诺都已履行或拒绝后解决的承诺,并带有一组对象,每个对象分别描述每个承诺的结果。

当您有多个不依赖于彼此成功完成的异步任务,或者您总是想知道每个承诺的结果时,通常使用它。

相比之下,如果任务相互依赖/如果您想立即拒绝其中任何一个,则由 Promise.all() 返回的 Promise 可能更合适。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled

【讨论】:

    猜你喜欢
    • 2019-03-03
    • 1970-01-01
    • 2016-01-10
    • 2014-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-03
    • 2018-08-01
    相关资源
    最近更新 更多