From 9979b80265b04ace90b4f944e1f14c6ec823b6fe Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 23 Aug 2023 14:34:44 +0200 Subject: [PATCH 1/3] add split stream benchmark --- .../BasicImplementation/Aggregate/Profile.php | 16 +++ .../BasicImplementation/Events/Reborn.php | 22 ++++ ...hSnapshotsBench.php => SnapshotsBench.php} | 2 +- tests/Benchmark/SplitStreamBench.php | 115 ++++++++++++++++++ ...stBench.php => SyncProjectionistBench.php} | 2 +- 5 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 tests/Benchmark/BasicImplementation/Events/Reborn.php rename tests/Benchmark/{LoadEventsWithSnapshotsBench.php => SnapshotsBench.php} (98%) create mode 100644 tests/Benchmark/SplitStreamBench.php rename tests/Benchmark/{WriteEventsWithSyncProjectionistBench.php => SyncProjectionistBench.php} (98%) diff --git a/tests/Benchmark/BasicImplementation/Aggregate/Profile.php b/tests/Benchmark/BasicImplementation/Aggregate/Profile.php index 875ed9b70..a62ce1cb2 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 @@ + Driver::class, + 'path' => self::DB_PATH, + ]); + + $this->bus = new DefaultEventBus(); + + $this->store = new DoctrineDbalStore( + $connection, + DefaultEventSerializer::createFromPaths([__DIR__ . '/BasicImplementation/Events']), + (new AttributeAggregateRootRegistryFactory())->create([__DIR__ . '/BasicImplementation/Aggregate']), + 'eventstore', + ); + + $this->repository = new DefaultRepository( + $this->store, + $this->bus, + Profile::metadata(), + null, + new SplitStreamDecorator( + new AttributeEventMetadataFactory() + ) + ); + + $schemaDirector = new DoctrineSchemaDirector( + $connection, + $this->store, + ); + + $schemaDirector->create(); + } + + public function provideData(): void + { + $profile = Profile::create(ProfileId::fromString('1'), 'Peter'); + + for ($i = 0; $i < 10_000; $i++) { + $profile->changeName(sprintf('Peter %d', $i)); + + if ($i % 100 === 0) { + $profile->reborn(); + } + } + + $this->repository->save($profile); + } + + #[Bench\Revs(20)] + #[Bench\BeforeMethods("provideData")] + public function benchLoad10000Events(): void + { + $this->repository->load('1'); + } + + #[Bench\Revs(20)] + public function benchWrite10000Events(): void + { + $profile = Profile::create(ProfileId::fromString('1'), 'Peter'); + + for ($i = 0; $i < 10_000; $i++) { + $profile->changeName(sprintf('Peter %d', $i)); + + if ($i % 100 === 0) { + $profile->reborn(); + } + } + + $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'; From 17afc2f6ca13a92074dfb70fb3de63f894915aab Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 13 Dec 2023 12:56:23 +0100 Subject: [PATCH 2/3] fix benchmark --- .../BasicImplementation/Aggregate/Profile.php | 2 +- .../BasicImplementation/ProfileId.php | 7 ++++++ tests/Benchmark/SplitStreamBench.php | 23 +++++++++++-------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/tests/Benchmark/BasicImplementation/Aggregate/Profile.php b/tests/Benchmark/BasicImplementation/Aggregate/Profile.php index a62ce1cb2..9d02bcc8c 100644 --- a/tests/Benchmark/BasicImplementation/Aggregate/Profile.php +++ b/tests/Benchmark/BasicImplementation/Aggregate/Profile.php @@ -44,7 +44,7 @@ public function reborn(): void { $this->recordThat(new Reborn( $this->id, - $this->name + $this->name, )); } diff --git a/tests/Benchmark/BasicImplementation/ProfileId.php b/tests/Benchmark/BasicImplementation/ProfileId.php index c0d4a1007..188a496b4 100644 --- a/tests/Benchmark/BasicImplementation/ProfileId.php +++ b/tests/Benchmark/BasicImplementation/ProfileId.php @@ -4,6 +4,8 @@ namespace Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation; +use function uniqid; + final class ProfileId { private function __construct( @@ -20,4 +22,9 @@ public function toString(): string { return $this->id; } + + public static function generate(): self + { + return new self(uniqid('', true)); + } } diff --git a/tests/Benchmark/SplitStreamBench.php b/tests/Benchmark/SplitStreamBench.php index 455d8da56..cc90efd82 100644 --- a/tests/Benchmark/SplitStreamBench.php +++ b/tests/Benchmark/SplitStreamBench.php @@ -15,8 +15,6 @@ use Patchlevel\EventSourcing\Repository\Repository; use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector; use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer; -use Patchlevel\EventSourcing\Snapshot\Adapter\InMemorySnapshotAdapter; -use Patchlevel\EventSourcing\Snapshot\DefaultSnapshotStore; use Patchlevel\EventSourcing\Snapshot\SnapshotStore; use Patchlevel\EventSourcing\Store\DoctrineDbalStore; use Patchlevel\EventSourcing\Store\Store; @@ -25,6 +23,7 @@ use PhpBench\Attributes as Bench; use function file_exists; +use function sprintf; use function unlink; #[Bench\BeforeMethods('setUp')] @@ -63,8 +62,8 @@ public function setUp(): void Profile::metadata(), null, new SplitStreamDecorator( - new AttributeEventMetadataFactory() - ) + new AttributeEventMetadataFactory(), + ), ); $schemaDirector = new DoctrineSchemaDirector( @@ -82,16 +81,18 @@ public function provideData(): void for ($i = 0; $i < 10_000; $i++) { $profile->changeName(sprintf('Peter %d', $i)); - if ($i % 100 === 0) { - $profile->reborn(); + if ($i % 100 !== 0) { + continue; } + + $profile->reborn(); } $this->repository->save($profile); } #[Bench\Revs(20)] - #[Bench\BeforeMethods("provideData")] + #[Bench\BeforeMethods('provideData')] public function benchLoad10000Events(): void { $this->repository->load('1'); @@ -100,14 +101,16 @@ public function benchLoad10000Events(): void #[Bench\Revs(20)] public function benchWrite10000Events(): void { - $profile = Profile::create(ProfileId::fromString('1'), 'Peter'); + $profile = Profile::create(ProfileId::generate(), 'Peter'); for ($i = 0; $i < 10_000; $i++) { $profile->changeName(sprintf('Peter %d', $i)); - if ($i % 100 === 0) { - $profile->reborn(); + if ($i % 100 !== 0) { + continue; } + + $profile->reborn(); } $this->repository->save($profile); From 6134991e7fe49943a037c1261ce5ef5381632a9d Mon Sep 17 00:00:00 2001 From: David Badura Date: Wed, 13 Dec 2023 13:13:49 +0100 Subject: [PATCH 3/3] improve benchmarks --- baseline.xml | 11 +- .../Adapter/InMemorySnapshotAdapter.php | 5 + ...adEventsBench.php => SimpleSetupBench.php} | 21 +++- tests/Benchmark/SnapshotsBench.php | 13 ++- tests/Benchmark/WriteEventsBench.php | 102 ------------------ 5 files changed, 43 insertions(+), 109 deletions(-) rename tests/Benchmark/{LoadEventsBench.php => SimpleSetupBench.php} (82%) delete mode 100644 tests/Benchmark/WriteEventsBench.php 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/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/SnapshotsBench.php b/tests/Benchmark/SnapshotsBench.php index 36f94ecea..0a14eee32 100644 --- a/tests/Benchmark/SnapshotsBench.php +++ b/tests/Benchmark/SnapshotsBench.php @@ -35,6 +35,8 @@ final class SnapshotsBench 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/WriteEventsBench.php deleted file mode 100644 index 0a2cff4ba..000000000 --- a/tests/Benchmark/WriteEventsBench.php +++ /dev/null @@ -1,102 +0,0 @@ - Driver::class, - 'path' => self::DB_PATH, - ]); - - $this->store = new DoctrineDbalStore( - $connection, - DefaultEventSerializer::createFromPaths([__DIR__ . '/BasicImplementation/Events']), - (new AttributeAggregateRootRegistryFactory())->create([__DIR__ . '/BasicImplementation/Aggregate']), - 'eventstore', - ); - - $profileProjection = new ProfileProjector($connection); - $projectionRepository = new InMemoryProjectorRepository( - [$profileProjection], - ); - - $projectionist = new DefaultProjectionist( - $this->store, - new InMemoryStore(), - $projectionRepository, - ); - - $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(); - - $this->profile = Profile::create(ProfileId::fromString('1'), 'Peter'); - $this->repository->save($this->profile); - } - - #[Bench\Revs(20)] - public function benchSave1Event(): void - { - $this->profile->changeName('Peter'); - $this->repository->save($this->profile); - } - - #[Bench\Revs(20)] - public function benchSave10000Events(): void - { - for ($i = 0; $i < 10_000; $i++) { - $this->profile->changeName('Peter'); - } - - $this->repository->save($this->profile); - } -}