Chỉ số lại giá gây ra bế tắc DB trong quá trình thanh toán


47

Tôi đang gặp vấn đề khi tôi tin rằng quy trình lập chỉ mục lại Giá sản phẩm đang gây ra ngoại lệ bế tắc trong quy trình thanh toán.

Tôi đã bắt gặp ngoại lệ này trong quá trình thanh toán:

Ngoại lệ chuyển đổi đơn hàng: SQLSTATE [40001]: Lỗi nối tiếp: 1213 Bế tắc được tìm thấy khi cố gắng khóa; thử khởi động lại giao dịch

Thật không may, tôi không có dấu vết ngăn xếp đầy đủ vì nơi bắt gặp ngoại lệ, nhưng kiểm tra trạng thái INNODB tôi có thể theo dõi bế tắc:

SELECT `si`.*, `p`.`type_id` FROM `cataloginventory_stock_item` AS `si` 
INNER JOIN `catalog_product_entity` AS `p` ON p.entity_id=si.product_id     
WHERE (stock_id=1) 
AND (product_id IN(47447, 56678)) FOR UPDATE

*** (1) WAITING FOR THIS LOCK TO BE GRANTED:

RECORD LOCKS space id 0 page no 329624 n bits 352 index 
`PRIMARY` of table `xxxx`.`catalog_product_entity` 

Khóa bảng yêu cầu SQL cuối cùng được tạo từ Mage_CatalogInventory_Model_Stock::registerProductsSale()khi nó đang cố lấy số lượng hàng tồn kho hiện tại để giảm giá trị.

Vào thời điểm bế tắc xảy ra, quá trình lập chỉ mục lại Giá sản phẩm đang chạy và tôi cho rằng nó có khóa đọc trên catalog_product_entity tableđó gây ra bế tắc. Nếu tôi hiểu chính xác về bế tắc thì bất kỳ khóa đọc nào cũng sẽ gây ra bế tắc, nhưng chỉ số lại giá sản phẩm giữ khóa trong một thời gian hợp lý vì trang web có ~ 50.000 sản phẩm.

Thật không may, tại thời điểm này trong dòng mã thanh toán, thẻ tín dụng của khách hàng đã bị tính phí (thông qua mô-đun thanh toán tùy chỉnh) và việc tạo đối tượng đặt hàng tương ứng không thành công.

Câu hỏi của tôi là:

  • Là mô-đun thanh toán tùy chỉnh logic bị lỗi? tức là có một luồng được chấp nhận để đảm bảo rằng Magento có thể chuyển đổi báo giá thành ngoại lệ đơn hàng miễn phí trước khi thực hiện tính phí cho phương thức thanh toán (thẻ tín dụng) không?

Chỉnh sửa: Dường như logic mô-đun thanh toán thực sự bị lỗi vì lệnh gọi $ Paymentmethod-> ủy quyền () sẽ xảy ra sau khi xảy ra bế tắc này, không phải trước đó (theo câu trả lời của Ivan bên dưới). Tuy nhiên, giao dịch vẫn sẽ bị chặn bởi bế tắc (mặc dù không có phí sai cho thẻ tín dụng).

  • Gọi hàm này $stockInfo = $this->_getResource()->getProductsStock($this, array_keys($qtys), true);trong Mage_CatalogInventory_Model_Stock::registerProductsSale()làm cho nó một khóa đọc, làm thế nào nguy hiểm nó sẽ là để làm cho nó một tổ chức phi khóa đọc?

  • Khi tìm kiếm câu trả lời trên web, một vài nơi đề nghị không chạy lại chỉ mục đầy đủ trong khi trang web đang nóng; hầu như không phải là một giải pháp tốt; là vấn đề lập chỉ mục gây ra bế tắc bảng và tranh chấp khóa là một vấn đề đã biết trong Magento, có cách giải quyết nào không?

Chỉnh sửa: Có vẻ như câu hỏi còn lại ở đây là câu hỏi từ câu hỏi thứ ba; lập chỉ mục lại gây ra bế tắc bảng. Tìm kiếm cách giải quyết cho việc này.

Chỉnh sửa: Khái niệm bế tắc không phải là vấn đề của chính họ, mà là phản ứng với chúng nên là trọng tâm, rất có ý nghĩa. Điều tra thêm để tìm một điểm trong mã để bắt ngoại lệ bế tắc và phát hành lại yêu cầu. Làm điều này ở cấp bộ điều hợp Zend Framework DB là một cách tiếp cận, nhưng tôi cũng đang tìm cách để làm điều này trong mã Magento để dễ bảo trì.

Có một bản vá thú vị trong chủ đề này: http : //www.magentoc Commerce.com/boards/viewthread/31666/P0/ dường như để giải quyết một điều kiện bế tắc liên quan (nhưng không phải là điều này cụ thể).

Chỉnh sửa: Rõ ràng bế tắc đã được giải quyết ở một mức độ trong CE 1.8 Alpha. Vẫn đang tìm cách giải quyết cho đến khi phiên bản này không còn Alpha


Gần đây chúng tôi đã chiến đấu với một vấn đề tương tự, bạn đang sử dụng tiện ích thanh toán nào?
Peter O'Callaghan

Đó là một tiện ích mở rộng được mã hóa tùy chỉnh
Roscius

1
@kalenjordan Các ứng dụng lập chỉ mục trong 1.13 và sơ đồ thử lại như philwinkle dưới đây đã giảm nhẹ vấn đề cho tôi.
Roscius

1
@Roscius khoảng bao nhiêu họ đã giảm nhẹ nó? Tôi đang thấy lỗi DB nào đó (thời gian chờ kết nối, thời gian chờ khóa, khóa chết) ảnh hưởng đến khoảng 0,2% đơn hàng của tôi. Rất hiếm nhưng tôi thực sự muốn giải quyết nó hoàn toàn.
kalenjordan

Câu trả lời:


16

Có khả năng khá lớn là phương thức thanh toán của bạn đang xử lý thanh toán sai.

Quá trình lưu đơn hàng Magento khá đơn giản:

  • Chuẩn bị tất cả dữ liệu nên được chuyển từ mục báo giá sang mục đặt hàng, bao gồm giá cả và thông tin sản phẩm, sau đó nó không gọi ra việc thu hồi giá.
  • Gọi trước khi đặt hàng gửi sự kiện checkout_type_onepage_save_ordersales_model_service_quote_submit_before
    • Mage_CatalogInventory_Model_Stock::registerProductsSale() được viện dẫn tại sự kiện này
  • Bắt đầu giao dịch DB
  • Gọi $order->place()phương thức xử lý thanh toán bằng cách gọi $paymentMethod->authorize(), $paymentMethod->capture()hoặc $paymentMethod->initialize()phụ thuộc vào logic của nó.
  • Gọi phương thức $ order-> save () lưu thứ tự đã xử lý vào các bảng DB sales_flat_order_*.
  • Cam kết giao dịch DB (Ở bước này DB phát hành khóa trên bảng kiểm kê)

Vì vậy, như bạn thấy, không thể, phương thức thanh toán sẽ tính tiền trước khi khóa hàng tồn kho và đọc giá sản phẩm hoặc thông tin sản phẩm.

Trong trường hợp chỉ có thể thực hiện nếu phương thức thanh toán được thực hiện theo cách như vậy, thì nó sẽ tự thực hiện tải sản phẩm với giá, sau khi lệnh gọi API cho hoạt động tính phí được thực hiện.

Hy vọng điều này sẽ giúp bạn trong việc gỡ lỗi vấn đề của bạn.

Đối với reindexing, nó sẽ an toàn, nếu bạn không gặp vấn đề này với phương thức thanh toán. Vì hoạt động đọc phụ thuộc vào khóa được thực hiện trước khi tiền được tính phí.


1
Cảm ơn, có vẻ như logic mô-đun thanh toán tùy chỉnh bị tắt một chút. Tuy nhiên, có vẻ như một quy trình lập chỉ mục sẽ chặn thanh toán bằng cách gây ra ngoại lệ registerProductsSale()(hiểu rằng với các bản sửa lỗi cho mô-đun thanh toán tùy chỉnh sẽ loại bỏ vấn đề tính phí thẻ của khách hàng).
Roscius

8

Vì đây là tiện ích mở rộng tùy chỉnh, chúng tôi có thể tìm một cách giải quyết tùy chỉnh (đọc: hack) để thử lại lưu mà không cần chỉnh sửa các tệp cốt lõi.

Tôi đã giải quyết tất cả các tai ương bế tắc của mình bằng hai phương thức sau được thêm vào một lớp trợ giúp. Thay vì gọi $product->save()bây giờ tôi gọi Mage::helper('mymodule')->saferSave($product):

/**
 * Save with a queued retry upon deadlock, set isolation level
 * @param  stdClass $obj object must have a pre-defined save() method
 * @return n/a      
 */
public function saferSave($obj)
{

    // Deadlock Workaround
    $adapter = Mage::getModel('core/resource')->getConnection('core_write');
    // Commit any existing transactions (use with caution!)
    if ($adapter->getTransactionLevel > 0) {
        $adapter->commit();
    }
    $adapter->query('SET TRANSACTION ISOLATION LEVEL SERIALIZABLE');

    //begin a retry loop that will recycle should a deadlock pop up
    $tries = 0;
        do {
            $retry = false;
            try {
                $obj->save();
            } catch (Exception $e) {
                if ($tries < 10 and $e->getMessage()=='SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction') {
                    $retry = true;
                } else {
                    //we tried at least 10 times, go ahead and throw exception
                    throw new Zend_Db_Statement_Exception($e->getMessage());
                }
                sleep($this->getDelay());
                $tries++;
            }
        } while ($retry);

    //free resources
    unset($adapter);
    return;
}

public function getDelay($tries){
    return (int) pow(2, $tries);
}

Điều này thực hiện hai điều khác biệt - nó xếp hàng thử lại khi gặp bế tắc và nó đặt thời gian chờ tăng theo cấp số nhân cho lần thử lại đó. Nó cũng đặt mức cô lập giao dịch. Có rất nhiều thông tin về SO và trên DBA.SE để biết thêm thông tin về các mức cô lập giao dịch của MySQL.

FWIW, tôi đã không gặp bế tắc kể từ đó.


1
@Mage :: getModel ('core / resource') @ sẽ tạo kết nối mới. Tôi không hiểu làm thế nào nó có thể thay đổi mức cô lập giao dịch hiện tại.
giftnuss

@giftnuss đủ công bằng. Nên là singleton cho chắc chắn. Hãy đóng góp điều này trên mô-đun bế tắc của tôi trên github
philwinkle

@philwinkle cảm ơn vì người đàn ông này. Tôi đang cố gắng tìm hiểu xem liệu bản nâng cấp EE 1.13 sẽ giải quyết được tai ương của tôi hay liệu tôi cũng nên xem xét vấn đề này. Tôi biết rằng 1.13 không lập chỉ mục không đồng bộ là điều tuyệt vời nhưng nếu có cùng các truy vấn cơ bản, tôi có một thời gian khó hiểu làm thế nào một mình async sẽ ngăn chặn các bế tắc xảy ra.
kalenjordan

1
@kalenjordan, đây là sự kết hợp giữa async và varien db được cập nhật thay đổi trong 1.8 / 1.13 làm giảm khả năng bế tắc.
philwinkle

Tôi nghĩ rằng bạn đã quên chuyển qua $trieschức năng nàysleep($this->getDelay());
Tahir Yasin

3

Trên các diễn đàn Magento, họ nói về việc chỉnh sửa tệp thư viện Zend: lib / Zend / Db / Statement / Pdo.php

Hàm _execute ban đầu:

public function _execute(array $params = null)
    {
        // begin changes
        $tries = 0;
        do {
            $retry = false;
            try {
                if ($params !== null) {
                    return $this->_stmt->execute($params);
                } else {
                    return $this->_stmt->execute();
                }
            } catch (PDOException $e) {
                #require_once 'Zend/Db/Statement/Exception.php';
                if ($tries < 10 and $e->getMessage()=='SQLSTATE[40001]: Serialization failure: 1213 Deadlock found when trying to get lock; try restarting transaction') {
                    $retry = true;
                } else {
                    throw new Zend_Db_Statement_Exception($e->getMessage());
                }
                $tries++;
            }
        } while ($retry);
        // end changes
    }

Sau khi sửa đổi:

public function _execute(array $params = null)
    {
        $tries = 0;
        do {
            $retry = false;
            try {
                $this->clear_result();
                $result = $this->getConnection()->query($sql);
                $this->clear_result();
            }
            catch (Exception $e) {
                if ($tries < 10 and $e->getMessage()=='SQLSTATE[HY000]: General error: 1205 Lock wait timeout exceeded; try restarting transaction') {
                    $retry = true;
                } else {
                    throw $e;
                }
                $tries++;
            }
        } while ($retry);

        return $result;
    }

Như bạn có thể thấy điều duy nhất đã được thay đổi là $ cố gắng đã được di chuyển ra ngoài vòng lặp.

Như mọi khi, bạn nên thử điều này trên môi trường phát triển / thử nghiệm và không triển khai ngay bản sửa lỗi này trên môi trường sản xuất.


2
Tôi lo lắng về việc chỉnh sửa các tệp khung cơ bản, có vẻ như việc thử lại sẽ xảy ra ở cấp mã Magento.
Roscius

Chúng tôi đã thử bản sửa lỗi được đề xuất và nó thực sự đã ngăn chặn sự bế tắc đặc biệt này gây ra sự cố. Chúng tôi cũng đã nhận được các bế tắc về tiết kiệm cho sales_flat_order_grid, với cách khắc phục này, thay vào đó họ ném các vi phạm chống vi phạm liêm chính, điều này rõ ràng là không tốt.
Peter O'Callaghan

2

Tôi có vấn đề tương tự trên trang web Magento 1.11 và tôi có một vé mở với Magento từ ngày 11/12/2012. Họ xác nhận đây là một vấn đề và được cho là đang tạo ra một bản vá.

Câu hỏi của tôi là tại sao giá phải được reindexed tại thời điểm này? Tôi không nghĩ rằng điều này là cần thiết:

#8 /var/www/html/app/code/core/Mage/CatalogInventory/Model/Observer.php(689): Mage_Catalog_Model_Resource_Product_Indexer_Price->reindexProductIds(Array)

1
Nếu một sản phẩm hết hàng và hết hàng, các sản phẩm không được hiển thị trong danh mục, tôi tin rằng chúng bị ẩn bởi công trạng không có hồ sơ chỉ số giá cuối cùng loại trừ chúng khỏi bộ sưu tập sản phẩm khi giá được tham gia vào nó .
davidalger

Điều này không trả lời câu hỏi. Có vẻ như bạn đang cố thêm thông tin bổ sung vào câu hỏi ban đầu. Có lẽ thông tin này sẽ tốt hơn khi bình luận về câu hỏi ban đầu.
Luke Mills

Tôi đi với bạn, Kim. Tôi đã có cùng một vé mở từ tháng 11/2011.
philwinkle

Tôi biết đây không phải là một câu trả lời mà là một câu hỏi phụ, tuy nhiên nó trả lời câu hỏi tham khảo câu hỏi này như một bản sao! Vì vậy, Kimberly Thomas và davidalger nhận được sự ủng hộ của tôi khi trả lời cụ thể của tôi "Tại sao nó lại giới thiệu giá?" câu hỏi mà tôi hiện đang googling! Cảm ơn!
cygnus kỹ thuật số

0

Chúng tôi đã có một vấn đề bế tắc tương tự khi các cuộc gọi nhất định được thực hiện trong một chỉ mục lại. Đối với chúng tôi, nó thể hiện chủ yếu khi khách hàng sẽ thêm một cái gì đó vào giỏ hàng. Mặc dù có thể không khắc phục được sự cố cơ bản thực tế, việc thực hiện lập chỉ mục lại không đồng bộ đã tạm dừng hoàn toàn tất cả các cuộc gọi bế tắc mà chúng ta đã thấy trước đây. Nên hoạt động như một khoảng trống cho đến khi vấn đề cơ bản được khắc phục và được đẩy lên các phiên bản EE / CE (cuối cùng chúng tôi đã mua một tiện ích mở rộng để thực hiện).


0

Tôi đề nghị bạn cài đặt Philwinkle DeadlockR tem. Nó làm việc cho cơ sở dữ liệu của chúng tôi.

https://github.com/philwinkle/Philwinkle_DeadlockRetry

Tôi cũng sẽ đề nghị xem xét bất kỳ chương trình bên ngoài nào đánh vào api web của bạn. Chúng tôi đã có một bản cập nhật QTY cho các sản phẩm và nó đã gây ra nhiều bế tắc. Chúng tôi đã viết lại và đi thẳng vào cơ sở dữ liệu.


1
Repo này không còn được hỗ trợ, nhưng may mắn thay, nó khuyên bạn nên thay thế github.com/AOEpeople/Aoe_DbR tem .
Ngỗng

-1

Tôi đã gặp vấn đề bế tắc năm ngoái nhiều lần tôi đã sắp xếp nó đơn giản bằng cách tăng bộ nhớ cho máy chủ của chúng tôi vì quá trình lập chỉ mục ăn tất cả các tài nguyên.

Bạn cũng nên cho chúng tôi giải pháp reindex async mà tôi đã sử dụng miravist

Để có một hệ thống ổn định hơn, bạn nên nghĩ đến việc tách phần phụ trợ của bạn khỏi frontend để chúng không ăn RAM của nhau.

Đối với kinh nghiệm của tôi, nó không phải là vấn đề của mã nguồn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.