Andrei Herford
Andrei Herford

Reputation: 18729

Doctrine: How to prevent transaction from becoming 'rollback only' through caught exception?

Deleting an entity fails because of an exception within a postRemove event handler. Even if the exception is caught the deletion fails because the transaction cannot be commit any more. How to solve this?

The complete story:

I need to keep track of some deleted entities in a Symfony 3.4 based web service using Doctrine.

To to this I have create an EventSubscriber which handles the postRemove event to check whether the deleted entity needs to be logged. In this case the entities UUID is stored in a DeleteLog table of th DB.

This works fine, but in rare cases persisting of the the DeleteLogEntry fails since there already exists a log entry for the given UUID which needs to be unique.

The source of this problem is some 3rd party code I cannot change my self. As a temporary solution tried to catch the UniqueConstraintViolationException. This does not solve the problem since now I get ConnectionException

Transaction commit failed because the transaction has been marked for rollback only.

Is it possible to solve this dilemma?

Of course I could check if a DeleteLogEntry with the given UUID exists before creating a new one. But since this problem occurs only in rare cases, the check would be negative most of the time. Of course running the check anyway is not a catastrophic performance impact but simply seems not be the best solution.

Is there any may to catch the exception and keep the transaction from being marked as rollback only?

Upvotes: 3

Views: 8669

Answers (1)

Andreas
Andreas

Reputation: 506

Nope, it's not possible to keep a transaction from being marked.

Doctine starts a nested transaction for postRemove and if it fails no other transactions should be committed. Marking a transaction for rollback only (and even closing entity manager) is expected behavior in such scenario, because there is no other way for Doctrine to ensure consistency as there are no support for real nested transactions.

If performance is not an issue, then checking for DeleteLogEntry is a good option.

Other possible workarounds:

  • store ID somewhere (Redis, Memcache, file, etc.) temporarily and update DeleteLogEntry later, after initial delete is committed
  • use a separate entity manager/connection to update DeleteLogEntry
  • remove Unique Constrain and use a background task to watch for duplicates

Upvotes: 4

Related Questions