diff --git a/baseline.xml b/baseline.xml index 1516fe05e..6ad294974 100644 --- a/baseline.xml +++ b/baseline.xml @@ -82,30 +82,31 @@ $name - + $bus $repository $store - + + $adapter $bus $repository $snapshotStore $store - + $bus - $profile $repository + $snapshotStore $store - + $bus $profile diff --git a/src/Snapshot/Adapter/InMemorySnapshotAdapter.php b/src/Snapshot/Adapter/InMemorySnapshotAdapter.php index 74fa75bc3..70ab200da 100644 --- a/src/Snapshot/Adapter/InMemorySnapshotAdapter.php +++ b/src/Snapshot/Adapter/InMemorySnapshotAdapter.php @@ -26,4 +26,9 @@ public function load(string $key): array return $this->snapshots[$key]; } + + public function clear(): void + { + $this->snapshots = []; + } } diff --git a/tests/Benchmark/BasicImplementation/Aggregate/Profile.php b/tests/Benchmark/BasicImplementation/Aggregate/Profile.php index 875ed9b70..9d02bcc8c 100644 --- a/tests/Benchmark/BasicImplementation/Aggregate/Profile.php +++ b/tests/Benchmark/BasicImplementation/Aggregate/Profile.php @@ -10,6 +10,7 @@ use Patchlevel\EventSourcing\Attribute\Snapshot; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events\NameChanged; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events\ProfileCreated; +use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Events\Reborn; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Normalizer\ProfileIdNormalizer; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId; @@ -39,6 +40,14 @@ public function changeName(string $name): void $this->recordThat(new NameChanged($name)); } + public function reborn(): void + { + $this->recordThat(new Reborn( + $this->id, + $this->name, + )); + } + #[Apply] protected function applyProfileCreated(ProfileCreated $event): void { @@ -52,6 +61,13 @@ protected function applyNameChanged(NameChanged $event): void $this->name = $event->name; } + #[Apply] + protected function applyReborn(Reborn $event): void + { + $this->id = $event->profileId; + $this->name = $event->name; + } + public function name(): string { return $this->name; diff --git a/tests/Benchmark/BasicImplementation/Events/Reborn.php b/tests/Benchmark/BasicImplementation/Events/Reborn.php new file mode 100644 index 000000000..e4d7e9ef2 --- /dev/null +++ b/tests/Benchmark/BasicImplementation/Events/Reborn.php @@ -0,0 +1,22 @@ +id; } + + public static function generate(): self + { + return new self(uniqid('', true)); + } } diff --git a/tests/Benchmark/LoadEventsBench.php b/tests/Benchmark/SimpleSetupBench.php similarity index 82% rename from tests/Benchmark/LoadEventsBench.php rename to tests/Benchmark/SimpleSetupBench.php index fb98e5bd7..a2c2078ae 100644 --- a/tests/Benchmark/LoadEventsBench.php +++ b/tests/Benchmark/SimpleSetupBench.php @@ -23,7 +23,7 @@ use function unlink; #[Bench\BeforeMethods('setUp')] -final class LoadEventsBench +final class SimpleSetupBench { private const DB_PATH = __DIR__ . '/BasicImplementation/data/db.sqlite3'; @@ -74,4 +74,23 @@ public function benchLoad10000Events(): void { $this->repository->load('1'); } + + #[Bench\Revs(20)] + public function benchSave1Event(): void + { + $profile = Profile::create(ProfileId::generate(), 'Peter'); + $this->repository->save($profile); + } + + #[Bench\Revs(20)] + public function benchSave10000Events(): void + { + $profile = Profile::create(ProfileId::generate(), 'Peter'); + + for ($i = 1; $i < 10_000; $i++) { + $profile->changeName('Peter'); + } + + $this->repository->save($profile); + } } diff --git a/tests/Benchmark/LoadEventsWithSnapshotsBench.php b/tests/Benchmark/SnapshotsBench.php similarity index 89% rename from tests/Benchmark/LoadEventsWithSnapshotsBench.php rename to tests/Benchmark/SnapshotsBench.php index 2f87ff3ba..0a14eee32 100644 --- a/tests/Benchmark/LoadEventsWithSnapshotsBench.php +++ b/tests/Benchmark/SnapshotsBench.php @@ -26,7 +26,7 @@ use function unlink; #[Bench\BeforeMethods('setUp')] -final class LoadEventsWithSnapshotsBench +final class SnapshotsBench { private const DB_PATH = __DIR__ . '/BasicImplementation/data/db.sqlite3'; @@ -35,6 +35,8 @@ final class LoadEventsWithSnapshotsBench private SnapshotStore $snapshotStore; private Repository $repository; + private InMemorySnapshotAdapter $adapter; + public function setUp(): void { if (file_exists(self::DB_PATH)) { @@ -55,7 +57,9 @@ public function setUp(): void 'eventstore', ); - $this->snapshotStore = new DefaultSnapshotStore(['default' => new InMemorySnapshotAdapter()]); + $this->adapter = new InMemorySnapshotAdapter(); + + $this->snapshotStore = new DefaultSnapshotStore(['default' => $this->adapter]); $this->repository = new DefaultRepository($this->store, $this->bus, Profile::metadata(), $this->snapshotStore); @@ -76,6 +80,13 @@ public function setUp(): void $this->snapshotStore->save($profile); } + #[Bench\Revs(20)] + public function benchLoad10000EventsMissingSnapshot(): void + { + $this->adapter->clear(); + $this->repository->load('1'); + } + #[Bench\Revs(20)] public function benchLoad10000Events(): void { diff --git a/tests/Benchmark/WriteEventsBench.php b/tests/Benchmark/SplitStreamBench.php similarity index 60% rename from tests/Benchmark/WriteEventsBench.php rename to tests/Benchmark/SplitStreamBench.php index 0a2cff4ba..cc90efd82 100644 --- a/tests/Benchmark/WriteEventsBench.php +++ b/tests/Benchmark/SplitStreamBench.php @@ -6,36 +6,35 @@ use Doctrine\DBAL\Driver\PDO\SQLite\Driver; use Doctrine\DBAL\DriverManager; +use Patchlevel\EventSourcing\EventBus\Decorator\SplitStreamDecorator; use Patchlevel\EventSourcing\EventBus\DefaultEventBus; use Patchlevel\EventSourcing\EventBus\EventBus; use Patchlevel\EventSourcing\Metadata\AggregateRoot\AttributeAggregateRootRegistryFactory; -use Patchlevel\EventSourcing\Projection\Projection\Store\InMemoryStore; -use Patchlevel\EventSourcing\Projection\Projectionist\DefaultProjectionist; -use Patchlevel\EventSourcing\Projection\Projector\InMemoryProjectorRepository; +use Patchlevel\EventSourcing\Metadata\Event\AttributeEventMetadataFactory; use Patchlevel\EventSourcing\Repository\DefaultRepository; use Patchlevel\EventSourcing\Repository\Repository; use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector; use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer; +use Patchlevel\EventSourcing\Snapshot\SnapshotStore; use Patchlevel\EventSourcing\Store\DoctrineDbalStore; use Patchlevel\EventSourcing\Store\Store; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Aggregate\Profile; -use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Processor\SendEmailProcessor; use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId; -use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Projection\ProfileProjector; use PhpBench\Attributes as Bench; use function file_exists; +use function sprintf; use function unlink; #[Bench\BeforeMethods('setUp')] -final class WriteEventsBench +final class SplitStreamBench { private const DB_PATH = __DIR__ . '/BasicImplementation/data/db.sqlite3'; private Store $store; private EventBus $bus; + private SnapshotStore $snapshotStore; private Repository $repository; - private Profile $profile; public function setUp(): void { @@ -48,6 +47,8 @@ public function setUp(): void 'path' => self::DB_PATH, ]); + $this->bus = new DefaultEventBus(); + $this->store = new DoctrineDbalStore( $connection, DefaultEventSerializer::createFromPaths([__DIR__ . '/BasicImplementation/Events']), @@ -55,48 +56,63 @@ public function setUp(): void 'eventstore', ); - $profileProjection = new ProfileProjector($connection); - $projectionRepository = new InMemoryProjectorRepository( - [$profileProjection], - ); - - $projectionist = new DefaultProjectionist( + $this->repository = new DefaultRepository( $this->store, - new InMemoryStore(), - $projectionRepository, + $this->bus, + Profile::metadata(), + null, + new SplitStreamDecorator( + new AttributeEventMetadataFactory(), + ), ); - $this->bus = new DefaultEventBus(); - $this->bus->addListener(new SendEmailProcessor()); - - $this->repository = new DefaultRepository($this->store, $this->bus, Profile::metadata()); - $schemaDirector = new DoctrineSchemaDirector( $connection, $this->store, ); $schemaDirector->create(); - $projectionist->boot(); + } + + public function provideData(): void + { + $profile = Profile::create(ProfileId::fromString('1'), 'Peter'); + + for ($i = 0; $i < 10_000; $i++) { + $profile->changeName(sprintf('Peter %d', $i)); - $this->profile = Profile::create(ProfileId::fromString('1'), 'Peter'); - $this->repository->save($this->profile); + if ($i % 100 !== 0) { + continue; + } + + $profile->reborn(); + } + + $this->repository->save($profile); } #[Bench\Revs(20)] - public function benchSave1Event(): void + #[Bench\BeforeMethods('provideData')] + public function benchLoad10000Events(): void { - $this->profile->changeName('Peter'); - $this->repository->save($this->profile); + $this->repository->load('1'); } #[Bench\Revs(20)] - public function benchSave10000Events(): void + public function benchWrite10000Events(): void { + $profile = Profile::create(ProfileId::generate(), 'Peter'); + for ($i = 0; $i < 10_000; $i++) { - $this->profile->changeName('Peter'); + $profile->changeName(sprintf('Peter %d', $i)); + + if ($i % 100 !== 0) { + continue; + } + + $profile->reborn(); } - $this->repository->save($this->profile); + $this->repository->save($profile); } } diff --git a/tests/Benchmark/WriteEventsWithSyncProjectionistBench.php b/tests/Benchmark/SyncProjectionistBench.php similarity index 98% rename from tests/Benchmark/WriteEventsWithSyncProjectionistBench.php rename to tests/Benchmark/SyncProjectionistBench.php index ab691e939..028eec29c 100644 --- a/tests/Benchmark/WriteEventsWithSyncProjectionistBench.php +++ b/tests/Benchmark/SyncProjectionistBench.php @@ -33,7 +33,7 @@ use function unlink; #[Bench\BeforeMethods('setUp')] -final class WriteEventsWithSyncProjectionistBench +final class SyncProjectionistBench { private const DB_PATH = __DIR__ . '/BasicImplementation/data/db.sqlite3';