Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction: INSERT INTO

Recently, I found lots of deadlock errors in my application.

Mysql2::Error: Deadlock found when trying to get lock; try restarting transaction: INSERT INTO `products`....

the code as below:

After user has been created, I will add some products to user. I don't understand why deadlock happened.

class User < ActiveRecord::Base
    after_create :add_products
    has_many :products, :dependent => :destroy

    def self.create_user
      User.create!(.......)
    end

    def add_products
      Product.add(self, "product_name", 10)
    end
    .....
end

class Product < ActiveRecord::Base
    belongs_to :user

    def self.add(user, product_name, amount)
       transaction do
         product = user.products.find_by_product_name(product_name)
         if product
            product.increment :amount, amount
            product.save!
         else
            product = self.create! user_id: user.id,
                                   product_name: product_name,
                                   amount: amount
         end
       end
       product
    end
end

I didn't find the root cause, can anyone give me some advice? Thanks in advance!!!

like image 263
pangpang Avatar asked Dec 02 '14 09:12

pangpang


People also ask

How do you fix a deadlock problem?

Deadlock frequency can sometimes be reduced by ensuring that all applications access their common data in the same order - meaning, for example, that they access (and therefore lock) rows in Table A, followed by Table B, followed by Table C, and so on.

What is deadlock found when trying to get lock try restarting transaction?

Deadlock happens when different concurrent transactions are unable to proceed because each one holds a lock that the other needs. Here is an example: Consider transaction #1 and transaction #2 both running at the same time.

How do I stop deadlocks in Mysql?

Keep transactions small and short in duration to make them less prone to collision. Commit transactions immediately after making a set of related changes to make them less prone to collision. In particular, do not leave an interactive mysql session open for a long time with an uncommitted transaction.


2 Answers

My guess is that you are using InnoDB and probably doing concurrent insertions.

To trace and understand the cause, check out these articles:

One way to fix the issue is to retry like it is shown in the code below:

def add_products
    retries = 0

    begin
        Product.add(self, "product_name", 10)
    rescue  ActiveRecord::StatementInvalid => ex
        if ex.message =~ /Deadlock found when trying to get lock/ #ex not e!!
            retries += 1   
            raise ex if retries > 3  ## max 3 retries 
            sleep 10
            retry
        else
            raise ex
        end
    end
end

Or, there are some gems like transaction_retry to handle MySQL deadlocks.

like image 198
Siva Avatar answered Sep 19 '22 14:09

Siva


This has saved me a lot of headaches: transaction_retry ruby gem.

From the README of the gem:

Retries database transaction on deadlock and transaction serialization errors. Supports MySQL, PostgreSQL, and SQLite.

like image 42
fguillen Avatar answered Sep 18 '22 14:09

fguillen