Skip to content

Commit

Permalink
Preserve collection changes until transaction has succeeded
Browse files Browse the repository at this point in the history
  • Loading branch information
alcaeus committed May 10, 2024
1 parent 11bf470 commit a1aae66
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 6 deletions.
9 changes: 3 additions & 6 deletions lib/Doctrine/ODM/MongoDB/Persisters/DocumentPersister.php
Original file line number Diff line number Diff line change
Expand Up @@ -1512,14 +1512,11 @@ private function handleCollections(object $document, array $options): void
$collections[] = $coll;
}

if (! empty($collections)) {
$this->cp->update($document, $collections, $options);
if (empty($collections)) {
return;
}

// Take new snapshots from visited collections
foreach ($this->uow->getVisitedCollections($document) as $coll) {
$coll->takeSnapshot();
}
$this->cp->update($document, $collections, $options);
}

/**
Expand Down
6 changes: 6 additions & 0 deletions lib/Doctrine/ODM/MongoDB/UnitOfWork.php
Original file line number Diff line number Diff line change
Expand Up @@ -477,6 +477,12 @@ function (Session $session) use ($options): void {
$this->evm->dispatchEvent(Events::postFlush, new Event\PostFlushEventArgs($this->dm));

// Clear up
foreach ($this->visitedCollections as $collections) {
foreach ($collections as $coll) {
$coll->takeSnapshot();
}
}

$this->scheduledDocumentInsertions =
$this->scheduledDocumentUpserts =
$this->scheduledDocumentUpdates =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Documents\Address;
use Documents\ForumUser;
use Documents\FriendUser;
use Documents\Phonenumber;
use Documents\User;
use MongoDB\BSON\ObjectId;
use MongoDB\Client;
Expand Down Expand Up @@ -561,6 +562,36 @@ public function testTransientDeleteErrorWithEmbeddedDocument(): void
self::assertFalse($this->uow->isScheduledForDelete($user));
}

public function testTransientErrorPreservesCollectionChangesets(): void
{
// Create a dummy user so that we later have a user to remove
$fooUser = new User();
$fooUser->setUsername('foo');
$this->uow->persist($fooUser);
$this->uow->commit();

// Create a new user with a collection update
$alcaeus = new User();
// Set an identifier to force an upsert, which causes separate queries
// for collection updates
$alcaeus->setId(new ObjectId());
$alcaeus->setUsername('alcaeus');
$alcaeus->getPhonenumbers()->add(new Phonenumber('12345'));
$this->uow->persist($alcaeus);

// Remove fooUser and create a transient failpoint to force the deletion
// to fail. This exposes the issue with collections
$this->uow->remove($fooUser);
$this->createTransientFailPoint('delete');

$this->uow->commit();

$this->dm->clear();
$check = $this->dm->find(User::class, $alcaeus->getId());
self::assertNotNull($check);
self::assertCount(1, $check->getPhonenumbers());
}

/** Create a document manager with a single host to ensure failpoints target the correct server */
protected static function createTestDocumentManager(): DocumentManager
{
Expand Down

0 comments on commit a1aae66

Please sign in to comment.