DDC-3758: Transactional parallelism #4609

Open
opened 2026-01-22 14:45:53 +01:00 by admin · 7 comments
Owner

Originally created by @doctrinebot on GitHub (Jun 7, 2015).

Originally assigned to: @beberlei on GitHub.

Jira issue originally created by user flespi:

Assuming that I am in an application that supports concurrency, as most Web applications, this is the problem:

The transactions do not currently have an execution context.
To work with a transaction I must do the following:

$em-> getConnection () -> beginTransaction (); // Auto-commit suspend
try {
    // ... Do some work
    $ user = new User;
    $ user-> setName ('George');
    $ em-> persist ($ user);
    $ em-> flush ();
    $ em-> getConnection () -> commit ();
} catch (Exception $ e) {
    $em-> getConnection () -> rollback ();
    throw $e;
}

But what if another (parallel) request executes "flush" before it?

I think that the problem is because the transaction lives in the context of the EntityManager. Because of that any need to "flush" apply to that object.

The best thing would be to do something like:

$transaction = $em-> getConnection () -> CreateTransaction (); // Auto-commit suspend
try {
    // ... Do some work
    $user = new User;
    $user-> setName ('George');
    $transaction-> persist ($ user);
    $transaction-> flush ();
    $transaction-> commit ();
} catch (Exception $ e) {
    $transaction-> rollback ();
    throw $ e;
}
Originally created by @doctrinebot on GitHub (Jun 7, 2015). Originally assigned to: @beberlei on GitHub. Jira issue originally created by user flespi: Assuming that I am in an application that supports concurrency, as most Web applications, this is the problem: The transactions do not currently have an execution context. To work with a transaction I must do the following: ``` $em-> getConnection () -> beginTransaction (); // Auto-commit suspend try {     // ... Do some work     $ user = new User;     $ user-> setName ('George');     $ em-> persist ($ user);     $ em-> flush ();     $ em-> getConnection () -> commit (); } catch (Exception $ e) {     $em-> getConnection () -> rollback ();     throw $e; } ``` But what if another (parallel) request executes "flush" before it? I think that the problem is because the transaction lives in the context of the EntityManager. Because of that any need to "flush" apply to that object. The best thing would be to do something like: ``` $transaction = $em-> getConnection () -> CreateTransaction (); // Auto-commit suspend try {     // ... Do some work     $user = new User;     $user-> setName ('George');     $transaction-> persist ($ user);     $transaction-> flush ();     $transaction-> commit (); } catch (Exception $ e) {     $transaction-> rollback ();     throw $ e; } ```
admin added the Improvement label 2026-01-22 14:45:53 +01:00
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by @ocramius:

[~flespi] I don't understand the question. Are you aware that EntityManager#flush() implicitly starts a new transaction?

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by @ocramius: [~flespi] I don't understand the question. Are you aware that `EntityManager#flush()` implicitly starts a new transaction?
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by flespi:

Marco, all the changes I do in the EntityManager until call flush or commit, are collected by the EntityManager. This is dangerous in a web application, where the requests can imply a race condition.

The changes are not collected in any transaction abstracrion and the state of the EntityManager is what realy modify.

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by flespi: Marco, all the changes I do in the EntityManager until call flush or commit, are collected by the EntityManager. This is dangerous in a web application, where the requests can imply a race condition. The changes are not collected in any transaction abstracrion and the state of the EntityManager is what realy modify.
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by @ocramius:

There's your confusion :-)

In Doctrine, the EntityManager IS the abstraction of a transaction.

This may be changed in future, when we'll maybe have different sessions/transactions spawned from the EntityManager, but right now the EntityManager is your transaction.

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by @ocramius: There's your confusion :-) In Doctrine, the `EntityManager` _IS_ the abstraction of a transaction. This may be changed in future, when we'll maybe have different sessions/transactions spawned from the `EntityManager`, but right now the `EntityManager` is your transaction.
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by flespi:

Oh, thanks for the answer.

Last think... I'm using Symfony, do you know if the getManager method return allways the same object? Because in that case I don't know how to resolver that problem.

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by flespi: Oh, thanks for the answer. Last think... I'm using Symfony, do you know if the getManager method return allways the same object? Because in that case I don't know how to resolver that problem.
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by @ocramius:

Yes, in symfony, getManager will give you the same instance over multiple calls.

A good way to solve the problem is to force a transaction around your controller dispatch logic - that creates a "safe bubble" where you can operate quite safely.

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by @ocramius: Yes, in symfony, `getManager` will give you the same instance over multiple calls. A good way to solve the problem is to force a transaction around your controller dispatch logic - that creates a "safe bubble" where you can operate quite safely.
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by flespi:

That's the race condition I'm talking about.
If I have 2 actions and both call beginTransaction, in a race condition can be interpreted as nested transactions instead of paralell transactions.

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by flespi: That's the race condition I'm talking about. If I have 2 actions and both call beginTransaction, in a race condition can be interpreted as nested transactions instead of paralell transactions.
Author
Owner

@doctrinebot commented on GitHub (Jun 7, 2015):

Comment created by @ocramius:

{quote}If I have 2 actions and both call beginTransaction, in a race condition can be interpreted as nested transactions instead of paralell transactions.{quote}
They happen on different processes anyway, so the nesting level is not relevant.

@doctrinebot commented on GitHub (Jun 7, 2015): Comment created by @ocramius: {quote}If I have 2 actions and both call beginTransaction, in a race condition can be interpreted as nested transactions instead of paralell transactions.{quote} They happen on different processes anyway, so the nesting level is not relevant.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#4609