[DC-984] Pessimistic locking locks entire table rather than record Created: 16/Mar/11  Updated: 17/Apr/14

Status: Open
Project: Doctrine 1
Component/s: Transactions
Affects Version/s: None
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Barry O'Donovan Assignee: Jonathan H. Wage
Resolution: Unresolved Votes: 1
Labels: None
Environment:

Standard LAMP stack using current SVN from http://svn.doctrine-project.org/branches/1.2/lib/Doctrine/Locking/Manager


Attachments: File Doctrine_Locking_Manager_Pessimistic.diff    

 Description   

When using pessimistic locking as described in:

http://www.doctrine-project.org/projects/orm/1.2/docs/manual/component-overview:locking-manager:examples/zh

the locking manager locks the entire table rather than the specific object.

This should be clear from the attached patch which corrects the issue (assuming I have correctly interpreted the intention of pessimistic locking!).

The current behavior will have worked as expected for users but it will have locked far more than was intended and may thus have affected performance.

NB: I can confirm this works for non-composite keys but please review and test for composite keys as I have no such tables to hand.



 Comments   
Comment by Barry O'Donovan [ 18/Oct/11 ]

Folks - just wondering if anyone had a chance to look at this as, while not critical, it does appear to be a genuinely major performance issue.

Comment by Grégoire Paris [ 13/Dec/12 ]

Duplicate with more information : http://www.doctrine-project.org/jira/browse/DC-185





[DC-845] One of our Foreign Keys is not being inserted/passed Created: 27/Aug/10  Updated: 27/Aug/10

Status: Open
Project: Doctrine 1
Component/s: Record, Relations, Transactions
Affects Version/s: 1.2.0, 1.2.1, 1.2.2, 1.2.3
Fix Version/s: None

Type: Bug Priority: Major
Reporter: charles wisniewski Assignee: Jonathan H. Wage
Resolution: Unresolved Votes: 0
Labels: None
Environment:

Linux ubuntu 2.6.31-22-generic #63-Ubuntu SMP Thu Aug 19 00:23:50 UTC 2010 x86_64 GNU/Linux
PHP 5.2.10-2ubuntu6.4 with Suhosin-Patch 0.9.7 (cli) (built: Jan 6 2010 22:56:44)
mysql Ver 14.14 Distrib 5.1.37, for debian-linux-gnu (x86_64) using EditLine wrapper



 Description   

We are working on a symfony/doctrine project and have come to a near halt on development.

We feel that now, it may be a bug/feature in doctrine, regarding foreign keys

Part of our model includes a join table that references three different table. Below is a diagram of what the model looks like, and the relevant portion of our schema.yml is at the bottom.

Image of our schema: http://imgur.com/dfFYI.png

We have a form that contains a set of embedded forms that attempt to create a new Person entry and add rows to the join table, adding items to the PersonName table as needed.

The form attempts to do this by creating and saving PersonName objects with NameType parameters, but are running into the problem of Doctrine not including that column when trying to do an insert into the join table.

Part of the problem seems to be caused by the Doctrine_Connection_UnitOfWork::saveAssociations method:

foreach ($v->getInsertDiff() as $r)

{ $assocRecord = $assocTable->create(); $assocRecord->set($assocTable->getFieldName($rel->getForeign()), $r); $assocRecord->set($assocTable->getFieldName($rel->getLocal()), $record); $this->saveGraph($assocRecord); }

Are we correct in understanding that this means that Doctrine 1.2 does not support tables with multiple foreign keys in this scenario?

Here is the relevant portion of schema.yml:

agPerson:
columns:
id:
primary: true
type: integer(5)
autoincrement: true
relations:
agPersonName:
class: agPersonName
refClass: agPersonMjAgPersonName
local: person_id
foreign: person_name_id
agPersonNameType:
class: agPersonNameType
refClass: agPersonMjAgPersonName
local: person_id
foreign: person_name_type_id
agPersonNameType:
columns:
id:
primary: true
type: integer(2)
autoincrement: true
person_name_type:
unique: true
type: string(30)
notnull: true
app_display:
default: 1
type: boolean
notnull: true
relations:
agPerson:
class: agPerson
refClass: agPersonMjAgPersonName
local: person_name_type_id
foreign: person_id
agPersonName:
class: agPersonName
refClass: agPersonMjAgPersonName
local: person_name_type_id
foreign: person_name_id
agPersonName:
columns:
id:
primary: true
type: integer(5)
autoincrement: true
person_name:
unique: true
type: string(64)
notnull: true
relations:
agPerson:
class: agPerson
refClass: agPersonMjAgPersonName
local: person_name_id
foreign: person_id
agPersonNameType:
class: agPersonNameType
refClass: agPersonMjAgPersonName
local: person_name_id
foreign: person_name_type_id
agPersonMjAgPersonName:
columns:
id:
primary: true
type: integer(5)
autoincrement: true
person_id:
type: integer(5)
notnull: true
person_name_id:
type: integer(5)
notnull: true
person_name_type_id:
type: integer(2)
notnull: true
is_primary:
type: boolean
notnull: true
indexes:
UX_ag_person_mj_ag_person_name:
fields: [person_name_id, person_name_type_id, person_id]
type: unique
relations:
agPerson:
local: person_id
foreign: id
agPersonName:
local: person_name_id
foreign: id
agPersonNameType:
local: person_name_type_id
foreign: id
actAs:
Timestampable:

As a caveat: we've noticed that sfdoctrineguard group table, has such a relationship:

mysql> describe sf_guard_user_group;
---------------------------------------+

Field Type Null Key Default Extra

---------------------------------------+

user_id int(11) NO PRI 0  
group_id int(11) NO PRI 0  
created_at datetime NO   NULL  
updated_at datetime NO   NULL  

---------------------------------------+

i.e. IT has two foreign keys taken into account






[DC-526] Savepoint-rollback ignored in nested transaction context. Created: 26/Feb/10  Updated: 06/Aug/10

Status: Open
Project: Doctrine 1
Component/s: Transactions
Affects Version/s: 1.2.1
Fix Version/s: None

Type: Bug Priority: Major
Reporter: Michael Nielsen Assignee: Jonathan H. Wage
Resolution: Unresolved Votes: 0
Labels: None


 Description   

The example from the documentation on savepoints:

 
try {
    $conn->beginTransaction();
    // do some operations here 

    // creates a new savepoint called mysavepoint
    $conn->beginTransaction('mysavepoint');
    try {
        // do some operations here

        $conn->commit('mysavepoint');
    } catch(Exception $e) {
        $conn->rollback('mysavepoint'); # (a)
    }
    $conn->commit();
} catch(Exception $e) {
    $conn->rollback();
}

What seems to be a bug to me: (a) doesn't actually do a savepoint-rollback if reached, but rather returns false as soon as the nesting level is seen to be > 1.



 Comments   
Comment by Jonathan H. Wage [ 15/Mar/10 ]

What rdbms are you using? Does it support this feature?

Comment by Michael Nielsen [ 15/Mar/10 ]

I use MySQL 5.0.51/InnoDB. It is supported and the doctrine-example does indeed work, if I hack the rollback-method a bit.

316	    public function rollback($savepoint = null)
317	    {
318	        if ($this->_nestingLevel == 0) {
319	            throw new Doctrine_Transaction_Exception("Rollback failed. There is no active transaction.");
320	        }
321	       
322	        $this->conn->connect();
323	
324	        if ($this->_internalNestingLevel >= 1 && $this->_nestingLevel > 1) {
325	            $this->_internalNestingLevel--;
326	            $this->_nestingLevel--;
327	            return false;
328	        } else if ($this->_nestingLevel > 1) {
329	            $this->_nestingLevel--;
330	            return false;
331	        }
332	
333	        $listener = $this->conn->getAttribute(Doctrine_Core::ATTR_LISTENER);
334	
335	        if ( ! is_null($savepoint)) {
[...]

The problem is that in a nested context, the rollback-method never does anything but a nesting-level decrement and a false-return, regardless of savepoints. So in the doctrine-example, (a) just ends up at the false-return at line 327 or 330, rather than reaching the savepoint-rollback-block starting at line 335. And so the command is never sent to MySQL.

Comment by Jonathan H. Wage [ 15/Mar/10 ]

Do you have a patch/diff that fixes the issue for you?

Comment by Michael Nielsen [ 15/Mar/10 ]

I just skipped the nestingLevel-section in case of a savepoint. I have not spent any time reviewing how (un)healthy that might be for the level counters and the method in general, but it fixes my current application of it.

@@ -321,14 +321,16 @@
         
         $this->conn->connect();
 
-        if ($this->_internalNestingLevel >= 1 && $this->_nestingLevel > 1) {
-            $this->_internalNestingLevel--;
-            $this->_nestingLevel--;
-            return false;
-        } else if ($this->_nestingLevel > 1) {
-            $this->_nestingLevel--;
-            return false;
-        }
+	if (is_null($savepoint)) {
+		if ($this->_internalNestingLevel >= 1 && $this->_nestingLevel > 1) {
+			$this->_internalNestingLevel--;
+			$this->_nestingLevel--;
+			return false;
+		} else if ($this->_nestingLevel > 1) {
+			$this->_nestingLevel--;
+			return false;
+		}
+	}
 
         $listener = $this->conn->getAttribute(Doctrine_Core::ATTR_LISTENER);
Comment by Jonathan H. Wage [ 08/Jun/10 ]

The change unfortunately breaks the tests.

Comment by Hendri Kurniawan [ 06/Aug/10 ]

Hi Jonathan,

I can also replicate and confirm the issue. I found a workaround that will fix this issue.
However in saying that, I'm not very familiar with Doctrine base code and may have broken something else.
I'd be very grateful if you can have a look at the following fix.

The patch is for Doctrine/Transaction.php

@@ -214,9 +214,9 @@
                 }
                 $listener->postTransactionBegin($event);
             }
-        }
 
         $level = ++$this->_nestingLevel;
+        }
 
         return $level;
     }
@@ -244,7 +244,7 @@
         $listener = $this->conn->getAttribute(Doctrine_Core::ATTR_LISTENER);
 
         if ( ! is_null($savepoint)) {
-            $this->_nestingLevel -= $this->removeSavePoints($savepoint);
+            $this->removeSavePoints($savepoint);
 
             $event = new Doctrine_Event($this, Doctrine_Event::SAVEPOINT_COMMIT);
 
@@ -333,7 +333,7 @@
         $listener = $this->conn->getAttribute(Doctrine_Core::ATTR_LISTENER);
 
         if ( ! is_null($savepoint)) {
-            $this->_nestingLevel -= $this->removeSavePoints($savepoint);
+            $this->removeSavePoints($savepoint);
 
             $event = new Doctrine_Event($this, Doctrine_Event::SAVEPOINT_ROLLBACK);
 
@@ -507,7 +507,10 @@
      */
     public function beginInternalTransaction($savepoint = null)
     {
+        if (is_null($savepoint)) {
         $this->_internalNestingLevel++;
+        }
+
         return $this->beginTransaction($savepoint);
     }

Thanks





Generated at Mon Nov 24 16:42:41 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.