Fiyatların yeniden endekslenmesi ödeme sırasında DB kilitlenmelerine neden


47

Ürün Fiyatının yeniden endekslenmesi işleminin çıkış işleminde kilitlenme istisnasına yol açtığına inandığım bir sorun yaşıyorum.

Ödeme sırasında bu istisnayı yakaladım:

Sipariş dönüştürme istisnası: SQLSTATE [40001]: Seri hale getirme hatası: 1213 Kilitlenmeye çalışırken kilitlenme bulundu; işlemi yeniden başlatmayı deneyin

Maalesef, istisnanın yakalandığı yer nedeniyle tam bir yığın izlemem yok, ancak INNODB durumunu kontrol ederek kilitlenme izini sürdüm:

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` 

SQL istekte bulunan tablo kilidi, sonuçta Mage_CatalogInventory_Model_Stock::registerProductsSale(), azaltmak için mevcut envanter sayımını almaya çalışırken ortaya çıkar.

Kilitlenme gerçekleştiği sırada, Product Price re-index işlemi devam ediyordu ve catalog_product_entity tablekilitlenmeye neden olan bir okuma kilidi olduğunu farz ediyorum . Kilitlenmeyi doğru anlıyorsam, herhangi bir okuma kilidi kilitlenmeye neden olur, ancak ürün fiyat endeksi, site ~ 50.000 ürüne sahip olduğundan, kilidi yeniden adil bir süre tutar.

Maalesef, ödeme kodu akışındaki bu noktada, müşterinin kredi kartından (özel bir ödeme modülü aracılığıyla) ödeme yapıldı ve ilgili sipariş nesnesinin yaratılması başarısız oldu.

Benim sorularım:

  • Özel ödeme modülü mantığı arızalı mı? Örneğin, Magento'nun, ödemeyi ödeme yöntemine (kredi kartı) taahhüt etmeden önce teklifini ücretsiz bir sipariş istisnasına dönüştürmesini sağlamak için kabul edilmiş bir akış var mı?

Düzenleme: Görünüşe göre ödeme modülü mantığı gerçekten hatalı çünkü $ paymentmethod-> authorize (), bu kilitlenmenin gerçekleştiği yerin peşinde, daha önce değil (Ivan'ın cevabına göre) gerçekleşmelidir. Ancak, işlem hala kilitlenme tarafından engellenecek (kredi kartına hatalı ücret ödememekle birlikte).

  • Bu işlev çağrısı $stockInfo = $this->_getResource()->getProductsStock($this, array_keys($qtys), true);içinde Mage_CatalogInventory_Model_Stock::registerProductsSale()bir kilitleme okuma, ne kadar tehlikeli olurdu markaların olmayan bir kilitleme okuma yapmak için?

  • Bir cevap için web'de arama yaparken, site sıcakken tam bir yeniden indeksleme yapmamayı öneren birkaç yer önerildi; pek iyi bir çözüm gibi görünmüyor; Tablo kilitlenme ve kilit çekişmelerine neden olan endeksleme sorunu Magento'da bilinen bir sorun mu?

Düzenleme: Burada kalan soru üçüncü sorudan biri gibi görünüyor; Tablo kilitlenmelerine neden olan yeniden indeksleme. Bunun için geçici çözümler arıyorsunuz.

Düzenleme: Kilitlenmelerin kendi başlarına değil, başlı başına bir mesele olmadığı, bunlara verilen yanıtın odak noktası olması gerektiği çok mantıklı olmalıdır. Kilitlenme istisnasını yakalamak ve isteği yeniden yayınlamak için kodda bir nokta bulmak için daha fazla araştırma yapmak. Bunu Zend Framework DB bağdaştırıcısı düzeyinde yapmak bir yaklaşımdır, ancak aynı zamanda bunu sürdürmek için Magento kodunda da yapmanın bir yolunu arıyorum.

Bu konuda ilginç bir yama var: http://www.magentocommerce.com/boards/viewthread/31666/P0/ ilgili bir kilitlenme durumunu çözüyor gibi görünüyor (ama bunu özellikle değil).

Düzenleme: Görünüşe göre kilitlenme CE 1.8 Alpha bir dereceye kadar ele alınmıştır. Bu sürüm Alpha dışında olana kadar hala bir geçici çözüm arıyor


Son zamanlarda benzer bir sorunla karşı karşıya kaldık, hangi ödeme uzantısını kullanıyorsunuz?
Peter O'Callaghan

Bu özel kodlanmış bir eklentidir
Roscius

1
@kalenjordan 1.13'teki endeksleme iyileştirmeleri ve Philwinkle'nın aşağıdaki gibi bir yeniden deneme şeması benim için sorunu büyük ölçüde hafifletti.
Roscius

1
@Roscius kabaca ne kadar hafifletti? Bir çeşit DB hatası görüyorum (bağlantı zaman aşımı, kilitleme bekleme zaman aşımı, kilitlenme) siparişlerimin yaklaşık% 0.2'sini etkiliyor. Çok nadir ama gerçekten tam olarak çözülmesini istiyorum.
kalenjordan

Yanıtlar:


16

Ödeme yönteminizin ödemeyi yanlış işleme koyma olasılığı oldukça yüksek.

Magento Sipariş Tasarruf İşlemi oldukça basittir:

  • Fiyatlar ve ürün bilgileri dahil olmak üzere teklif kaleminden sipariş kalemine aktarılması gereken tüm verileri hazırlar, daha sonra fiyat alımına neden olmaz.
  • Sipariş göndermeden önce etkinlik davet et checkout_type_onepage_save_ordervesales_model_service_quote_submit_before
    • Mage_CatalogInventory_Model_Stock::registerProductsSale() bu olayda gözlemci çağrılır
  • DB işlemini başlat
  • Çağırır $order->place()arayarak ödeme işlemleri yöntem $paymentMethod->authorize(), $paymentMethod->capture()ya $paymentMethod->initialize()kendi mantığına bağlıdır.
  • İşlenmiş sırayı DB tablolarına kaydeden $ order-> save () yöntemini kullanın sales_flat_order_*.
  • DB işleminin gerçekleştirilmesi (Bu aşamada DB, envanter tablosundaki kilidi serbest bırakır)

Gördüğünüz gibi, mümkün olamazdı, ödeme yöntemi envanter kilitlenmeden ve ürün fiyatlarını veya ürün bilgilerini okumadan önce para alıyor.

API, ücretlendirme işlemi için API çağrısı yapıldıktan sonra, ödeme yönteminin bu şekilde uygulanması durumunda ürünlerin fiyatla kendi yüklemesini gerçekleştirmesi durumunda mümkündür.

Umarım bu sorununuzu ayıklamak size yardımcı olacaktır.

Yeniden indekslemeye gelince, eğer ödeme yöntemiyle ilgili bir sorununuz yoksa, güvenli olmalıdır. Kilitler bağlı okuma işlemi para alınmadan önce yapıldığından.


1
Teşekkürler, özel ödeme modülü mantığı biraz kapalı gibi görünüyor. Bununla birlikte, bir endeksleme işleminin bir istisnaya neden olarak ödeme işlemini engelleyeceği registerProductsSale()anlaşılıyor (özel ödeme modülüne yapılan düzeltmeler ile müşterinin kartının tahsil edilmesi sorununu ortadan kaldıracaktır).
Roscius,

8

Bu özel bir uzantı olduğundan, çekirdek dosyaları düzenlemeden kaydetmeyi yeniden denemek için özel bir geçici çözüm (okuma: hack) bulabiliriz.

Tüm kilitlenme sıkıntılarımı yardımcı bir sınıfa eklenmiş aşağıdaki iki yöntemle çözdüm. Aramak yerine $product->save()şimdi aradım 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);
}

Bu, iki farklı şeyi başarır - bir kilitlenme ile karşılaşıldığında yeniden deneme sırasını keser ve bu yeniden deneme için katlanarak artan bir zaman aşımı süresi belirler. Ayrıca işlem yalıtım seviyesini de ayarlar. MySQL'in işlem izolasyon seviyeleri hakkında daha fazla bilgi için SO ve DBA.SE hakkında birçok bilgi var.

FWIW, o zamandan beri bir kilitlenme ile karşılaşmadım.


1
@Mage :: getModel ('core / resource') @ yeni bir bağlantı oluşturmalı. Geçerli işlem yalıtım düzeyini nasıl değiştirebileceğini anlamıyorum.
giftnuss

@giftnuss yeterince adil. Kesin olarak singleton olmalı. Bu kilitlenme
modülüme

@ philwinkle bu adam için teşekkürler. Bir EE 1.13 güncellemesinin sıkıntılarımı çözüp çözmeyeceğini veya buna da bakmam gerekip gerekmediğini anlamaya çalışıyorum. 1.13'ün asenkron olarak indekslendiğini biliyorum ki bu harika bir şey ama aynı temel sorgular söz konusuysa, asenkronin tek başına kilitlenmelerin oluşmasını nasıl önleyeceğini anlamakta zorlanıyorum.
kalenjordan

1
@kalenjordan bir async birleşimidir ve güncellenmiş varien db 1.8 / 1.13'te değişmiştir ve bu da kilitlenme olasılığını azaltır.
Philwinkle

Sanırım $triesbu işleve geçmeyi unuttunsleep($this->getDelay());
Tahir Yasin,

3

Magento forumlarında bir Zend kitaplığı dosyasını düzenlemekten bahsediyorlar: lib / Zend / Db / Statement / Pdo.php

Orijinal _execute işlevi:

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
    }

Değişiklikten sonra:

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;
    }

Gördüğünüz gibi değiştirilmiş olan tek şey $ $ dengesinin dışına taşınmış olmasıdır.

Her zaman olduğu gibi, bunu bir geliştirme / test ortamında denemeniz ve bu düzeltmeyi anında bir üretim ortamına dağıtmamanız önerilmektedir.


2
Altta yatan çerçeve dosyalarını düzenleme konusunda endişeliyim, yeniden denemenin yerine Magento kod düzeyinde gerçekleşmesi gerekiyor.
Roscius

Önerilen düzeltmeyi denedik ve bu özel çıkmazın sorunlara yol açmasını engelledi. Ayrıca sales_flat_order_grid'e yapılan tasarruflarda kilitlenmeler alıyorduk, bu düzeltmeyi uyguladıkları zaman bunun yerine bütünlük kısıtlamaları ihlal ediyorlardı ki bu açıkça iyi değil.
Peter O'Callaghan

2

Aynı sorunu Magento 1.11 sitesinde de yaşıyorum ve 11/12/2012 tarihinden beri Magento'da açık bir biletim var. Bir sorun olduğunu doğruladılar ve bir yama oluşturduğunu farz ettiler.

Sorum şu: fiyatın neden şu anda yeniden yönlendirilmesi gerekiyor? Bunun gerekli olduğunu sanmıyorum:

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

1
Bir ürün stokta kalkarsa ve stokta olmayan ürünlerin katalogda gösterilmemesi gerekiyorsa, fiyatlara eklendiğinde bunları ürün koleksiyonundan hariç tutan hiçbir fiyat endeksi kaydının bulunmaması nedeniyle gizlendiğine inanıyorum. .
davidalger

Bu soruya cevap vermiyor. Orijinal soruya ek bilgi eklemeye çalışıyorsunuz gibi görünüyor. Belki de bu bilgi orijinal soruya yorum olarak daha iyi olurdu.
Luke Mills

Ben seninleyim Kim. 11/2011'den beri aynı bileti açtım.
Philwinkle,

Bunun teknik olarak bir cevap değil, bir alt soru olduğunu biliyorum, ancak bu soruyu iki kopya olarak gösteren soruya cevap veriyor! Öyleyse Kimberly Thomas ve davidalger, "Neden fiyatları yeniden değerlendiriyor?" soru ben '; şu anda googling m! Teşekkürler!
Cygnus Digital

0

Yeniden dizin oluşturma sırasında belirli aramalar yapıldığında benzer bir kilitlenme sorunu yaşadık. Bizim için çoğunlukla bir müşterinin sepete bir şey ekleyeceği zaman kendini gösterdi. Muhtemelen gerçek sorunu çözmese de, zaman uyumsuz yeniden indeksleme uygulamak daha önce gördüğümüz tüm kilitlenme çağrılarını tamamen durdurdu. Temel sorun giderilinceye ve EE / CE basımlarına itilene kadar durma boşluğu gibi çalışmalı (bunun için bir uzatma satın aldık).


0

Philwinkle DeadlockRetry'i yüklemenizi öneririm. Veritabanımız için çalıştı.

https://github.com/philwinkle/Philwinkle_DeadlockRetry

Ayrıca, web api'nize çarpan dış programlarınıza da bakmanızı öneririm. Ürünler için QTY'yi güncelleyen bir tane vardı ve birçok kilitlenmeye neden oluyordu. Bunu tekrar yazdık ve doğrudan veritabanına gittik.


1
Bu depo artık desteklenmiyor, ama neyse ki değiştirilmesini tavsiye ediyor github.com/AOEpeople/Aoe_DbRetry .
Kaz

-1

Geçen yıl birçok kez kilitlenme sorunuyla karşılaştım, yalnızca indeksleme işlemi tüm kaynakları yerse, sunucumuz için belleği artırarak çözdüm.

Siz de bize eşzamansız reindex çözümü yapmalıyım miravist

Daha kararlı bir sistem için, arka ucunuzu ön uçtan ayırmayı düşünmelisiniz, böylece birbirlerinin RAM'lerini yemezler.

Tecrübelerime göre, kaynak kodun sorunu değil.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.