FIX: Upsert topic votes count atomically (#71)
The `Topic#update_vote_count` method should be concurrency-safe to prevent unique constraint violation errors when multiple processes try to insert a topic_votes_count record for the same topic simultaneously. Signed-off-by: OsamaSayegh <asooomaasoooma90@gmail.com>
This commit is contained in:
parent
148d76facc
commit
8dbc75f965
|
@ -38,3 +38,17 @@ module DiscourseVoting
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: discourse_voting_category_settings
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# category_id :integer
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_discourse_voting_category_settings_on_category_id (category_id) UNIQUE
|
||||||
|
#
|
||||||
|
|
|
@ -7,3 +7,18 @@ module DiscourseVoting
|
||||||
belongs_to :topic
|
belongs_to :topic
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: discourse_voting_topic_vote_count
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# topic_id :integer
|
||||||
|
# votes_count :integer
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_discourse_voting_topic_vote_count_on_topic_id (topic_id) UNIQUE
|
||||||
|
#
|
||||||
|
|
|
@ -8,3 +8,19 @@ module DiscourseVoting
|
||||||
belongs_to :topic
|
belongs_to :topic
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# == Schema Information
|
||||||
|
#
|
||||||
|
# Table name: discourse_voting_votes
|
||||||
|
#
|
||||||
|
# id :bigint not null, primary key
|
||||||
|
# topic_id :integer
|
||||||
|
# user_id :integer
|
||||||
|
# archive :boolean default(FALSE)
|
||||||
|
# created_at :datetime not null
|
||||||
|
# updated_at :datetime not null
|
||||||
|
#
|
||||||
|
# Indexes
|
||||||
|
#
|
||||||
|
# index_discourse_voting_votes_on_user_id_and_topic_id (user_id,topic_id) UNIQUE
|
||||||
|
#
|
||||||
|
|
|
@ -27,8 +27,16 @@ module DiscourseVoting
|
||||||
def update_vote_count
|
def update_vote_count
|
||||||
count = self.votes.count
|
count = self.votes.count
|
||||||
|
|
||||||
topic_vote_count = self.topic_vote_count || DiscourseVoting::TopicVoteCount.new(topic: self)
|
DB.exec(<<~SQL, topic_id: self.id, votes_count: count)
|
||||||
topic_vote_count.update!(votes_count: count)
|
INSERT INTO discourse_voting_topic_vote_count
|
||||||
|
(topic_id, votes_count, created_at, updated_at)
|
||||||
|
VALUES
|
||||||
|
(:topic_id, :votes_count, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
|
||||||
|
ON CONFLICT (topic_id) DO UPDATE SET
|
||||||
|
votes_count = :votes_count,
|
||||||
|
updated_at = CURRENT_TIMESTAMP
|
||||||
|
WHERE discourse_voting_topic_vote_count.topic_id = :topic_id
|
||||||
|
SQL
|
||||||
end
|
end
|
||||||
|
|
||||||
def who_voted
|
def who_voted
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe DiscourseVoting::TopicExtension do
|
||||||
|
let(:user) { Fabricate(:user) }
|
||||||
|
let(:user2) { Fabricate(:user) }
|
||||||
|
|
||||||
|
let(:topic) { Fabricate(:topic) }
|
||||||
|
let(:topic2) { Fabricate(:topic) }
|
||||||
|
|
||||||
|
before do
|
||||||
|
SiteSetting.voting_enabled = true
|
||||||
|
SiteSetting.voting_show_who_voted = true
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#update_vote_count' do
|
||||||
|
it 'upserts topic votes count' do
|
||||||
|
topic.update_vote_count
|
||||||
|
topic2.update_vote_count
|
||||||
|
|
||||||
|
expect(topic.reload.topic_vote_count.votes_count).to eq(0)
|
||||||
|
expect(topic2.reload.topic_vote_count.votes_count).to eq(0)
|
||||||
|
|
||||||
|
DiscourseVoting::Vote.create!(user: user, topic: topic)
|
||||||
|
topic.update_vote_count
|
||||||
|
topic2.update_vote_count
|
||||||
|
|
||||||
|
expect(topic.reload.topic_vote_count.votes_count).to eq(1)
|
||||||
|
expect(topic2.reload.topic_vote_count.votes_count).to eq(0)
|
||||||
|
|
||||||
|
DiscourseVoting::Vote.create!(user: user2, topic: topic)
|
||||||
|
DiscourseVoting::Vote.create!(user: user, topic: topic2)
|
||||||
|
topic.update_vote_count
|
||||||
|
topic2.update_vote_count
|
||||||
|
|
||||||
|
expect(topic.reload.topic_vote_count.votes_count).to eq(2)
|
||||||
|
expect(topic2.reload.topic_vote_count.votes_count).to eq(1)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in New Issue