From ae0625323ac441d3bef49c21313e698447721b48 Mon Sep 17 00:00:00 2001 From: Alan Guo Xiang Tan Date: Wed, 9 Feb 2022 21:48:18 +0800 Subject: [PATCH] FIX: Avoid errors when updating post and topic count user stats. (#15876) In ab5361d69ae4b55a7635676eef7d80dd96d8460c, we rescue from the PG error but the transaction is already aborted causing any DB query after to fail. As such, we avoid triggering the error in the first place by checking that we would not be insertin a negative number into the counter cache. Follow-up to ab5361d69ae4b55a7635676eef7d80dd96d8460c --- app/services/user_stat_count_updater.rb | 31 ++++++++++--------- spec/services/user_stat_count_updater_spec.rb | 7 ++++- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/app/services/user_stat_count_updater.rb b/app/services/user_stat_count_updater.rb index 38810e5c689..90866295d7b 100644 --- a/app/services/user_stat_count_updater.rb +++ b/app/services/user_stat_count_updater.rb @@ -17,23 +17,24 @@ class UserStatCountUpdater return if post.topic.private_message? stat = user_stat || post.user.user_stat - if post.is_first_post? - stat.public_send(action, :topic_count) - elsif post.post_type == Post.types[:regular] - stat.public_send(action, :post_count) - end - rescue ActiveRecord::StatementInvalid => e - if e.cause.is_a?(PG::CheckViolation) + column = + if post.is_first_post? + :topic_count + elsif post.post_type == Post.types[:regular] + :post_count + end + + return if column.blank? + + if action == :decrement! && stat.public_send(column) < 1 # There are still spots in the code base which results in the counter cache going out of sync. However, - # we have a job that runs on a daily basis which will correct the count. Therefore, avoid raising an error for now - # and log the exception instead. - Discourse.warn_exception( - e, - message: "Attempted to insert negative count into UserStat#post_count or UserStat#topic_count" - ) - else - raise + # we have a job that runs on a daily basis which will correct the count. Therefore, we always check that we + # wouldn't end up with a negative count first before inserting. + Rails.logger.warn("Attempted to insert negative count into UserStat##{column}\n#{caller.join('\n')}") + return end + + stat.public_send(action, column) end end end diff --git a/spec/services/user_stat_count_updater_spec.rb b/spec/services/user_stat_count_updater_spec.rb index a765e97072d..2e60a451103 100644 --- a/spec/services/user_stat_count_updater_spec.rb +++ b/spec/services/user_stat_count_updater_spec.rb @@ -6,6 +6,7 @@ describe UserStatCountUpdater do fab!(:user) { Fabricate(:user) } fab!(:user_stat) { user.user_stat } fab!(:post) { Fabricate(:post) } + fab!(:post_2) { Fabricate(:post, topic: post.topic) } before do @orig_logger = Rails.logger @@ -19,6 +20,10 @@ describe UserStatCountUpdater do it 'should log the exception when a negative count is inserted' do UserStatCountUpdater.decrement!(post, user_stat: user_stat) - expect(@fake_logger.warnings.first).to include("PG::CheckViolation") + expect(@fake_logger.warnings.last).to match("topic_count") + + UserStatCountUpdater.decrement!(post_2, user_stat: user_stat) + + expect(@fake_logger.warnings.last).to match("post_count") end end