当前位置: 动力学知识库 > 问答 > 编程问答 >

concurrency - Rails: ActiveRecord:RecordNotUnique with first_or_create

问题描述:

I have this index in my Model's table:

UNIQUE KEY `index_panel_user_offer_visits_on_offer_id_and_panel_user_id` (`offer_id`,`panel_user_id`)

And this code:

def get_offer_visit(offer_id, panel_user_id)

PanelUserOfferVisit.where(:offer_id => offer_id, :panel_user_id => panel_user_id).first_or_create!

end

is randomly causing an ActiveRecord::RecordNotUnique exception in our application,

the issue is:

Duplicate entry for key

'index_panel_user_offer_visits_on_offer_id_and_panel_user_id'

I've read on the rails doc that first_or_create! is not an atomic method and can cause this kind of issues in a high concurrency environment and that a solution would be just to catch the exception and retry.

I tried different approaches, including Retriable gem to retry a certain number of times to repeat the operation but RecordNotUnique is still raised.

I even tried to change the logic:

def get_offer_visit(offer_id, panel_user_id)

begin

offer_visit = PanelUserOfferVisit.where(:offer_id => offer_id, :panel_user_id => panel_user_id).first_or_initialize

offer_visit.save!

offer_visit

rescue ActiveRecord::RecordNotUnique

offer_visit = PanelUserOfferVisit.where(:offer_id => offer_id, :panel_user_id => panel_user_id).first

raise Exception.new("offer_visit not found") if offer_visit.nil?

offer_visit

end

end

But the code still raise the custom exception I made and I really don't understand how it can fails to create the record on the first try because the record exists but then when it tries to find the record it doesn't find it again.

What am I missing?

网友答案:

You seem to be hitting rails query cache for PanelUserOfferVisit.where(...).first query: rails have just performed identical SQL query, it thinks that result will remain the same for the duration of the request.

ActiveRecord::Base.connection.clear_query_cache before the second call to db should help

Also you do not have to save! the record if it is not new_record?

网友答案:

If I understand properly your problem here is due to a race condition. I didn't found the exact piece of code from Github but lets say the method find_or_create is something like (edit: here is the code):

def first_or_create!(attributes = nil, &block)
  first || create!(attributes, &block)
end

You should try to solve the race conditions using optimistic or pessimistic locking

Once locking implemented within your custom method, you should continue capturing possible NotUnique exceptions but try to clean cache or use uncached block before to perform the find again.

分享给朋友:
您可能感兴趣的文章:
随机阅读: