如何返回失败的任务导致继续任务 [英] How to return failed task result in continuation task

查看:72
本文介绍了如何返回失败的任务导致继续任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在用Kotlin编写我的第一个应用程序,并且正在使用Firestore& Firebase存储.在删除文档的过程中,我想删除该文档引用的存储中的所有文件(因为在我的情况下,这是对它们的唯一引用).如果存储删除失败,我想中止文档删除,以避免在我的存储中出现孤立文件.我也想在一个任务"中做所有事情,以允许正确显示进度条.我的简化代码如下:

I'm writing my first app in Kotlin and am using Firestore & Firebase Storage. In the process of deleting a document, I want to delete all files in Storage that the document references (as it is the only reference to them in my case). If the Storage delete fails, I want to abort the document delete, in order to avoid orphan files in my Storage. I also want to do everything in "one Task", to allow showing a progress bar properly. My simplified code looks like this:

    fun deleteItem(id: String): Task<Void>? {
        val deleteTask = deleteTaleMedia(id)
        continueWithTaskOrInNew(deleteTask) { task ->
            if (task?.isSuccessful != false) { ... }
        }
    }

    fun deleteItemMedia(id: String): Task<Void>? =
        getItem(id)?.continueWithTask { task ->
            if (task.isSuccessful)
                task.result?.toObject(ItemModel::class.java)?.let { deleteFiles(it.media) }
            else ???
        }

    fun deleteFiles(filesList: List<String>): Task<Void>? {
        var deleteTask: Task<Void>? = null
        for (file in filesList) deleteTask = continueWithTaskOrInNew(deleteTask) { task ->
            if (task?.isSuccessful != false) deleteFile(file)
            else task 
        }
        return task
    }

    fun deleteFile(fileName: String) = Firebase.storage.getReferenceFromUrl(fileName).delete()

    fun getItem(id: String): Task<DocumentSnapshot>? {
        val docRef = userDocRef?.collection(COLLECTION_PATH)?.document(id)
        return docRef?.get()
            ?.addOnCompleteListener { ... }
    }

    fun <ResultT, TContinuationResult> continueWithTaskOrInNew(
        task: Task<ResultT>?,
        continuation: (Task<ResultT>?) -> Task<TContinuationResult>?
    ) = task?.continueWithTask { continuation.invoke(task) } ?: continuation.invoke(null)

data class ItemModel(
    @DocumentId val id: String = "",
    var title: String = "",
    var media: List<String> = listOf()
)

我的问题来自deleteItemMedia函数(末尾为"???").万一get任务失败,我想返回一个任务,该任务将告诉我的deleteItem函数中止删除(task.isSuccessful == false).我无法返回get任务本身(在代码中用"task"替换"???"),因为它的类型(Task<DocumentSnapshot>)与删除任务(Task<Void>)的类型不同.我无法返回null,因为在完全没有介质的情况下返回了null,这对我来说是一个有效的情况(应删除文档).有没有办法创建新的失败任务"?

My problem comes in deleteItemMedia function (the "???" at the end). In case the get task failed, I want to return a task that will tell my deleteItem function to abort deletion (task.isSuccessful == false). I cannot return the get task itself (replace "???" with "task" in code), because it's type (Task<DocumentSnapshot>) differs from the type of the delete task (Task<Void>). I cannot return null, as null is returned in the case of no media at all, which is a valid case for me (document should be deleted). Is there a way to create a new "failed Task"?

推荐答案

在删除文档的过程中,我要删除该文档引用的存储中的所有文件(因为这是我的唯一引用).

In the process of deleting a document I want to delete all files in Storage that the document references (as it is the only reference to them in my case).

没有API可以做到这一点.您必须自己执行两个删除操作.

There is no API that's doing that. You have to perform both delete operations yourself.

我也想在一个任务"中做所有事情,以允许正确显示进度条.

I also want to do everything in "one Task", to allow showing a progress bar properly.

不幸的是,这不可能一次完成.如果您想到了原子操作,那么这也是不可能的,因为Firebase服务都不支持这种跨产品的交易操作.您需要做的是,获取文档,获取存储中文件的引用,删除文档,并在删除操作完成后立即删除文件.您可以通过尝试从客户端回滚数据来绝对降低风险,但是您不能在一个任务"中以原子方式进行处理.但是,在某个时间点,会有一个客户端无法回滚的异常.

Unfortunately, this is not possible in a single go. If you think of an atomic operation, this also not possible because none of the Firebase services support this kind of cross-product transactional operations. What you need to do is, get the document, get the references to the files in the Storage, delete the document and as soon as the delete operation is complete, delete the files. You can definitely reduce the risk by trying to roll-back the data from the client, but you cannot do them atomic, in "one Task". However, at some point in time, there will be an Exception that the client can't rollback.

如果存储删除失败,我想中止文档删除,以避免存储中出现孤立文件.

If the Storage delete fails, I want to abort the document delete, in order to avoid orphan files in my Storage.

为避免这种情况,首先,请尽量不要保留不完整的数据.例如,当您阅读文档并获得相应的存储URL时,请不要盲目地假设所有这些文件实际上都存在.文件由于多种原因而无法使用(先前已删除,由于某些原因服务不可用,等等).

To avoid that, first, try not to have incomplete data. For instance, when you read the document and you get the corresponding Storage URLs, don't blindly assume that all those files actually exist. A file can unavailable for many reasons (was previously deleted, for some reasons the service is unavailable, etc.)

另一种方法可能是使用 Firebase的云功能,因此您可以删除所需的文档,然后使用 onDelete 函数从存储中删除相应的文件.这意味着,如果文档删除失败,则不会删除存储中的文件.如果删除文档的操作成功,则将触发云功能",并且将从存储中删除图像.这种方法将大大减少在文档删除操作和从存储中删除文件之间出现故障的机会,但是并不能100%消除这种机会.

Another approach might be to use Cloud Functions for Firebase, so you can delete the desired document, and use onDelete function to delete the corresponding files from the Storage. Meaning, when document delete fails, the files from the Storage won't be deleted. If the operation to delete the document is successful, the Cloud Function will be triggered and the images will be deleted from the Storage. This approach will drastically reduce the chances of having failures between the document delete operation and the deletion of the files from Storage, but it doesn't eliminate that chance 100%.

此外,最常见的避免失败的方法是使代码尽可能强大,以防止失败并进行频繁的数据库清理.

Besides that, the most common approach to avoid failures is to make your code as robust as you possibly can against failure and do frequent database cleanups.

这篇关于如何返回失败的任务导致继续任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆