Indeks ulang harga menyebabkan kebuntuan DB selama checkout

47

Saya mengalami masalah di mana saya percaya proses pengindeksan ulang Harga Produk menyebabkan pengecualian kebuntuan dalam proses checkout.

Saya menangkap pengecualian ini dalam proses checkout:

Pengecualian konversi pesanan: SQLSTATE [40001]: Kegagalan serialisasi: 1213 Deadlock ditemukan saat mencoba mendapatkan kunci; coba mulai kembali transaksi

Sayangnya saya tidak memiliki jejak tumpukan penuh karena di mana pengecualian ditangkap, tetapi memeriksa status INNODB saya dapat melacak kebuntuan:

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` 

Kunci tabel permintaan SQL pada akhirnya dihasilkan dari Mage_CatalogInventory_Model_Stock::registerProductsSale()ketika mencoba untuk mendapatkan jumlah persediaan saat ini untuk menurunkannya.

Pada saat kebuntuan terjadi, proses indeks ulang Harga Produk sedang berjalan dan saya berasumsi itu memiliki kunci baca catalog_product_entity tableyang menyebabkan kebuntuan. Jika saya memahami kebuntuan dengan benar setiap kunci baca akan menyebabkan kebuntuan, tetapi indeks harga produk menahan kunci untuk waktu yang adil karena situs memiliki ~ 50.000 produk.

Sayangnya, pada titik ini dalam alur kode checkout, kartu kredit pelanggan telah ditagih (melalui modul pembayaran khusus), dan pembuatan objek pesanan yang sesuai gagal.

Pertanyaan saya adalah:

  • Apakah logika modul pembayaran khusus salah? yaitu apakah ada aliran yang diterima untuk memastikan bahwa Magento dapat mengubah penawaran menjadi pengecualian pesanan gratis sebelum melakukan tagihan ke metode pembayaran (kartu kredit)?

Sunting: Tampaknya logika modul pembayaran memang salah karena panggilan ke $ paymentmethod-> otorize () harus terjadi setelah tempat kebuntuan ini terjadi, bukan sebelumnya (sesuai jawaban Ivan di bawah). Namun, transaksi masih akan diblokir oleh jalan buntu (meskipun tanpa biaya sesat ke kartu kredit).

  • Ini panggilan fungsi $stockInfo = $this->_getResource()->getProductsStock($this, array_keys($qtys), true);dalam Mage_CatalogInventory_Model_Stock::registerProductsSale()merek itu penguncian membaca, betapa berbahayanya itu akan membuatnya non-penguncian membaca?

  • Dalam mencari jawaban di web, beberapa tempat menyarankan tidak menjalankan pengindeksan ulang penuh saat situs sedang panas; sepertinya bukan solusi yang bagus; Apakah masalah pengindeksan menyebabkan kebuntuan tabel dan pertikaian kunci masalah yang dikenal di Magento, apakah ada solusi?

Sunting: Tampaknya pertanyaan yang tersisa di sini adalah pertanyaan dari pertanyaan ketiga; pengindeksan ulang menyebabkan kebuntuan tabel. Mencari solusi untuk ini.

Sunting: Konsep bahwa kebuntuan tidak ada dalam dan tentang masalah mereka sendiri, melainkan tanggapan terhadap mereka harus menjadi fokus, masuk akal. Investigasi lebih lanjut untuk menemukan titik dalam kode untuk menangkap pengecualian kebuntuan dan menerbitkan kembali permintaan. Melakukan hal ini pada tingkat adaptor DB Zend Framework adalah satu pendekatan, tetapi saya juga mencari cara untuk melakukan ini dalam kode Magento untuk memudahkan pemeliharaan.

Ada tambalan yang menarik di utas ini: http://www.magentocommerce.com/boards/viewthread/31666/P0/ yang tampaknya mengatasi kondisi kebuntuan terkait (tapi bukan yang ini secara khusus).

Sunting: Rupanya kebuntuan telah diatasi pada gelar di CE 1.8 Alpha. Masih mencari solusi hingga versi ini keluar dari Alpha

Roscius
sumber
Kami telah menghadapi masalah serupa baru-baru ini, ekstensi pembayaran apa yang Anda gunakan?
Peter O'Callaghan
Ini adalah ekstensi kode khusus
Roscius
1
@kalenjordan Peningkatan pengindeksan di 1.13 dan skema coba ulang seperti di bawah ini sebagian besar telah mengurangi masalah bagi saya.
Roscius
1
@Roscius kira-kira berapa banyak yang telah mereka mitigasi? Saya melihat beberapa jenis kegagalan DB (batas waktu koneksi, waktu tunggu kunci, deadlock) memengaruhi sekitar 0,2% pesanan saya. Sangat jarang tetapi saya benar-benar ingin menyelesaikannya sepenuhnya.
kalenjordan
1
Kemungkinan duplikat Apa alasan menjalankan reindex harga setelah melakukan pemesanan?
Teja Bhagavan Kollepara

Jawaban:

16

Ada kemungkinan yang cukup besar bahwa metode pembayaran Anda memproses pembayaran secara salah.

Proses Penyimpanan Pesanan Magento cukup sederhana:

  • Mempersiapkan semua data yang harus ditransfer dari item kutipan ke item pesanan, termasuk harga dan informasi produk, setelah itu tidak meminta pengambilan harga.
  • Meminta sebelum pesanan mengirimkan acara checkout_type_onepage_save_orderdansales_model_service_quote_submit_before
    • Mage_CatalogInventory_Model_Stock::registerProductsSale() dipanggil pada pengamat acara ini
  • Mulai transaksi DB
  • Meminta $order->place()metode yang memproses pembayaran dengan menelepon $paymentMethod->authorize(), $paymentMethod->capture()atau $paymentMethod->initialize()bergantung pada logikanya.
  • Gunakan metode $ order-> save () yang menyimpan pesanan yang telah diproses ke tabel DB sales_flat_order_*.
  • Komit transaksi DB (Pada langkah ini DB melepaskan kunci pada tabel persediaan)

Jadi seperti yang Anda lihat, tidak mungkin, metode pembayaran mengenakan biaya sebelum inventaris mengunci dan membaca harga produk atau info produk.

Ini hanya mungkin dalam kasus jika metode pembayaran dilaksanakan sedemikian rupa, sehingga melakukan pemuatan produk dengan harga, setelah panggilan API untuk operasi pengisian dilakukan.

Semoga ini bisa membantu Anda dalam men-debug masalah Anda.

Adapun pengindeksan ulang, itu harus aman, jika Anda tidak memiliki masalah ini dengan metode pembayaran. Sejak operasi baca yang bergantung pada kunci dilakukan sebelum uang dikenakan biaya.

Ivan Chepurnyi
sumber
1
Terima kasih, sepertinya logika modul pembayaran khusus tidak aktif. Namun sepertinya proses pengindeksan akan memblokir checkout dengan menyebabkan pengecualian dalam registerProductsSale()(memahami bahwa dengan perbaikan ke modul pembayaran khusus akan menghilangkan masalah meminta kartu pelanggan dibebankan).
Roscius
8

Karena ini adalah ekstensi khusus, kami dapat menemukan solusi khusus (baca: retas) untuk mencoba ulang penyimpanan tanpa mengedit file inti.

Saya telah menyelesaikan semua masalah jalan buntu saya dengan dua metode berikut yang ditambahkan ke kelas pembantu. Alih-alih menelepon $product->save()saya sekarang memanggil 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);
}

Ini menyelesaikan dua hal yang berbeda - ini mengantri coba lagi ketika jalan buntu ditemukan, dan menetapkan batas waktu yang meningkat secara eksponensial untuk coba lagi itu. Ini juga menetapkan tingkat isolasi transaksi. Ada banyak informasi tentang SO dan DBA.SE untuk informasi lebih lanjut tentang tingkat isolasi transaksi MySQL.

FWIW, saya tidak pernah menemui jalan buntu sejak itu.

Philwinkle
sumber
1
@Mage :: getModel ('core / resource') @ harus membuat koneksi baru. Saya tidak mengerti bagaimana hal itu dapat mengubah tingkat isolasi transaksi saat ini.
giftnuss
@ Giftnuss cukup adil. Pasti singleton pasti. Jangan ragu untuk berkontribusi ini pada modul kebuntuan saya di github
philwinkle
@ philwinkle terima kasih untuk pria ini. Saya mencoba untuk mencari tahu apakah upgrade EE 1.13 akan menyelesaikan masalah saya atau apakah saya harus melihat ini juga. Saya tahu bahwa 1,13 melakukan pengindeksan secara asinkronik yang besar tetapi jika permintaan mendasar yang sama terlibat, saya kesulitan memahami bagaimana async sendiri akan mencegah kebuntuan yang pernah terjadi.
kalenjordan
1
@kalenjordan itu adalah kombinasi dari async dan perubahan varien db yang diperbarui di 1.8 / 1.13 yang mengurangi kemungkinan deadlock.
philwinkle
Saya pikir Anda lupa beralih $trieske fungsi inisleep($this->getDelay());
Tahir Yasin
3

Di forum Magento mereka berbicara tentang mengedit file perpustakaan Zend: lib / Zend / Db / Statement / Pdo.php

Fungsi _execute asli:

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
    }

Setelah modifikasi:

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

Seperti yang Anda lihat, satu-satunya hal yang telah diubah adalah $ mencoba telah dipindahkan di luar loop.

Seperti biasa disarankan untuk mencoba ini pada lingkungan pengembangan / pengujian dan tidak langsung menerapkan perbaikan ini pada lingkungan produksi.

Kenny
sumber
2
Saya khawatir tentang pengeditan file kerangka kerja yang mendasarinya, tampaknya percobaan ulang harus dilakukan di tingkat kode Magento.
Roscius
Kami telah mencoba perbaikan yang disarankan, dan memang mencegah kebuntuan khusus ini menyebabkan masalah. Kami juga menerima deadlock pada save ke sales_flat_order_grid, dengan perbaikan ini, mereka malah melakukan pelanggaran integritas contraint, yang jelas tidak bagus.
Peter O'Callaghan
2

Saya memiliki masalah yang sama pada situs Magento 1.11 dan saya memiliki tiket terbuka dengan Magento sejak 11/12/2012. Mereka mengkonfirmasi itu adalah masalah dan seharusnya membuat tambalan.

Pertanyaan saya adalah mengapa harga harus diindeks ulang saat ini? Saya pikir ini tidak diperlukan:

#8 /var/www/html/app/code/core/Mage/CatalogInventory/Model/Observer.php(689): Mage_Catalog_Model_Resource_Product_Indexer_Price->reindexProductIds(Array)
Kimberely Thomas
sumber
1
Jika suatu produk kehabisan stok dan kehabisan stok produk tidak seharusnya ditampilkan dalam katalog, saya percaya mereka disembunyikan karena tidak memiliki catatan indeks harga yang akhirnya mengeluarkan mereka dari koleksi produk ketika harga digabungkan ke dalamnya .
davidalger
Ini tidak menjawab pertanyaan. Sepertinya Anda mencoba menambahkan informasi tambahan ke pertanyaan awal. Mungkin informasi ini akan lebih baik sebagai komentar pada pertanyaan awal.
Luke Mills
Aku bersamamu, Kim. Saya sudah memiliki tiket yang sama sejak 11/2011.
philwinkle
Saya tahu ini secara teknis bukan jawaban tapi sub-pertanyaan, namun itu menjawab pertanyaan yang merujuk pertanyaan ini sebagai duplikat! Jadi, Kimberly Thomas dan davidalger mendapatkan jawaban saya untuk menjawab pertanyaan spesifik saya, "Mengapa itu mengindeks ulang harga?" pertanyaan yang sedang saya googling! Terima kasih!
cygnus digital
0

Kami memiliki masalah kebuntuan yang serupa ketika panggilan tertentu dilakukan selama indeks ulang. Bagi kami, itu memanifestasikan dirinya sebagian besar ketika pelanggan akan menambahkan sesuatu ke troli. Meskipun mungkin tidak memperbaiki masalah mendasar yang sebenarnya, menerapkan pengindeksan ulang asinkron telah sepenuhnya menghentikan semua panggilan deadlock yang sebelumnya kita lihat. Harus berfungsi sebagai penghenti sementara sampai masalah mendasar diperbaiki dan didorong ke edisi EE / CE (kami akhirnya membeli ekstensi untuk melakukannya).

fr0x
sumber
0

Saya sarankan Anda menginstal Philwinkle DeadlockRetry. Ini berfungsi untuk basis data kami.

https://github.com/philwinkle/Philwinkle_DeadlockRetry

Saya juga menyarankan melihat program luar Anda yang mengenai api web Anda. Kami punya satu yang memperbarui QTY untuk produk dan itu menyebabkan banyak kebuntuan. Kami menulis ulang itu dan langsung ke database.

Chris Rosenau
sumber
1
Repo ini tidak lagi didukung, tetapi untungnya merekomendasikan penggantian github.com/AOEpeople/Aoe_DbRetry .
Angsa
-1

Saya menemui masalah kebuntuan tahun lalu berkali-kali saya mengatasinya hanya dengan meningkatkan memori untuk server kami karena proses pengindeksan memakan semua sumber daya.

Anda juga harus menggunakan kami async reindex solusi yang saya gunakan miravist

Untuk sistem yang lebih stabil, Anda harus memikirkan memisahkan backend Anda dari frontend sehingga mereka tidak saling memakan RAM.

Bagi pengalaman saya, itu bukan masalah kode sumber.

phanvugiap
sumber