【问题标题】:Best retry policy on data creation response timeout数据创建响应超时的最佳重试策略
【发布时间】:2017-08-02 07:07:29
【问题描述】:

在这种情况下最好的重试策略是什么:

Database 成功创建数据条目,但是响应需要很长时间才能到达Application。所以为了完成这项工作,Application 重试创建,当然Database 返回一个“已经存在”错误。所以最后从Application的角度来看,好像是创建失败了,其实是成功了。更糟糕的是,如果这是在一系列步骤的中间,那么Application 将无法决定是否触发前面步骤的回滚。

增加Application 的超时长度不是一个可接受的解决方案,因为 IP 网络永远不可能 100% 可靠,而且响应在网络中丢失的可能性总是很小。

在创建之前添加<data> 的存在性检查可以工作。但这只是在考虑并发性的情况下。在我的情况下,Database 可以有多个客户,我不确定竞争条件的可能性。

+-------------+                                             +-----------+    
| Application |                                             | Database  |    
+-------------+                                             +-----------+    
       |                                                          |          
       | CREATE <data>                                            |          
       |--------------------------------------------------------->|          
       |                                                          |          
       |                                                          | creating 
       |                                                          |--------- 
       |                                                          |        | 
       |                                                          |<-------- 
       | -------------------------------\                         |          
       |-| timeout waiting for response |                         |          
       | |------------------------------|                         |          
       |                                                          |          
       |                                                  SUCCESS |          
       |<---------------------------------------------------------|          
       | -----------------------------------------------\         |          
       |-| response from a timed out session is ignored |         |          
       | |----------------------------------------------|         |          
       |                                                          |          
       | retry CREATE <data>                                      |          
       |--------------------------------------------------------->|          
       |                                                          |          
       |                             ERROR: <data> ALREADY EXISTS |          
       |<---------------------------------------------------------|          
       | ---------------------------------------------------\     |          
       |-| no idea whether the creation actually took place |     |          
       | |--------------------------------------------------|     |          
       |                                                          |          

【问题讨论】:

  • 您的应用程序没有收到“网络连接超时”错误吗?
  • @NevilleK 是的。这就是触发重试的原因。

标签: database algorithm ldap data-consistency retrypolicy


【解决方案1】:

大多数现代数据库都提供了某种编写“upsert”语句的方法,如果数据不存在,它将自动插入数据,如果数据已经存在,则更新(或不执行任何操作)。这样,您的应用程序可以安全地重试,并且如果数据已经创建,则不会出现错误,从而使您的数据创建idempotent

一些流行数据库的示例:

  • MySQL:

     -- Do nothing if data exists
     INSERT IGNORE ...
     -- Update if data exists
     INSERT ...  ON DUPLICATE KEY UPDATE ...
    
  • PostgreSQL:

    -- Do nothing if data exists
    INSERT ... ON CONFLICT DO NOTHING
    -- Update if data exists
    INSERT ... ON CONFLICT ... DO NOTHING
    
  • Oracle:

    -- Do nothing if data exists
    MERGE INTO ... USING ...
    WHEN NOT MATCHED THEN INSERT ...
    -- Update if data exists
    MERGE INTO ... USING ...
    WHEN NOT MATCHED THEN INSERT ...
    WHEN MATCHED THEN UPDATE ...
    

如果不能选择原子操作或事务,您可以编写数据库操作以使重试无害,并在循环中执行每个操作,首先检查数据库是否已处于所需状态,然后尝试如果不是,则操作,并在失败时重试。换句话说,类似(伪代码):

max_retries = n
retries = 0
WHILE NOT database_in_desired_state
    IF retries < max_retries THEN
        perform_database_operation
        retries = retries + 1
    ELSE
        fail

您可以通过使操作有条件(例如UPDATE some_table SET field = value, version = version + 1 WHERE version = expected_version 或添加唯一约束等以禁止重复操作)来使重试无害。如果您提供有关您正在使用的数据库的更多详细信息,我也许可以提供更具体的建议。

如果您在多个远程系统上执行一长串操作,如果发生故障,整个操作应该回滚,并且无法将所有交互包装在单个(分布式)事务中,您将需要编写补偿事务,这些事务将手动回滚迄今为止所做的错误工作。当然,补偿交易也可能失败,您需要考虑如何处理。一种方法是定期进行清理任务,以查找失败的事务或不一致的状态。

【讨论】:

  • 我们使用的数据库是基于 LDAP 的。大多数情况下,一个客户请求必须通过多个 LDAP 操作来满足,并在流程中与另一个模块进行一些交互。
  • 请明确 LDAP 要求 - 大多数人在您说“数据库”时会想到 RDBMS,因此您会得到很多不好的答案。
  • 我试图让问题更通用,因为在我看来,这不是数据库可以解决的问题,而是应用程序方面的更多策略选择。但下次我提出问题时,我会考虑您的建议。
【解决方案2】:

这一切都取决于上下文。

是的,网络连接可能会失败 - 但您必须确定这是多大的风险。如果您使用专业的托管设置和企业级设备,这将会发生 - 嗯,几乎从来没有。在这种情况下,我不会在应用程序中构建很多额外的逻辑来处理网络问题;您应该依靠数据库的事务管理功能来确保数据处于一致状态。 一旦您的应用程序捕获到网络异常,您就可以向用户显示错误,并要求他们重新开始。

如果您的环境本质上是不可靠的 - 例如,您通过公共互联网进行连接 - 常见的架构模式是使用消息总线,而不是同步操作。

编写同步代码来处理不可靠的网络状况并非易事;您将从发布的伪代码@markusk 开始,但我会添加关闭并重新打开数据库连接。

【讨论】:

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