{"id":33946147,"url":"https://github.com/gosuperscript/zero-downtime-event-replays","last_synced_at":"2026-04-09T03:31:00.248Z","repository":{"id":44744776,"uuid":"406404676","full_name":"gosuperscript/zero-downtime-event-replays","owner":"gosuperscript","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-06T17:50:40.000Z","size":94,"stargazers_count":3,"open_issues_count":6,"forks_count":4,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-07T01:44:06.538Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"PHP","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gosuperscript.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":".github/SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-09-14T14:39:01.000Z","updated_at":"2024-08-15T10:25:12.000Z","dependencies_parsed_at":"2024-08-15T12:12:19.234Z","dependency_job_id":null,"html_url":"https://github.com/gosuperscript/zero-downtime-event-replays","commit_stats":{"total_commits":41,"total_committers":5,"mean_commits":8.2,"dds":"0.41463414634146345","last_synced_commit":"9a74ee7ecddd8e622ca7c2603223ac0ee71d3fe9"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":"spatie/package-skeleton-laravel","purl":"pkg:github/gosuperscript/zero-downtime-event-replays","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gosuperscript%2Fzero-downtime-event-replays","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gosuperscript%2Fzero-downtime-event-replays/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gosuperscript%2Fzero-downtime-event-replays/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gosuperscript%2Fzero-downtime-event-replays/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gosuperscript","download_url":"https://codeload.github.com/gosuperscript/zero-downtime-event-replays/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gosuperscript%2Fzero-downtime-event-replays/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31584567,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-08T14:31:17.711Z","status":"online","status_checked_at":"2026-04-09T02:00:06.848Z","response_time":112,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-12-12T18:00:27.448Z","updated_at":"2026-04-09T03:31:00.242Z","avatar_url":"https://github.com/gosuperscript.png","language":"PHP","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Zero downtime event replay for Spatie's Laravel event sourcing package\n\n[comment]: \u003c\u003e ([![Latest Version on Packagist]\u0026#40;https://img.shields.io/packagist/v/gosuperscript/zero-downtime-event-replays.svg?style=flat-square\u0026#41;]\u0026#40;https://packagist.org/packages/gosuperscript/zero-downtime-event-replays\u0026#41;)\n\n[comment]: \u003c\u003e ([![GitHub Tests Action Status]\u0026#40;https://img.shields.io/github/workflow/status/gosuperscript/zero-downtime-event-replays/run-tests?label=tests\u0026#41;]\u0026#40;https://github.com/gosuperscript/zero-downtime-event-replays/actions?query=workflow%3Arun-tests+branch%3Amain\u0026#41;)\n\n[comment]: \u003c\u003e ([![GitHub Code Style Action Status]\u0026#40;https://img.shields.io/github/workflow/status/gosuperscript/zero-downtime-event-replays/Check%20\u0026%20fix%20styling?label=code%20style\u0026#41;]\u0026#40;https://github.com/gosuperscript/zero-downtime-event-replays/actions?query=workflow%3A\"Check+%26+fix+styling\"+branch%3Amain\u0026#41;)\n\n[comment]: \u003c\u003e ([![Total Downloads]\u0026#40;https://img.shields.io/packagist/dt/gosuperscript/zero-downtime-event-replays.svg?style=flat-square\u0026#41;]\u0026#40;https://packagist.org/packages/gosuperscript/zero-downtime-event-replays\u0026#41;)\n\n---\n\nUsually you have to deal with a few problems that might cause downtime when replaying events. \nYour read models will be truncated at the beginning of a replay, and won't have the correct data until the replay has finished.\nBesides that, you'd have to wait with projecting newly recorded events until the replay has finished, to protect the replay order. \n\nThis package solves both problems, allowing a replay to happen to a copy of your read models. \nOnce the replay is up to speed, newly recorded events will be played to both projections, so that your copy is kept up to date with the live read model. \nAfter verifying that the new replay is correct, the package enabled to promote your replay to live. Resulting in a (near) zero downtime release. \n\nUsually running a new reply will look like this:\n1. Create a new replay, ang give it key to identify it. For example \"add_extra_field_to_balance_projection\". You also specify the projectors you want to play to in this step.\n\n```php\n$manager = resolve(\\Gosuperscript\\ZeroDowntimeEventReplays\\ReplayManager::class);\n// Create a replay\n$manager-\u003ecreateReplay('add_extra_field_to_balance_projection', [\n    \"App\\Projectors\\BalanceProjector\"\n]);\n``` \n\n2. The replay can be started. When replaying, it calls the `useConnection` method on the projector. So the projector knows where it should write its data to.\nThis package comes with an EloquentZeroDowntimeProjector that gives you some magic for dealing with different connections.\n```php\n$manager-\u003estartReplay('add_extra_field_to_balance_projection');\n```\n\n3. Once the replay is finished, but there is still some lag to production because of newly recorded events. \nYou can start the replay again, it will start from the latest projected event. Its always possible to monitor the state of the replay and the lag compared to production.\n```php\n// get the state \u0026 progress of your replay \n$manager-\u003egetReplay('add_extra_field_to_balance_projection');\n\n// how many events is the replay behind the event stream?\n$manager-\u003egetReplayLag('add_extra_field_to_balance_projection');\n```\n\n4. Once there is no lag, we can start projecting new events to replays.\n\n```php\n$manager-\u003estartProjectingToReplay('add_extra_field_to_balance_projection');\n```\n\n5. Once every thing checks out, you can promote your replay to production.\n```php\n    $manager-\u003eputReplayLive('add_extra_field_to_balance_projection');\n```\n\n6. Lastly you can cleanup your replay\n```php\n    $manager-\u003eremoveReplay('add_extra_field_to_balance_projection');\n```\n\n## Installation\n\nYou can install the package via composer:\n\n```bash\ncomposer require gosuperscript/zero-downtime-event-replays\n```\n\n[comment]: \u003c\u003e (You can publish and run the migrations with:)\n\n[comment]: \u003c\u003e (```bash)\n\n[comment]: \u003c\u003e (php artisan vendor:publish --provider=\"Gosuperscript\\ZeroDowntimeEventReplays\\ZeroDowntimeEventReplaysServiceProvider\" --tag=\"zero-downtime-event-replays-migrations\")\n\n[comment]: \u003c\u003e (php artisan migrate)\n\n[comment]: \u003c\u003e (```)\n\n[comment]: \u003c\u003e (You can publish the config file with:)\n\n[comment]: \u003c\u003e (```bash)\n\n[comment]: \u003c\u003e (php artisan vendor:publish --provider=\"Gosuperscript\\ZeroDowntimeEventReplays\\ZeroDowntimeEventReplaysServiceProvider\" --tag=\"zero-downtime-event-replays-config\")\n\n[comment]: \u003c\u003e (```)\n\n[comment]: \u003c\u003e (This is the contents of the published config file:)\n\n[comment]: \u003c\u003e (```php)\n\n[comment]: \u003c\u003e (return [)\n\n[comment]: \u003c\u003e (];)\n\n[comment]: \u003c\u003e (```)\n\n## Usage\n\n```php\n$manager = resolve(\\Gosuperscript\\ZeroDowntimeEventReplays\\ReplayManager::class);\n// Create a replay\n$manager-\u003ecreateReplay('your_replay_key', ['projectorA', 'projectorB']);\n\n// Start replay history\n$manager-\u003estartReplay('your_replay_key');\n\n// get the state \u0026 progress of your replay \n$manager-\u003egetReplay('your_replay_key');\n\n// how many events is the replay behind the event stream?\n$manager-\u003egetReplayLag('your_replay_key');\n\n// once a replay is up to date with the event stream, we can project events to it when they happen \n$manager-\u003estartProjectingToReplay('your_replay_key');\n\n// Once the replay is approved, we can promote it to production\n$manager-\u003eputReplayLive('your_replay_key');\n\n// Or we can delete the replay\n$manager-\u003eremoveReplay('your_replay_key');\n\n```\n\n## ZeroDowntime projectors\nIn order to make projectors work with zero downtime replays, they have to implement the `ZeroDowntimeProjector` interface. This interface asks you to implement the following methods: \n```php \ninterface ZeroDowntimeProjector\n{\n    // This method lets the projector know that its replaying on a replay\n    public function forReplay(): self;\n\n    // Sets the connection to replay to, using the replay key. Each connection must be treated as a clone of the production schema.\n    public function useConnection(string $connection): self;\n\n    // Promote your connection to production\n    public function promoteConnectionToProduction(): void;\n\n    // cleanup/remove connection\n    public function removeConnection();\n}\n```\n\nSince most projections probably are replaying to eloquent, this package includes a `EloquentZeroDowntimeProjector` abstract class and a `Projectable` trait to be used on your eloquent read models. \n\nTo make your projectors work with this package: \n1. Make sure your projector extends the `EloquentZeroDowntimeProjector`. \n2. On all read models used by the projector, add the `Projectable` trait. \n3. Implement a `models` method on your projector, that returns all models that the projector writes to. This is used by the EloquentZeroDowntimeProjector in order to setup the right db scheme and promote the right models to production.  \n```php \n    public function models(): array\n    {\n        return [\n            new BalanceProjector(),\n        ];\n    }\n```\n4. Everywhere where you query or update your read model, use the `forProjection` method. \n```php\n    // when truncating\n    Balance::forProjection($this-\u003econnection)-\u003etruncate();\n    \n    // when querying\n    Balance::forProjection($this-\u003econnection)-\u003ewhere('user_id', $event-\u003euser_id)-\u003efirst();\n    \n    // when updating\n    Balance::forProjection($this-\u003econnection)-\u003ewhere('user_id', $event-\u003euser_id)-\u003eincrement('total', $event-\u003eamount);\n    \n    // when newing an instance\n    $balance = Balance::newForProjection($this-\u003econnection, ['id' =\u003e $event-\u003euser_id, 'total' =\u003e $event-\u003eamount]);\n    $balance-\u003esave();\n```\n\n\n\n\n## Testing\n\n```bash\ncomposer test\n```\n\n## Changelog\n\nPlease see [CHANGELOG](CHANGELOG.md) for more information on what has changed recently.\n\n## Contributing\n\nPlease see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.\n\n## Security Vulnerabilities\n\nPlease review [our security policy](../../security/policy) on how to report security vulnerabilities.\n\n## Credits\n\n- [Robertbaelde](https://github.com/robertbaelde)\n- [All Contributors](../../contributors)\n\n## License\n\nThe MIT License (MIT). Please see [License File](LICENSE.md) for more information.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgosuperscript%2Fzero-downtime-event-replays","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgosuperscript%2Fzero-downtime-event-replays","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgosuperscript%2Fzero-downtime-event-replays/lists"}