2012-10-25 28 views
6

Tôi đang viết một hàm PHP lưu trữ/cập nhật bộ dữ liệu lớn vào một bảng và có thể gây ra bế tắc. Tôi đã thử điều tra làm thế nào để thử lại một giao dịch thất bại với Doctrine nhưng thật đáng buồn không thể tìm thấy bất kỳ thông tin trực tuyến. Tôi cuối cùng đã viết đoạn mã sauLàm cách nào để thử lại giao dịch sau khi bế tắc bằng 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" 
    ); 
} 

Câu hỏi của tôi: là có một cơ hội tiếp cận này sẽ chèn bản sao trong cơ sở dữ liệu? nếu có, làm cách nào tôi có thể buộc Doctrine quay lại giao dịch?

Trả lời

11

Một bế tắc trả về lỗi 1213 mà bạn nên xử lý trên các mặt hàng

Lưu ý rằng một bế tắc và chờ đợi khóa là những thứ khác nhau. Trong bế tắc, không có giao dịch "thất bại": cả hai đều có tội. Không có sự đảm bảo nào sẽ được khôi phục.

Bạn phải sử dụng rollback, mã kiểu của bạn sẽ chèn trùng lặp. ví dụ: bạn nên:

$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++; 

    } 

} 

Hy vọng trợ giúp này.

+0

Cảm ơn bạn :) Điều này ít nhất là cho tôi một ý tưởng về cách tiến hành. – Rorchackh

+18

Trên thực tế, điều này không còn đúng, như trường hợp ngoại lệ 'EntityManager' vào trạng thái đóng và nó sẽ ném một' ORMException 'nói rằng trình quản lý thực thể được đóng trên lần thử thứ hai. Phiên bản Doctrine 2.4. * – Mantas

+0

đây là cách đặt lại entitymanager khi đóng https://codedump.io/share/rjB45oiwtqwo/1/doctrine2-the-entitymanager-is-closed-how-to-reset-entity-manager-in- symfony2 –

-3

Nếu bạn không sử dụng ORM thì sử dụng nó sẽ tự động quản lý tình trạng bế tắc.

+0

Tôi không sử dụng ORM cho chèn. Bạn có thể giải thích cách bế tắc được xử lý tự động không? – Rorchackh

+1

Rorchackh, nếu cả hai bảng có khóa ngoài tham chiếu lẫn nhau, một trong số chúng không thể là NOT NULL. Nếu mọi thứ được ánh xạ chính xác, học thuyết sẽ luôn luôn lưu giữ bảng với ràng buộc nullable, duy trì ràng buộc bắt buộc với * và * chỉnh sửa giá trị đầu tiên để đặt giá trị FK chính xác. –

+3

Học thuyết * là * một ORM và * không * xử lý tình huống bế tắc tự động. Nó bị treo và ghi với lỗi cơ sở dữ liệu đầu tiên và không dễ dàng phục hồi. Vấn đề với việc sử dụng ORM là bạn đang thuê ngoài một cách hiệu quả vấn đề ở một nơi khác, và khi một nơi nào khác đi sai, nó có thể là một cơn đau lớn ở cổ để sửa chữa. Đó là mức giá bạn trả cho việc sử dụng ORM có sẵn. – StampyCode

1

Đây là cách tôi đối phó với thử lại giao dịch thất bại với Sf2.7 và học thuyết 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; 
    } 
} 
Các vấn đề liên quan