Skip to content

Commit

Permalink
Update events to play nice with transactions (#2594)
Browse files Browse the repository at this point in the history
* Pass session and transaction information to event args

* Only dispatch lifecycle events once per commit operation

* Remove isInTransaction property in event args

* Split method signature for readability

* Use property promotion for event args classes

* Extract construction of eventArgs

* Inline spl_object_hash calls

* Avoid injecting test instance

* Add session to commitOptions in persister

* Add session assertions in LifecycleEventManager
  • Loading branch information
alcaeus authored Jan 8, 2024
1 parent 601714f commit 6126986
Show file tree
Hide file tree
Showing 8 changed files with 455 additions and 57 deletions.
15 changes: 15 additions & 0 deletions lib/Doctrine/ODM/MongoDB/Event/LifecycleEventArgs.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\Persistence\Event\LifecycleEventArgs as BaseLifecycleEventArgs;
use Doctrine\Persistence\ObjectManager;
use MongoDB\Driver\Session;

/**
* Lifecycle Events are triggered by the UnitOfWork during lifecycle transitions
Expand All @@ -15,6 +17,14 @@
*/
class LifecycleEventArgs extends BaseLifecycleEventArgs
{
public function __construct(
object $object,
ObjectManager $objectManager,
public readonly ?Session $session = null,
) {
parent::__construct($object, $objectManager);
}

public function getDocument(): object
{
return $this->getObject();
Expand All @@ -24,4 +34,9 @@ public function getDocumentManager(): DocumentManager
{
return $this->getObjectManager();
}

public function isInTransaction(): bool
{
return $this->session?->isInTransaction() ?? false;
}
}
16 changes: 8 additions & 8 deletions lib/Doctrine/ODM/MongoDB/Event/PreLoadEventArgs.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,21 @@
namespace Doctrine\ODM\MongoDB\Event;

use Doctrine\ODM\MongoDB\DocumentManager;
use MongoDB\Driver\Session;

/**
* Class that holds event arguments for a preLoad event.
*/
final class PreLoadEventArgs extends LifecycleEventArgs
{
/** @var array<string, mixed> */
private array $data;

/** @param array<string, mixed> $data */
public function __construct(object $document, DocumentManager $dm, array &$data)
{
parent::__construct($document, $dm);

$this->data =& $data;
public function __construct(
object $document,
DocumentManager $dm,
private array &$data,
?Session $session = null,
) {
parent::__construct($document, $dm, $session);
}

/**
Expand Down
32 changes: 17 additions & 15 deletions lib/Doctrine/ODM/MongoDB/Event/PreUpdateEventArgs.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use Doctrine\ODM\MongoDB\DocumentManager;
use Doctrine\ODM\MongoDB\UnitOfWork;
use InvalidArgumentException;
use MongoDB\Driver\Session;

use function get_class;
use function sprintf;
Expand All @@ -18,26 +19,27 @@
*/
final class PreUpdateEventArgs extends LifecycleEventArgs
{
/** @psalm-var array<string, ChangeSet> */
private array $documentChangeSet;

/** @psalm-param array<string, ChangeSet> $changeSet */
public function __construct(object $document, DocumentManager $dm, array $changeSet)
{
parent::__construct($document, $dm);

$this->documentChangeSet = $changeSet;
public function __construct(
object $document,
DocumentManager $dm,
private array $changeSet,
?Session $session = null,
) {
parent::__construct($document, $dm, $session);

$this->changeSet = $changeSet;
}

/** @return array<string, ChangeSet> */
public function getDocumentChangeSet(): array
{
return $this->documentChangeSet;
return $this->changeSet;
}

public function hasChangedField(string $field): bool
{
return isset($this->documentChangeSet[$field]);
return isset($this->changeSet[$field]);
}

/**
Expand All @@ -49,7 +51,7 @@ public function getOldValue(string $field)
{
$this->assertValidField($field);

return $this->documentChangeSet[$field][0];
return $this->changeSet[$field][0];
}

/**
Expand All @@ -61,7 +63,7 @@ public function getNewValue(string $field)
{
$this->assertValidField($field);

return $this->documentChangeSet[$field][1];
return $this->changeSet[$field][1];
}

/**
Expand All @@ -73,8 +75,8 @@ public function setNewValue(string $field, $value): void
{
$this->assertValidField($field);

$this->documentChangeSet[$field][1] = $value;
$this->getDocumentManager()->getUnitOfWork()->setDocumentChangeSet($this->getDocument(), $this->documentChangeSet);
$this->changeSet[$field][1] = $value;
$this->getDocumentManager()->getUnitOfWork()->setDocumentChangeSet($this->getDocument(), $this->changeSet);
}

/**
Expand All @@ -84,7 +86,7 @@ public function setNewValue(string $field, $value): void
*/
private function assertValidField(string $field): void
{
if (! isset($this->documentChangeSet[$field])) {
if (! isset($this->changeSet[$field])) {
throw new InvalidArgumentException(sprintf(
'Field "%s" is not a valid field of the document "%s" in PreUpdateEventArgs.',
$field,
Expand Down
5 changes: 5 additions & 0 deletions lib/Doctrine/ODM/MongoDB/MongoDBException.php
Original file line number Diff line number Diff line change
Expand Up @@ -155,4 +155,9 @@ public static function cannotCreateRepository(string $className): self
{
return new self(sprintf('Cannot create repository for class "%s".', $className));
}

public static function transactionalSessionMismatch(): self
{
return new self('The transactional operation cannot be executed because it was started in a different session.');
}
}
9 changes: 8 additions & 1 deletion lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,14 @@
*
* @template T of object
*
* @psalm-import-type CommitOptions from UnitOfWork
* @psalm-type CommitOptions array{
* fsync?: bool,
* safe?: int,
* session?: ?Session,
* w?: int,
* withTransaction?: bool,
* writeConcern?: WriteConcern
* }
* @psalm-import-type Hints from UnitOfWork
* @psalm-import-type FieldMapping from ClassMetadata
* @psalm-import-type SortMeta from Sort
Expand Down
17 changes: 11 additions & 6 deletions lib/Doctrine/ODM/MongoDB/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -456,8 +456,12 @@ public function commit(array $options = []): void
$this->evm->dispatchEvent(Events::onFlush, new Event\OnFlushEventArgs($this->dm));

if ($this->useTransaction($options)) {
$session = $this->dm->getClient()->startSession();

$this->lifecycleEventManager->enableTransactionalMode($session);

with_transaction(
$this->dm->getClient()->startSession(),
$session,
function (Session $session) use ($options): void {
$this->doCommit(['session' => $session] + $this->stripTransactionOptions($options));
},
Expand All @@ -484,6 +488,7 @@ function (Session $session) use ($options): void {
$this->hasScheduledCollections = [];
} finally {
$this->commitsInProgress--;
$this->lifecycleEventManager->clearTransactionalState();
}
}

Expand Down Expand Up @@ -1171,7 +1176,7 @@ private function executeInserts(ClassMetadata $class, array $documents, array $o
$persister->executeInserts($options);

foreach ($documents as $document) {
$this->lifecycleEventManager->postPersist($class, $document);
$this->lifecycleEventManager->postPersist($class, $document, $options['session'] ?? null);
}
}

Expand All @@ -1195,7 +1200,7 @@ private function executeUpserts(ClassMetadata $class, array $documents, array $o
$persister->executeUpserts($options);

foreach ($documents as $document) {
$this->lifecycleEventManager->postPersist($class, $document);
$this->lifecycleEventManager->postPersist($class, $document, $options['session'] ?? null);
}
}

Expand All @@ -1218,13 +1223,13 @@ private function executeUpdates(ClassMetadata $class, array $documents, array $o
$persister = $this->getDocumentPersister($className);

foreach ($documents as $oid => $document) {
$this->lifecycleEventManager->preUpdate($class, $document);
$this->lifecycleEventManager->preUpdate($class, $document, $options['session'] ?? null);

if (! empty($this->documentChangeSets[$oid]) || $this->hasScheduledCollections($document)) {
$persister->update($document, $options);
}

$this->lifecycleEventManager->postUpdate($class, $document);
$this->lifecycleEventManager->postUpdate($class, $document, $options['session'] ?? null);
}
}

Expand Down Expand Up @@ -1266,7 +1271,7 @@ private function executeDeletions(ClassMetadata $class, array $documents, array
$value->clearSnapshot();
}

$this->lifecycleEventManager->postRemove($class, $document);
$this->lifecycleEventManager->postRemove($class, $document, $options['session'] ?? null);
}
}

Expand Down
Loading

0 comments on commit 6126986

Please sign in to comment.