Skip to content

Commit

Permalink
bulk insert
Browse files Browse the repository at this point in the history
  • Loading branch information
DavidBadura committed Dec 17, 2023
1 parent 1129cc9 commit 71689b0
Show file tree
Hide file tree
Showing 3 changed files with 142 additions and 20 deletions.
23 changes: 23 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
FROM php:8.3

ARG EXTENSIONS="pcntl zip intl bcmath"

RUN apt-get update && apt-get install -y \
git \
zip \
unzip \
curl \
&& rm -rf /var/lib/apt/lists/*

COPY --from=composer /usr/bin/composer /usr/bin/composer

ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/

RUN chmod +x /usr/local/bin/install-php-extensions && \
install-php-extensions $EXTENSIONS

RUN mkdir -p /tmp/blackfire \
&& architecture=$(uname -m) \
&& curl -A "Docker" -L https://blackfire.io/api/v1/releases/cli/linux/$architecture | tar zxp -C /tmp/blackfire \
&& mv /tmp/blackfire/blackfire /usr/bin/blackfire \
&& rm -Rf /tmp/blackfire
85 changes: 65 additions & 20 deletions src/Store/DoctrineDbalStore.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
use Doctrine\DBAL\Connection;
use Doctrine\DBAL\Query\QueryBuilder;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Types\Types;
use Patchlevel\EventSourcing\Aggregate\AggregateRoot;
use Patchlevel\EventSourcing\EventBus\HeaderNotFound;
Expand All @@ -16,6 +17,9 @@
use Patchlevel\EventSourcing\Schema\SchemaConfigurator;
use Patchlevel\EventSourcing\Serializer\EventSerializer;

use function array_fill;
use function count;
use function implode;
use function is_int;
use function is_string;
use function sprintf;
Expand All @@ -27,6 +31,7 @@ public function __construct(
private readonly EventSerializer $serializer,
private readonly AggregateRootRegistry $aggregateRootRegistry,
private readonly string $storeTableName = 'eventstore',
private readonly int $batch = 1000,

Check failure on line 34 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Property Patchlevel\EventSourcing\Store\DoctrineDbalStore::$batch is never read, only written.
) {
}

Expand Down Expand Up @@ -114,38 +119,78 @@ public function save(Message ...$messages): void
{
$this->connection->transactional(
function (Connection $connection) use ($messages): void {
$jsonType = Type::getType(Types::JSON);
$booleanType = Type::getType(Types::BOOLEAN);
$dateTimeType = Type::getType(Types::DATETIMETZ_IMMUTABLE);

$placeholders = [];

$columns = [
'aggregate',
'aggregate_id',
'playhead',
'event',
'payload',
'recorded_on',
'new_stream_start',
'archived',
'custom_headers',
];

$placeholder = implode(', ', array_fill(0, count($columns), '?'));

foreach ($messages as $message) {
$data = $this->serializer->serialize($message->event());

try {
$connection->insert(
$this->storeTableName,
[
'aggregate' => $this->aggregateRootRegistry->aggregateName($message->aggregateClass()),
'aggregate_id' => $message->aggregateId(),
'playhead' => $message->playhead(),
'event' => $data->name,
'payload' => $data->payload,
'recorded_on' => $message->recordedOn(),
'new_stream_start' => $message->newStreamStart(),
'archived' => $message->archived(),
'custom_headers' => $message->customHeaders(),
],
[
'recorded_on' => Types::DATETIMETZ_IMMUTABLE,
'custom_headers' => Types::JSON,
'new_stream_start' => Types::BOOLEAN,
'archived' => Types::BOOLEAN,
],
);
$parameters[] = $this->aggregateRootRegistry->aggregateName($message->aggregateClass());

Check failure on line 146 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

PossiblyUndefinedVariable

src/Store/DoctrineDbalStore.php:146:25: PossiblyUndefinedVariable: Possibly undefined variable $parameters, first seen on line 146 (see https://psalm.dev/018)

Check failure on line 146 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:146:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $message->aggregateId();

Check failure on line 147 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:147:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $message->playhead();

Check failure on line 148 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:148:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $data->name;

Check failure on line 149 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:149:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $data->payload;

Check failure on line 150 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:150:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $dateTimeType->convertToDatabaseValue($message->recordedOn(), $connection->getDatabasePlatform());

Check failure on line 151 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedAssignment

src/Store/DoctrineDbalStore.php:151:25: MixedAssignment: Unable to determine the type of this assignment (see https://psalm.dev/032)

Check failure on line 151 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:151:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $booleanType->convertToDatabaseValue($message->newStreamStart(), $connection->getDatabasePlatform());

Check failure on line 152 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedAssignment

src/Store/DoctrineDbalStore.php:152:25: MixedAssignment: Unable to determine the type of this assignment (see https://psalm.dev/032)

Check failure on line 152 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by Psalm (locked, 8.3, ubuntu-latest)

MixedArrayAssignment

src/Store/DoctrineDbalStore.php:152:25: MixedArrayAssignment: Cannot access array value on mixed variable $parameters (see https://psalm.dev/117)
$parameters[] = $booleanType->convertToDatabaseValue($message->archived(), $connection->getDatabasePlatform());
$parameters[] = $jsonType->convertToDatabaseValue($message->customHeaders(), $connection->getDatabasePlatform());
} catch (HeaderNotFound $e) {
throw new MissingDataForStorage($e->name, $e);
}

$placeholders[] = $placeholder;
}

if ($parameters === []) {

Check failure on line 162 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Strict comparison using === between non-empty-array<int<0, max>, mixed> and array{} will always evaluate to false.

Check failure on line 162 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Variable $parameters might not be defined.
return;
}

$query = sprintf(
"INSERT INTO %s (%s) VALUES\n(%s)",
$this->storeTableName,
implode(', ', $columns),
implode("),\n(", $placeholders),
);

$connection->executeStatement($query, $parameters);

Check failure on line 173 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Variable $parameters might not be defined.
},
);
}

/**
* @param string[] $fields
*
* @return string[]
*/
private function placeholder(array $fields): array

Check failure on line 183 in src/Store/DoctrineDbalStore.php

View workflow job for this annotation

GitHub Actions / Static Analysis by PHPStan (locked, 8.3, ubuntu-latest)

Method Patchlevel\EventSourcing\Store\DoctrineDbalStore::placeholder() is unused.
{
$placeholders = [];

foreach ($fields as $field) {
$placeholders[] = ':' . $field;
}

return $placeholders;
}

/**
* @param Closure():ClosureReturn $function
*
Expand Down
54 changes: 54 additions & 0 deletions tests/Benchmark/blackfire.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
<?php

declare(strict_types=1);

use Doctrine\DBAL\Driver\PDO\SQLite\Driver;
use Doctrine\DBAL\DriverManager;
use Patchlevel\EventSourcing\EventBus\DefaultEventBus;
use Patchlevel\EventSourcing\Metadata\AggregateRoot\AttributeAggregateRootRegistryFactory;
use Patchlevel\EventSourcing\Repository\DefaultRepository;
use Patchlevel\EventSourcing\Schema\DoctrineSchemaDirector;
use Patchlevel\EventSourcing\Serializer\DefaultEventSerializer;
use Patchlevel\EventSourcing\Store\DoctrineDbalStore;
use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\Aggregate\Profile;
use Patchlevel\EventSourcing\Tests\Benchmark\BasicImplementation\ProfileId;

require_once __DIR__ . '/../../vendor/autoload.php';

const DB_PATH = __DIR__ . '/BasicImplementation/data/db.sqlite3';

if (file_exists(DB_PATH)) {
unlink(DB_PATH);
}

$connection = DriverManager::getConnection([
'driverClass' => Driver::class,
'path' => DB_PATH,
]);

$bus = new DefaultEventBus();

$store = new DoctrineDbalStore(
$connection,
DefaultEventSerializer::createFromPaths([__DIR__ . '/BasicImplementation/Events']),
(new AttributeAggregateRootRegistryFactory())->create([__DIR__ . '/BasicImplementation/Aggregate']),
'eventstore',
);

$repository = new DefaultRepository($store, $bus, Profile::metadata());

$schemaDirector = new DoctrineSchemaDirector(
$connection,
$store,
);

$schemaDirector->create();

$id = ProfileId::generate();
$profile = Profile::create($id, 'Peter');

for ($i = 0; $i < 10_000; $i++) {
$profile->changeName('Peter ' . $i);
}

$repository->save($profile);

0 comments on commit 71689b0

Please sign in to comment.