From e8dd5f6ae5dd3b8c2b1bfd5cfdf092c99d0075b2 Mon Sep 17 00:00:00 2001 From: Jonathan Kolyer Date: Wed, 19 Mar 2014 04:24:42 +0000 Subject: [PATCH] VRFS-1483 state transitions and optimistic locking --- db/up/emails.sql | 8 +- ruby/lib/jam_ruby/app/mailers/batch_mailer.rb | 5 +- ruby/lib/jam_ruby/models/email_batch.rb | 76 ++++++++++++++++++- 3 files changed, 84 insertions(+), 5 deletions(-) diff --git a/db/up/emails.sql b/db/up/emails.sql index 47a33d105..9c68b643e 100644 --- a/db/up/emails.sql +++ b/db/up/emails.sql @@ -6,8 +6,12 @@ CREATE TABLE email_batches ( aasm_state VARCHAR(32) NOT NULL default 'pending', - test_emails TEXT NOT NULL, - batch_size INTEGER NOT NULL default 1000, + test_emails TEXT NOT NULL default '', + + qualified_count INTEGER NOT NULL default 0, + sent_count INTEGER NOT NULL default 0, + + lock_version INTEGER, started_at TIMESTAMP, completed_at TIMESTAMP, diff --git a/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb b/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb index 8b16acd08..899e01b16 100644 --- a/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb +++ b/ruby/lib/jam_ruby/app/mailers/batch_mailer.rb @@ -6,7 +6,8 @@ module JamRuby sendgrid_category :use_subject_lines sendgrid_unique_args :env => Environment.mode - def send_batch_email(batch_id, user_id) + def send_batch_email(batch_id, user_ids) + @users = User.find_all_by_id(user_ids) @user = User.where(:id => user_id).limit(1).first batch = EmailBatch.where(:id => batch_id).limit(1).first @body = batch.merged_body(user) @@ -17,6 +18,7 @@ module JamRuby format.text format.html end + batch.did_send(user.email) end def send_batch_email_test(batch_id, email_addy) @@ -29,6 +31,7 @@ module JamRuby format.text format.html end + batch.did_send(email_addy) end end diff --git a/ruby/lib/jam_ruby/models/email_batch.rb b/ruby/lib/jam_ruby/models/email_batch.rb index 9df7d3d30..d3b396c1e 100644 --- a/ruby/lib/jam_ruby/models/email_batch.rb +++ b/ruby/lib/jam_ruby/models/email_batch.rb @@ -2,21 +2,55 @@ module JamRuby class EmailBatch < ActiveRecord::Base self.table_name = "email_batches" + attr_accessible :lock_version + VAR_FIRST_NAME = '@FIRSTNAME' VAR_LAST_NAME = '@LASTNAME' DEFAULT_SENDER = "support@jamkazam.com" + include AASM + aasm do + state :pending, :initial => true + state :testing + state :tested + state :batching + state :batched + state :disabled + + event :enable do + transitions :from => :disabled, :to => :pending + end + event :do_test_run, :after => Proc.new { running_tests } do + transitions :from => [:pending, :tested, :batched], :to => :testing + end + event :did_test_run do + transitions :from => :testing, :to => :tested + end + event :do_batch_run, :after => Proc.new { running_batch } do + transitions :from => [:tested, :pending, :batched], :to => :batching + end + event :did_batch_run do + transitions :from => :batching, :to => :batched + end + event :disable do + transitions :from => [:pending, :tested, :batched], :to => :disabled + end + end + # has_many :email_batch_results, :class_name => 'JamRuby::EmailBatchResult' def self.qualified_users User.select(:email) .where(:opt_out_email_batch => false) - .order('created_at DESC') end def deliver - self.class.qualified_users.each + self.do_batch_run! + + self.class.qualified_users.find_each do |uu| + BatchMailer.send_batch_email_test(self.id, uu.id).deliver + end end def test_users @@ -31,6 +65,8 @@ module JamRuby end def send_test_batch + self.do_test_run! + self.test_users.each do |uu| BatchMailer.send_batch_email_test(self.id, uu.email).deliver end @@ -40,5 +76,41 @@ module JamRuby body.gsub(VAR_FIRST_NAME, user.first_name).gsub(VAR_LAST_NAME, user.last_name) end + def did_send(email) + self.update_with_conflict_validation({ :sent_count => self.sent_count + 1 }) + if self.sent_count >= self.qualified_count + if batching? + self.did_batch_run! + elsif testing? + self.did_test_run! + end + end + end + + protected + + def update_with_conflict_validation(*args) + num_try = 0 + update_attributes(*args) + rescue ActiveRecord::StaleObjectError + num_try += 1 + if 5 > num_try + self.reload + retry + end + end + + def running_batch + self.update_attributes({:qualified_count => self.class.qualified_users.count, + :sent_count => 0 + }) + end + + def running_test + self.update_attributes({:qualified_count => self.class.qualified_users.count, + :sent_count => 0 + }) + end + end end