1class UpdateSignatureCountsJob < ApplicationJob |
|
2 queue_as :highest_priority |
|
4 delegate :update_signature_counts, to: :Site |
|
5 delegate :signature_count_updated_at, to: :Site |
|
6 delegate :signature_count_interval, to: :Site |
|
7 delegate :signature_count_updated_at!, to: :Site |
|
8 delegate :petition_ids_signed_since, to: :Signature |
|
10 rescue_from StandardError do |exception| |
|
11 log_exception(exception)
|
|
12 retry_job(wait: signature_count_interval) |
|
13 end |
|
|
15 def perform(now = current_time) |
16 # Exit if updating signature counts is disabled |
|
17 return unless update_signature_counts |
|
19 time = now.in_time_zone
|
|
20 signature_count_at = signature_count_interval.seconds.ago(time)
|
|
22 # Exit if the signature counts have been updated since this job was scheduled |
|
23 return unless signature_count_updated_at < signature_count_at |
|
|
25 petitions.each do |petition| |
26 # Skip this petition if it's been updated since this job was scheduled |
|
27 next if petition.last_signed_at? && petition.last_signed_at > signature_count_at |
|
29 # Check to see if the signature count is being reset |
|
30 if petition.signature_count_reset_at? |
|
31 if petition.signature_count_reset_at < 5.minutes.ago |
|
32 # Something's seriously wrong if a petition is taking |
|
33 # more than 5 minutes to reset its signature count |
|
34 message = "Petition #{petition.id} has been resetting its count for more than 5 minutes" |
|
35 Appsignal.send_exception(RuntimeError.new(message)) |
|
36 end |
|
38 # Skip this petition as the updates will conflict |
|
39 next |
|
40 end |
|
42 # Save the current last_signed_at for the start of the journal window |
|
43 last_signed_at = petition.last_signed_at
|
|
45 # Don't update the journals unless we have updated the signature count |
|
46 # This prevents the journals getting multiple updates before the creator's |
|
47 # signature is added to the count which may not be done immediately as the |
|
48 # main signature count window lags by `signature_count_interval` seconds |
|
49 # to prevent race conditions with validated_at timestamps created in Ruby |
|
50 if petition.increment_signature_count!(signature_count_at) |
|
51 ConstituencyPetitionJournal.increment_signature_counts_for(petition, last_signed_at) |
|
52 CountryPetitionJournal.increment_signature_counts_for(petition, last_signed_at) |
|
53 end |
|
54 end |
|
56 signature_count_updated_at!(signature_count_at)
|
|
57 reschedule_job(scheduled_time(time))
|
|
58 end |
|
60 private
|
|
|
62 def current_time |
63 Time.current.change(usec: 0).iso8601 |
|
64 end |
|
|
66 def log_exception(exception) |
67 logger.info(log_message(exception))
|
|
68 end |
|
|
70 def log_message(exception) |
71 "#{exception.class.name} while running #{self.class.name}" |
|
72 end |
|
|
74 def petition_ids |
75 petition_ids_signed_since(signature_count_updated_at)
|
|
76 end |
|
|
78 def petitions |
79 Petition.where(id: petition_ids) |
|
80 end |
|
|
82 def reschedule_job(time) |
83 self.class.set(wait_until: time).perform_later(time.iso8601) |
|
84 end |
|
|
86 def scheduled_time(now) |
87 signature_count_interval.seconds.since(now)
|
|
88 end |
|
89end |