about summary refs log tree commit diff
path: root/app/services/vote_service.rb
diff options
context:
space:
mode:
authorThibG <thib@sitedethib.com>2019-09-29 22:58:01 +0200
committerEugen Rochko <eugen@zeonfederated.com>2019-09-29 22:58:01 +0200
commit3babf8464b0903b854ec16d355909444ef3ca0bc (patch)
treeec5e2bd9217ac2ef69925827ed9c3ca614c31826 /app/services/vote_service.rb
parentcfe2d1cc4a3c531741fd769241593ebbe03b6711 (diff)
Add voters count support (#11917)
* Add voters count to polls

* Add ActivityPub serialization and parsing of voters count

* Add support for voters count in WebUI

* Move incrementation of voters count out of redis lock

* Reword “voters” to “people”
Diffstat (limited to 'app/services/vote_service.rb')
-rw-r--r--app/services/vote_service.rb32
1 files changed, 29 insertions, 3 deletions
diff --git a/app/services/vote_service.rb b/app/services/vote_service.rb
index 0eeb8fd56..cb7dce6e8 100644
--- a/app/services/vote_service.rb
+++ b/app/services/vote_service.rb
@@ -12,12 +12,24 @@ class VoteService < BaseService
     @choices = choices
     @votes   = []
 
-    ApplicationRecord.transaction do
-      @choices.each do |choice|
-        @votes << @poll.votes.create!(account: @account, choice: choice)
+    already_voted = true
+
+    RedisLock.acquire(lock_options) do |lock|
+      if lock.acquired?
+        already_voted = @poll.votes.where(account: @account).exists?
+
+        ApplicationRecord.transaction do
+          @choices.each do |choice|
+            @votes << @poll.votes.create!(account: @account, choice: choice)
+          end
+        end
+      else
+        raise Mastodon::RaceConditionError
       end
     end
 
+    increment_voters_count! unless already_voted
+
     ActivityTracker.increment('activity:interactions')
 
     if @poll.account.local?
@@ -53,4 +65,18 @@ class VoteService < BaseService
   def build_json(vote)
     Oj.dump(serialize_payload(vote, ActivityPub::VoteSerializer))
   end
+
+  def increment_voters_count!
+    unless @poll.voters_count.nil?
+      @poll.voters_count = @poll.voters_count + 1
+      @poll.save
+    end
+  rescue ActiveRecord::StaleObjectError
+    @poll.reload
+    retry
+  end
+
+  def lock_options
+    { redis: Redis.current, key: "vote:#{@poll.id}:#{@account.id}" }
+  end
 end