数据创建响应超时的最佳重试策略



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

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 |     |          
| |--------------------------------------------------|     |          
|                                                          |          

大多数现代数据库都提供了某种编写"upsert"语句的方法,如果数据不存在,这些语句将以原子方式插入数据,如果数据已经存在,则更新(或不执行任何操作(。这样,应用程序可以安全地重试,并且如果已创建数据,则不会收到错误,从而使数据创建具有幂等性。

一些流行数据库的示例:

  • 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
    
  • 神谕:

    -- 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或添加唯一约束等,以便不允许重复操作。如果您提供有关您正在使用的数据库的更多详细信息,我可能会提供更具体的建议。

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

这一切都取决于上下文。

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

如果您的环境本质上是不可靠的(例如,您通过公共 Internet 进行连接(,则常见的体系结构模式是使用消息总线,而不是同步操作。

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

最新更新