Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replay events #387

Closed
keizah7 opened this issue Jan 13, 2023 · 2 comments
Closed

Replay events #387

keizah7 opened this issue Jan 13, 2023 · 2 comments

Comments

@keizah7
Copy link
Contributor

keizah7 commented Jan 13, 2023

How to replay all events and avoid Integrity constraint violation: 1062 Duplicate entry ?
How to replay from X event number and avoid same error?

Replaying is not working in spatie/laravel-shop too

php artisan event-sourcing:replay
Illuminate\Database\QueryException 

SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '16c81e18-212a-4027-991d-db6642803ff1' for key 'cart_durations.cart_durations_uuid_unique' (SQL: insert into `cart_durations` (`uuid`, `cart_uuid`, `created_at`) values (16c81e18-212a-4027-991d-db6642803ff1, 16c81e18-212a-4027-991d-db6642803ff1, 2023-01-13 11:59:23))

app/Domain/Cart/Projectors/CartDurationProjector.php:24
      Illuminate\Database\Eloquent\Model::__call("create")

it's not working with providing custom event too

php artisan event-sourcing:replay --from=4 
 Are you sure you want to replay events to all projectors? (yes/no) [yes]:
 > yes

Replaying 5 events...
 1/5 [▓▓▓▓▓░░░░░░░░░░░░░░░░░░░░░░░]  20%
   Illuminate\Database\QueryException 

  SQLSTATE[23000]: Integrity constraint violation: 1062 Duplicate entry '1ce84e51-43eb-41e4-860a-38e3cc824c84' for key 'cart_items.cart_items_uuid_unique' (SQL: insert into `cart_items` (`uuid`, `cart_uuid`, `product_id`, `amount`, `price_per_item_excluding_vat`, `price_per_item_including_vat`, `vat_percentage`, `vat_price`, `total_item_price_excluding_vat`, `total_item_price_including_vat`, `updated_at`, `created_at`) values (1ce84e51-43eb-41e4-860a-38e3cc824c84, 956aa575-8de2-49a0-ad59-2f385302286f, 5, 1, 8152, 9864, 21, 1712, 8152, 9864, 2023-01-13 12:36:28, 2023-01-13 12:36:28))

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:678
    674▕         // If an exception occurs when attempting to run a query, we'll format the error
    675▕         // message to include the bindings with SQL, which will make this exception a
    676▕         // lot more helpful to the developer instead of just the database's errors.
    677▕         catch (Exception $e) {
  ➜ 678▕             throw new QueryException(
    679▕                 $query, $this->prepareBindings($bindings), $e
    680▕             );
    681▕         }
    682▕ 

      +15 vendor frames 
  16  app/Domain/Cart/Projectors/CartItemProjector.php:25
      Illuminate\Database\Eloquent\Model::__call("create")

Using delete replay is successful then we replaying all events, but it is not working in replaying starting form X event number

public function resetState(): void
{
        Cart::query()->delete();
}

resetState is not event called then replaying form X event

public function resetState(): void
{
    dd('test');
    Cart::query()->delete();
}

How we should properly reset state in replaying event from X event number?

Maybe you can fix your premium package code?

@SimonMacIntyre
Copy link

How we should properly reset state in replaying event from X event number?

This is critical.

Off the top of my head, I am wondering how to build around this. Since there are not really hard rules set in stone about projectors being actually linked to specific tables etc.

Otherwise, it seems like the event or projection itself would need metadata linking it to a specific event number, so that something automated could theoretically only delete all projection table entries that are sourced from event >= 5.

Maybe a trait like projectionHasEventSource which adds like... an identifier column linking it to the event creation. It would also require projectors only ever projecting to a single table, I think. Which is not enforced, and requires following this standard.

I'm not sure a graceful way to build this into the package.

A workaround as you already alluded to yourself, might be to just always rely on replaying all events and never replaying a subset. Although it is far more work if you have a table with millions of rows, in theory the eventual consistency final state should be the same in both. So although it is inefficient, it should work to replay all events if you wipe the entire table and ALWAYS have a 1 to 1 link between a projection and a projector. (Maybe snapshotting will be enough for this workaround to function longterm at scale...)

@SimonMacIntyre
Copy link

Quote from the docs:

If your projector has a resetState method it will get called before replaying events. You can use that method to reset the state of your projector.

If you want to replay events starting from a certain event you can use the --from option when executing event-sourcing:replay. If you use this option the resetState on projectors will not get called. This package does not track which events have already been processed by which projectors. Be sure not to replay events to projectors that already have handled them.

So it would seem the onus is on us when using --from to be very careful.

@spatie spatie locked and limited conversation to collaborators Mar 14, 2023
@freekmurze freekmurze converted this issue into discussion #400 Mar 14, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants