From 526e76ced27058fd69f419ac919083a63147e429 Mon Sep 17 00:00:00 2001 From: Rafael dos Santos Silva Date: Mon, 29 Apr 2019 16:34:42 -0300 Subject: [PATCH] FIX: Use PostgreSQL 'ON CONFLICT' to deal with race condition On busy sites, concurrent requests to insert into post_timings can occur, which was dealt with using Ruby exceptions. This moves the handling to PostgreSQL which makes it a bit faster, and prevents a spam of ERROR in the database logs. --- app/models/post_timing.rb | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/app/models/post_timing.rb b/app/models/post_timing.rb index 0de539a6d37..e3d94baf2a4 100644 --- a/app/models/post_timing.rb +++ b/app/models/post_timing.rb @@ -33,20 +33,12 @@ class PostTiming < ActiveRecord::Base end def self.record_new_timing(args) - begin - DB.exec("INSERT INTO post_timings (topic_id, user_id, post_number, msecs) - SELECT :topic_id, :user_id, :post_number, :msecs - WHERE NOT EXISTS(SELECT 1 FROM post_timings - WHERE topic_id = :topic_id - AND user_id = :user_id - AND post_number = :post_number)", - args) - rescue PG::UniqueViolation - # concurrency is hard, we are not running serialized so this can possibly - # still happen, if it happens we just don't care, its an invalid record anyway - return - end - + DB.exec("INSERT INTO post_timings (topic_id, user_id, post_number, msecs) + SELECT :topic_id, :user_id, :post_number, :msecs + ON CONFLICT DO NOTHING", + args) + # concurrency is hard, we are not running serialized so this can possibly + # still happen, if it happens we just don't care, its an invalid record anyway Post.where(['topic_id = :topic_id and post_number = :post_number', args]).update_all 'reads = reads + 1' UserStat.where(user_id: args[:user_id]).update_all 'posts_read_count = posts_read_count + 1' end