gosuperscript/zero-downtime-event-replays

Spatie's Laravel 事件源包的零停机事件回放

dev-main 2024-08-15 10:25 UTC

README

通常,在回放事件时,你可能需要处理一些可能导致停机的问题。你的读取模型将在回放开始时被截断,并且直到回放完成之前都不会有正确的数据。除此之外,你还必须等待将新记录的事件投影出去,直到回放完成,以保护回放顺序。

此包解决了这两个问题,允许对读取模型的副本进行回放。一旦回放加速,新记录的事件将被播放到两个投影中,以确保你的副本与实时读取模型保持同步。在验证新回放正确无误后,该包启用将回放提升到实时。结果是一个(几乎)零停机发布。

通常运行一个新的回复看起来像这样

  1. 创建一个新的回放,并给它一个键以识别它。例如 "add_extra_field_to_balance_projection"。你还可以在此步骤中指定你想要播放到的投影。
$manager = resolve(\Gosuperscript\ZeroDowntimeEventReplays\ReplayManager::class);
// Create a replay
$manager->createReplay('add_extra_field_to_balance_projection', [
    "App\Projectors\BalanceProjector"
]);
  1. 可以开始回放。在回放过程中,它会调用投影器上的 useConnection 方法。这样投影器就知道应该将数据写入哪里。此包包含一个 EloquentZeroDowntimeProjector,它为处理不同的连接提供了一些魔法。
$manager->startReplay('add_extra_field_to_balance_projection');
  1. 一旦回放完成,但由于新记录的事件,生产环境中仍然存在一些延迟。你可以再次开始回放,它将从最新的投影事件开始。始终可以监控回放的状态和与生产的延迟。
// get the state & progress of your replay 
$manager->getReplay('add_extra_field_to_balance_projection');

// how many events is the replay behind the event stream?
$manager->getReplayLag('add_extra_field_to_balance_projection');
  1. 一旦没有延迟,我们就可以开始将新事件投影到回放中。
$manager->startProjectingToReplay('add_extra_field_to_balance_projection');
  1. 一旦一切检查无误,你可以将回放提升到生产。
    $manager->putReplayLive('add_extra_field_to_balance_projection');
  1. 最后,你可以清理你的回放
    $manager->removeReplay('add_extra_field_to_balance_projection');

安装

您可以通过 composer 安装此包

composer require gosuperscript/zero-downtime-event-replays

用法

$manager = resolve(\Gosuperscript\ZeroDowntimeEventReplays\ReplayManager::class);
// Create a replay
$manager->createReplay('your_replay_key', ['projectorA', 'projectorB']);

// Start replay history
$manager->startReplay('your_replay_key');

// get the state & progress of your replay 
$manager->getReplay('your_replay_key');

// how many events is the replay behind the event stream?
$manager->getReplayLag('your_replay_key');

// once a replay is up to date with the event stream, we can project events to it when they happen 
$manager->startProjectingToReplay('your_replay_key');

// Once the replay is approved, we can promote it to production
$manager->putReplayLive('your_replay_key');

// Or we can delete the replay
$manager->removeReplay('your_replay_key');

零停机投影器

为了使投影器与零停机回放一起工作,它们必须实现 ZeroDowntimeProjector 接口。此接口要求你实现以下方法

interface ZeroDowntimeProjector
{
    // This method lets the projector know that its replaying on a replay
    public function forReplay(): self;

    // Sets the connection to replay to, using the replay key. Each connection must be treated as a clone of the production schema.
    public function useConnection(string $connection): self;

    // Promote your connection to production
    public function promoteConnectionToProduction(): void;

    // cleanup/remove connection
    public function removeConnection();
}

由于大多数投影可能都是回放到 eloquent,因此此包包含一个 EloquentZeroDowntimeProjector 抽象类和一个 Projectable 特性,用于你的 eloquent 读取模型。

为了使你的投影器与该包一起工作

  1. 确保你的投影器扩展了 EloquentZeroDowntimeProjector
  2. 在所有用于投影器的读取模型上添加 Projectable 特性。
  3. 在投影器上实现一个 models 方法,该方法返回投影器写入的所有模型。这由 EloquentZeroDowntimeProjector 用于设置正确的数据库模式和将正确的模型提升到生产。
    public function models(): array
    {
        return [
            new BalanceProjector(),
        ];
    }
  1. 在查询或更新读取模型的所有地方,使用 forProjection 方法。
    // when truncating
    Balance::forProjection($this->connection)->truncate();
    
    // when querying
    Balance::forProjection($this->connection)->where('user_id', $event->user_id)->first();
    
    // when updating
    Balance::forProjection($this->connection)->where('user_id', $event->user_id)->increment('total', $event->amount);
    
    // when newing an instance
    $balance = Balance::newForProjection($this->connection, ['id' => $event->user_id, 'total' => $event->amount]);
    $balance->save();

测试

composer test

变更日志

有关最近更改的更多信息,请参阅 CHANGELOG

贡献

有关详细信息,请参阅 CONTRIBUTING

安全漏洞

有关报告安全漏洞的详细信息,请参阅 我们的安全策略

致谢

许可

MIT许可(MIT)。请参阅许可文件获取更多信息。