Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Restore document proxy state to uninitialized on load exception #2521

Merged
merged 1 commit into from Apr 13, 2023

Conversation

notrix
Copy link
Contributor

@notrix notrix commented Apr 3, 2023

Q A
Type bug
BC Break no
Fixed issues

Summary

This PR should fix issue when exception is thrown during proxy object load. If application catches exceptions and flushes at the end of request, unit of work finds changes for fields that have default values. I have seen network glitches of Amazon KMS service on load and successful flush at the end. This broke production environment badly.
Proxy is marked as not initialized if id is not found in database. So I think same should be if exception happens on load event.

@notrix notrix force-pushed the fix-proxy-state-on-init-exception branch 2 times, most recently from a7b30d5 to bb532ef Compare April 3, 2023 09:26
@notrix
Copy link
Contributor Author

notrix commented Apr 6, 2023

@malarzm I would like some comments in here: do you agree that it is a bug to leave ghost object initialized if there was exception loading data?
Is the branch doctrine:2.6.x correct to point MR to?
CI issues with PHPStan and Psalm? Are they relevant to my MR and should I fix it (it seems that errors are not related to my MR)

@malarzm
Copy link
Member

malarzm commented Apr 6, 2023

I would like some comments in here: do you agree that it is a bug to leave ghost object initialized if there was exception loading data?

I'm not sure yet :) Sounds plausible but I haven't given it a deeper thought yet

Is the branch doctrine:2.6.x correct to point MR to?

Given your description it's better to classify as a bug, in which case 2.5.x is a correct target

CI issues with PHPStan and Psalm? Are they relevant to my MR and should I fix it (it seems that errors are not related to my MR)

Please rebase atop 2.5.x - there checks are green :)

@notrix notrix changed the base branch from 2.6.x to 2.5.x April 6, 2023 10:45
@notrix notrix changed the base branch from 2.5.x to 2.6.x April 6, 2023 10:47
@notrix notrix force-pushed the fix-proxy-state-on-init-exception branch from bb532ef to cdd5c90 Compare April 6, 2023 10:55
@notrix notrix changed the base branch from 2.6.x to 2.5.x April 6, 2023 10:55
@notrix notrix force-pushed the fix-proxy-state-on-init-exception branch 3 times, most recently from cc182e9 to dda9e4f Compare April 6, 2023 11:36
@malarzm malarzm added the Bug label Apr 7, 2023
@malarzm malarzm added this to the 2.5.2 milestone Apr 7, 2023
Copy link
Member

@malarzm malarzm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm A-OK with changes in the StaticProxyFactory itself 👍


$collection->expects($this->once())
->method('findOne')
->willThrowException(LockException::lockFailed(null));
Copy link
Member

@malarzm malarzm Apr 12, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a bit torn so here goes nothing: how about instead of mocking 3 levels deep we mock DocumentPersister, have it thrown an exception on load? This would require manipulating UnitOfWork::$documentPersisters with reflection though, but the test should be easier to maintain with real database connection. For your consideration :)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At first I also wanted to mock DocumentPersister::load, but DocumentPersister is a final class. So my best shot was to mock Collection::findOne

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed then!

$metadata = $this->dm->getClassMetadata(PaymentProvider::class);

$proxy = $this->proxyFactory->getProxy($metadata, '123');
$uow->registerManaged($proxy, '123', []);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's better to call DocumentManager::getReference

try {
$proxy->initializeProxy();
} catch (LockException $exception) {
$this->assertInstanceOf(LockException::class, $exception);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please use self::assert* wherever possible, most of code was migrated to that

Comment on lines 62 to 64
$uow->computeChangeSets();

$this->assertFalse($uow->isScheduledForUpdate($proxy), 'Proxy should not be scheduled for update');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my opinion this is not needed. The test should care whether proxy is still uninitialized after the exception, not how change set computation works with uninitialized proxies (there are other tests for that).

$uow->computeChangeSets();

$this->assertFalse($uow->isScheduledForUpdate($proxy), 'Proxy should not be scheduled for update');
$this->assertFalse($proxy->isProxyInitialized(), 'Proxy should not be initialized');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If possible, the test should also add an event listener for documentNotFound and assert the listener is never called.

use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM;

/** @ODM\Document */
class PaymentProvider
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you please use any existing document instead of adding new one?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main issue for me was that my document has default values and those default value were flushed to database. But since we do not assert change set - I have removed new document

{
}

protected function createMockedDocumentManager(): DocumentManager
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
protected function createMockedDocumentManager(): DocumentManager
private function createMockedDocumentManager(): DocumentManager

No need for protected visibility unless this was supposed to be createTestDocumentManager originally?

@notrix notrix force-pushed the fix-proxy-state-on-init-exception branch from dda9e4f to 6b95e60 Compare April 13, 2023 13:27
@notrix notrix force-pushed the fix-proxy-state-on-init-exception branch from 6b95e60 to 9bbe84a Compare April 13, 2023 13:40
Copy link
Member

@malarzm malarzm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you very much for changes!

@malarzm malarzm merged commit bce7e33 into doctrine:2.5.x Apr 13, 2023
18 checks passed
@malarzm
Copy link
Member

malarzm commented Apr 13, 2023

And enjoy 2.5.2 ;)

@notrix notrix deleted the fix-proxy-state-on-init-exception branch April 21, 2023 13:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants