2012-10-25 11 views
6

Sto scrivendo una funzione PHP che memorizza/aggiorna grandi serie di dati in una tabella e può causare un deadlock. Ho provato a indagare su come riprovare una transazione fallita con Doctrine ma purtroppo non ho trovato alcuna informazione online. Alla fine ho scritto il seguente codiceCome riprovare la transazione dopo un deadlock utilizzando Doctrine?

$retry = 0; 
$done = false; 
while (!$done and $retry < 3) { 
    try { 

     $this->entityManager->flush(); 
     $done = true; 

    } catch (\Exception $e) { 
     sleep(1); 

     $retry++; 
    } 
} 

if ($retry == 3) { 
    throw new Exception(
     "[Exception: MySQL Deadlock] Too many people accessing the server at the same time. Try again in few minutes" 
    ); 
} 

La mia domanda: c'è una possibilità di questo approccio inserirà i duplicati nel database? In tal caso, come posso forzare Doctrine a ripristinare le transazioni?

risposta

11

Un deadlock restituisce l'errore 1213 che si dovrebbe elaborare sul lato client

Si noti che una situazione di stallo e di attesa di blocco sono cose diverse. In una situazione di stallo, non vi è alcuna transazione "fallita": sono entrambi colpevoli. Non vi è alcuna garanzia su quale verrà eseguito il rollback.

È necessario utilizzare rollback, il codice dello stile inserirà duplicato. per esempio dovresti:

$retry = 0; 

$done = false; 


$this->entityManager->getConnection()->beginTransaction(); // suspend auto-commit 

while (!$done and $retry < 3) { 

    try { 

     $this->entityManager->flush(); 

     $this->entityManager->getConnection()->commit(); // commit if succesfull 

     $done = true; 

    } catch (\Exception $e) { 

     $this->entityManager->getConnection()->rollback(); // transaction marked for rollback only 

     $retry++; 

    } 

} 

Spero che questo aiuto.

+0

Grazie :) Questo almeno mi dà un'idea di come procedere. – Rorchackh

+18

In realtà, questo non è più vero, poiché nel caso di Eccezione 'EntityManager' entra nello stato chiuso e genererà un' ORMException 'affermando che il gestore di entità è chiuso al secondo tentativo. Doctrine versione 2.4. * – Mantas

+0

questo è come reimpostare entitymanager quando chiuso https://codedump.io/share/rjB45oiwtqwo/1/doctrine2-the-entitymanager-is-closed-how-to-reset-entity-manager-in- Symfony2 –

-3

Se non si utilizza ORM, utilizzare la situazione di deadlock gestita automaticamente.

+0

Non sto usando ORM per inserimenti. Puoi spiegare come vengono gestiti automaticamente i deadlock? – Rorchackh

+1

Rorchackh, se entrambe le tabelle hanno chiavi esterne che si riferiscono l'una all'altra, una di queste non può essere NOT NULL. Se tutto è mappato correttamente, la dottrina manterrà sempre la tabella con il vincolo nullable, persisterà quella con il vincolo obbligatorio * e * modifica il primo per impostare il valore FK corretto. –

+3

Doctrine * è * un ORM e * non * gestisce automaticamente la situazione di deadlock. Si blocca e brucia con il primo errore del database e non è facilmente recuperabile. Il problema con l'utilizzo di un ORM è che si sta effettivamente esternalizzando il problema da qualche altra parte, e quando questo va a finire altrove, può essere un enorme dolore al collo da risolvere. Questo è il prezzo che si paga usando un ORM standard. – StampyCode

1

Questo è come mi occupo di riprovare le transazioni fallite con Sf2.7 e dottrina 2.4.7:

use Doctrine\Bundle\DoctrineBundle\Registry; 
use Doctrine\ORM\EntityManager; 

class Foo 
{ 
    /** 
    * @var Registry 
    */ 
    protected $doctrine; 

    public function __construct(Registry $registry) 
    { 
     $this->doctrine = $registry; 
    } 

    protected function doSomething($entity, $attempt) 
    { 
     $em = $this->getEntityManager(); 
     $conn = $em->getConnection(); 
     try{ 
      $conn->beginTransaction(); 
      $entity->setBar("baz"); 
      $em->flush(); 
      $conn->commit(); 
     } catch(\PDOException $e){ 
      $conn->rollBack(); 
      $attempt++; 
      if($attempt <= 3){ 
       $this->doSomething($repayment, $attempt); 
      } 
     } 
    } 

    /** 
    * @return EntityManager 
    */ 
    protected function getEntityManager() 
    { 
     /** @var EntityManager $em */ 
     $em = $this->doctrine->getManager(); 
     if(!$em->isOpen()){ 
      $this->doctrine->resetManager(); 
      $em = $this->doctrine->getManager(); 
     } 

     return $em; 
    } 
} 
Problemi correlati