补偿交易模式 Compensating Transaction Pattern
描述#
在最终一致性模型中,典型的业务操作由一系列自治步骤组成。如果一个或多个步骤失败,失败步骤的回滚操作将会由一系列执行的工作的步骤组成,这些步骤一起定义最终一致。
背景和问题#
在执行这些步骤时,系统状态的总体视图可能是不一致的,但是当操作完成并且所有步骤都已经执行时,系统应该再次变得一致。在最终一致性模型中,某些步骤是无法恢复的。例如转账业务。
解决方案#
实现补偿事务。补偿事务必须撤消原始操作步骤的效果。补偿事务中步骤的顺序不一定是原来步骤的相反,也可以是其他抵消效果的操作。
注意事项#
- 完整的操作日志。必须能够监视补偿逻辑的生命周期。
- 将补偿事务中的步骤定义为幂等命令。因为如果补偿事务失败,还可以重复这些步骤。
- 超时机制。所有操作必须在锁过期前完成。
- 重试机制。如果补偿失败,可以重新触发。
何时使用#
符合所有步骤都可以回滚或能达到撤销效果的补偿事务。
可用到的设计模式思维#
每个步骤都有自己独立的逻辑,每个步骤都可以看成一个指令。这里可以使用命令模式处理。
结构中包含的角色#
- StepCommand 抽象步骤命令
- ConcreteStepCommand 具体步骤命令
- Transactor 交易者
最小可表达代码#
interface StepCommand
{
public function handle() : bool;
}
class OneStepCommand implements StepCommand
{
public function handle() : bool
{
var_dump('记录步骤1 - 操作成功');
return true;
}
}
class TwoStepCommand implements StepCommand
{
public function handle() : bool
{
var_dump('记录步骤2 - 操作失败');
return false;
}
}
class OneRollBackStepCommand implements StepCommand
{
public function handle() : bool
{
var_dump('记录回滚步骤1 - 操作成功');
return true;
}
}
class TwoRollBackStepCommand implements StepCommand
{
public function handle() : bool
{
var_dump('记录回滚步骤2 - 操作成功');
return true;
}
}
// 交易者
class Transactor
{
private $currentIndex = 0;
private $commands = [];
private $rollBackStepCommands = [];
public function addCommand(StepCommand $stepCommand, StepCommand $rollBackStepCommand)
{
$this->commands[] = $stepCommand;
$this->rollBackStepCommands[] = $rollBackStepCommand;
}
public function execute()
{
$status = true;
// 正常流程执行步骤
foreach ($this->commands as $index => $command) {
$this->currentIndex = $index;
if (! $status = $command->handle()) {
break;
}
}
if ($status) {
return;
}
// 回滚步骤
foreach ($this->rollBackStepCommands as $index => $rollBackStepCommand) {
if (! $status = $rollBackStepCommand->handle()) {
throw new \Exception("交易异常,启动异常机制");
}
if ($this->currentIndex = $index) {
break;
}
}
}
}
$transactor = new Transactor();
$transactor->addCommand(new OneStepCommand, new OneRollBackStepCommand);
$transactor->addCommand(new TwoStepCommand, new TwoRollBackStepCommand);
$transactor->execute();
推荐文章: