【问题标题】:How to decide what exceptions are worth retrying when reading and writing to MongoDB (C# driver)?读写MongoDB(C#驱动)时,如何判断哪些异常值得重试?
【发布时间】:2021-02-28 17:03:09
【问题描述】:

通过查看this official documentation,似乎MongoDB C#驱动程序抛出的错误基本上分为三种类型:

  • 驱动程序无法正确选择或连接到服务器以发出查询时引发的错误。这些错误导致TimeoutException
  • 驱动程序已成功选择要对其运行查询的服务器,但在执行查询时服务器关闭时引发的错误。这些错误表现为MongoConnectionException
  • 写入操作期间引发的错误。这些错误会导致MongoWriteExceptionMongoBulkWriteException,具体取决于正在执行的写入操作的类型。

我正在尝试使使用 MongoDB 的软件对暂时性错误更具弹性,因此我想找出哪些异常值得重试。

问题不在于实施可靠的重试策略(我通常使用Polly .NET for that),而是了解重试何时有意义。

我认为重试TimeoutException 类型的异常没有意义,因为驱动程序本身会在操作超时之前等待几秒钟(默认为 30 秒,但您可以通过连接字符串更改它选项)。这个想法是,在超时之前等待 30 秒后重试操作可能是浪费时间。例如,如果您决定实施 3 次重试,它们之间的等待时间为 1 秒,则操作失败最多需要 93 秒(30 + 30 + 30 + 1 + 1 + 1)。这是一个巨大的时代。

As documented here 重试MongoConnectionException 只有在进行幂等操作时才是安全的。从我的角度来看,只要执行的操作是幂等的,那么总是重试这类错误是有意义的。

决定一个好的写入重试策略的难点是当您遇到MongoWriteExceptionMongoBulkWriteException 类型的异常时。

关于MongoWriteException 类型的异常可能值得重试所有具有ServerErrorCategory 除了 DuplicateKey 的异常。作为documented here,您可以使用MongoWriteException.WriteError 对象的this property 检测重复键错误。

重试重复键错误可能没有意义,因为您会再次得到它们(这不是暂时性错误)。

我不知道如何安全地处理MongoBulkWriteException 类型的错误。在这种情况下,您正在向 MongoDB 插入多个文档,并且完全有可能只有一些文档失败,而其他文档已成功写入 MongoDB。因此重试完全相同的批量插入操作可能会导致两次写入同一个文档(批量写入本质上不是幂等的)。我该如何处理这种情况?

你有什么建议吗?

您知道任何关于在 MongoDB 上为 C# 驱动程序重试查询的工作示例或参考吗?

【问题讨论】:

  • 无!!!第 1 项:只需增加连接超时。 2)如果服务器在插入过程中出现故障,您将在数据库中获得重复的条目 3)同样,如果您写入多行,您最终会出现重复。
  • @jdweng 我不确定从不重试操作是最佳选择。例如,为什么在读取期间引发 MongoConnectionException 时我不应该重试失败的读取操作?同样,为什么我不应该在写入操作期间由于服务器临时缓慢而引发 MongoWriteException 时重试 InsertOne 操作?
  • 真正的问题是,当您遇到错误并最终在数据库中出现重复条目​​时,您是否想要“重试”?您应该始终报告错误。
  • @jdweng 你是否建议即使从 mongodb 读取也跳过重试(在这种情况下,根本不可能破坏数据)?
  • 为什么阅读会出错?您想在重试之前调查错误吗?你认为重试真的有用吗?当 TCP 层已经有重试方法时,我不喜欢重试。重试成功的机会非常小。

标签: c# .net mongodb polly


【解决方案1】:

重试

让我们从重试的基础开始。

在某些情况下,您请求的操作依赖于在某个时间点可能无法访问的资源。换句话说,可能存在一个时间问题,它迟早会消失。这类问题可能会导致暂时性故障。通过重试,您可以通过尝试在未来的特定时刻重做相同的操作来克服这些问题。为了能够使用此机制,应满足以下标准组:

  • 可能引入的可观察到的影响是可以接受的
  • 该操作可以重做,没有任何不可逆转的副作用
  • 与承诺的可靠性相比,引入的复杂性可以忽略不计

让我们一一回顾:

  • “失败”一词表示请求者也可以观察到效果,例如通过更高的延迟/降低的吞吐量等。如果“惩罚”(延迟或降低的性能)是不可接受的,那么重试不是一个选项你。
  • 此要求也称为幂等操作。如果我多次使用相同的输入调用该操作,那么它将产生完全相同的结果。换句话说,操作的行为就像它只取决于它的参数,而没有其他任何东西会影响结果(就像其他对象的状态一样)。
  • 尽管这是最关键的条件之一,但它几乎总是被遗忘。与往常一样,存在权衡(如果我引入 Z,那么它将增加 X,但可能会减少 Y),我们应该充分意识到它们。 除非它会在最短的时间内给我们一些不想要的惊喜。

Mongo 异常

让我们继续讨论 MongoDb 的 C# 客户端可以抛出的异常。

过去几年我没有使用过 MongoDb,所以这些知识可能已经过时了。但我希望本质没有改变。

我还鼓励您在尝试缓解问题(例如重试)之前先引入检测逻辑(捕获和日志)。这将提供有关发生频率和数量的信息。它还将让您深入了解问题的性质。

  • MongoConnectionExceptionSocketException 作为内部
    • 何时:
      • 存在服务器选择问题
      • 连接已超时
      • 所选服务器不可用
    • 重试:
      • 如果问题是由网络问题引起的,那么重试可能会有用
      • 如果根本原因是配置错误,那么重试将无济于事
    • 日志:
  • MongoWriteExceptionMongoWriteConcernException
    • 何时:
      • 存在持久性问题
    • 重试:
      • 这取决于,如果您执行创建操作并且服务器可以检测到重复项 (DuplicateKeyError),那么最好尝试多次写入记录,然后尝试写入失败
      • 大多数时候更新不是幂等的,但是如果您使用某种记录版本控制,那么您可以尝试在optimistic locking 期间执行重试并失败
      • 可以以幂等方式实现删除。软删除和硬删除也是如此。
    • 日志:

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多