I have a recurring delayed job that sends out a confirmation email and marks the order as completed so that the next time the delayed job runs the order will not be re-processed.
Sometimes (seems to be when a certain string is tied to a promo code field but that might just be a coincidence) the job processes and sends the email but does not save the record and mark it as completed. I have used IRB to set the record to what the code would and verified that the record is valid.
Any ideas why this might be happening or has anyone seen this happen?
class PaymentEmailAndLock < Struct.new(:blank)
include Delayed::RecurringJob
run_at '8:00pm'
run_every 1.day
timezone 'US/Eastern'
queue 'dailyjobs'
def perform
time_set = 30.hours.ago..2.hours.ago
#mail_and_lock = Cart.where(updated_at:time_set,payment_sent:true,complete_order_lock:false)
#mail_and_lock.each do |obj|
obj.complete_order_lock = true
obj.survey_available = true
obj.save
if obj.payment == 1
MessageMailer.delay(queue: 'mailers').message_payment_paper(obj.cust_email,obj)
else
MessageMailer.delay(queue: 'mailers').message_payment_digital(obj.cust_email,obj)
end
end
end
end
Related
In my rails application I have a job model which I want the status to be changed automatically to "archived" after 30 days from approval by the admin is this possible? if so what is the way to do it?
I would add an attribute named "archive_time" as a datetime when it enters the approved state.
Then you can set up a rake task to set the archived state and where the archive_time is in the past. This might look like this:
jobs = Job.where("state = ? and archive_time >= ?", 'approved', Time.now)
jobs.each {|job| job.archive }
Then schedule the rake task to be run once a day. I would use cron to achieve this.
A rake task or background job (such as Active Job or Delayed Job ) can do the trick however you might not need them in this case. If you are dealing with timestamps in your database, you can create a scope or a method to mark jobs as archived.
For instance, if you have a column named approved_at that is a datetime. When you approve a job, you set the approved_at = Time.now.
Now you can create a method that indicates if the job is archived:
def is_archived?
approved_at && approved_at < Time.now - 30.days
end
And to get all the archived jobs, you can create a scope:
scope :archived, -> { where('approved_at IS NOT NULL AND approved_at <?', Time.now - 30.days)}
I created a batch email system for my website. The problem I have, which is terrible, is it continuously sends out emails. It seems the job is stuck in an infinite loop. Please advise. It is crazy because on my development server only one email is sent per account, but on my production server I received 5 emails. Thus, meaning all users of my site received multiple emails.
Controller:
class BatchEmailsController < ApplicationController
before_filter :authenticate_admin_user!
def deliver
flash[:notice] = "Email Being Delivered"
Delayed::Job.enqueue(BatchEmailJob.new(params[:batch_email_id]), 3, 10.seconds.from_now, :queue => 'batch-email', :attempts => 0)
redirect_to admin_batch_emails_path
end
end
Job in the lib folder:
class BatchEmailJob < Struct.new(:batch_email_id)
def perform
be = BatchEmail.find(batch_email_id)
if be.to.eql?("Contractors")
cs = Contractor.all
cs.each do|c|
begin
BatchEmailMailer.batch_email(be.subject, be.message, be.link_name, be.link_path, be.to, c.id).deliver
rescue Exception => e
Rails.logger.warn "Batch Email Error: #{e.message}"
end
else
ps = Painter.all
ps.each do |p|
begin
BatchEmailMailer.batch_email(be.subject, be.message, be.link_name, be.link_path, be.to, p.id).deliver
rescue Exception => e
Rails.logger.warn "Batch Email Error: #{e.message}"
end
end
end
end
end
Delayed Job Initializer:
Delayed::Worker.max_attempts = 0
Please provide feedback on this approach. I want to send out the batch email to all users, but avoid retrying multiple times if something goes wrong. I added rescue block to catch email exceptions in hope that the batch will skip errors and continue processing. As a last resort do not run again if something else goes wrong.
What one of my apps does which seems to work flawlessly after millions of emails:
1) in an initializer, do NOT let DelayedJob re-attempt a failed job AND ALSO not let DJ delete failed jobs:
Delayed::Worker.destroy_failed_jobs = false
Delayed::Worker.max_attempts = 1
2) Scheduling a mass email is 1 job, aka the "master job"
3) When THAT jobs runs, it spawns N jobs where N is the number of emails being sent. So each email gets its own job. (Note: if you use a production email service with 'batch' capability, one "email" might actually be a batch of 100 or 1000 emails.)
4) We have an admin panel that shows us if we have any failed jobs, and if they are, because we don't delete them, we can inspect the failed job and see what happened (malformed email address etc)
If one email fails, the others are un-affected. And no email can ever be sent twice.
I am doing the delayed_job by tobi and when I run the delayed_job but the fbLikes count is all wrong and it seems to increment each time I add one more company. Not sure wheres the logic wrong. The fbLikes method I tested before and it work(before I changed to delayed_job)
not sure where the "1" come from...
[output]
coca-cola
http://www.cocacola.com
Likes: 1 <--- Not sure why the fbLikes is 1 and it increment with second company fbLikes is 2 and so on...
.
[Worker(host:aname.local pid:1400)] Starting job worker
[Worker(host:aname.local pid:1400)] CountJob completed after 0.7893
[Worker(host:aname.local pid:1400)] 1 jobs processed at 1.1885 j/s, 0 failed ...
I am running the delayed_job in Model and trying to run the job of
counting the facebook likes
here is my code.
[lib/count_rb.job]
require 'net/http'
class CountJob< Struct.new(:fbid)
def perform
uri = URI("http://graph.facebook.com/#{fbid}")
data = Net::HTTP.get(uri)
return JSON.parse(data)['likes']
end
end
[Company model]
before_save :fb_likes
def fb_likes
self.fbLikes = Delayed::Job.enqueue(CountJob.new(self.fbId))
end
the issue is coming from
before_save :fb_likes
def fb_likes
self.fbLikes = Delayed::Job.enqueue(CountJob.new(self.fbId))
end
the enqueue method will not return the results of running the CountJob. I believe it will return whether the job successfully enqueued or not and when you are saving this to the fb_likes value it will evaluate to 1 when the job is enqueued successfully.
You should be setting fbLikes inside the job that is being run by delayed_job not as a result of the enqueue call.
before_save :enqueue_fb_likes
def fb_likes
Delayed::Job.enqueue(CountJob.new(self.fbId))
end
Your perform method in the CountJob class should probably take the model id for you to look up and have access to the fbId and the fbLikes attributes instead of just taking the fbId.
class CountJob< Struct.new(:id)
def perform
company = Company.find(id)
uri = URI("http://graph.facebook.com/#{company.fbid}")
data = Net::HTTP.get(uri)
company.fbLikes = JSON.parse(data)['likes']
company.save
end
I have a delayed_job designed to send an email using a mailer.
Upon completion, I need to record that the email was sent -- I do this by saving the newly created ContactEmail.
Right now, the new ContactEmail records gets saved even if the delayed_job fails.
How do I correct that so that the new ContactEmail is only saved when the mailer is successfully sent?
Here is the snippet from the cron task which calls the delayed_job:
puts contact_email.subject
contact_email.date_sent = Date.today
contact_email.date_created = Date.today
contact_email.body = email.substituted_message(contact, contact.colleagues)
contact_email.status = "sent"
#Delayed::Job.enqueue OutboundMailer.deliver_campaign_email(contact,contact_email)
Delayed::Job.enqueue SomeMailJob.new(contact,contact_email)
contact_email.save #now save the record
Here is the some_mail_job.rb
class SomeMailJob < Struct.new(:contact, :contact_email)
def perform
OutboundMailer.deliver_campaign_email(contact,contact_email)
end
end
And here is the outbound_mailer:
class OutboundMailer < Postage::Mailer
def campaign_email(contact,email)
subject email.subject
recipients contact.email
from '<me#me.com>'
sent_on Date.today
body :email => email
end
You could update the status in the perform of the job itself.
For example, something like:
contact_email.status = 'queued'
contact_email.save
contact_email.delay.deliver_campaign_email
And then in your ContactEmail class, something to the effect of
def deliver_campaign_email
OutboundMailer.deliver_campaign_email(self.contact, self)
self.status = 'sent' # or handle failure and set it appropriately
self.save
end
delayed_job has some magic bits that it adds to your models that will deal with the persistence.
In order to deal with your OutboundMailer throwing an exception, you can do something like so:
def deliver_campaign_email
begin
OutboundMailer.deliver_campaign_email(self.contact, self)
self.status = 'sent'
rescue
self.status = 'failed' # or better yet grab the the message from the exception
end
self.save
end
You need synchronic delivery so stop using delayed job in this case and do standard mailer delivery.
or add success column to you ContactEmail - initialy save it with false then update in job to true
I would like to be able to send a string of emails at a determined interval to different recipients.
I assign to each Contact this series of Emails called a Campaign, where Campaign has Email1, Email2, etc. Each Contact has a Contact.start_date. Each Email has email.days which stores the number of days since a Contact's start-date to send the email.
For example: Email1.days=5, Email2.days=7, Email3.days=11
Contact1.start_date = 4/10/2010; contact2.start_date = 4/08/2010
IF today is 4/15, then Contact1 receives Email 1 (4/15-4/10 = 5 days)
IF today is 4/15, then Contact2 received Email 2 (4/15 - 4/8 = 7 days).
What's a good action to run every day using a cron job that would then follow these rules to send out emails using ActionMailer?
NOTE: The question isn't about using ActionMailer. It is about doing the "math" as well as the execution. Which email to send to whom? I am guessing it has to do with some version of Date - Contact[x].start_date and then compare against email[x].days but I'm not exactly clear how. Thanks.
I'd like guidance on whether to use date.today versus time.now as well.
Note: the intent is that an individual person may need to schedule individual follow-up on a consistent basis. Rather than having to remember when to follow up which email with whom, it would just follow a pre-determined campaign and send for that person.
So it's not a "bulk mail" -- it's really automating the follow-up for individual correspondence.
I would use DelayedJob for this ( assuming you are not sending large number of emails emails a day, i.e. 100's of thousands per day etc.)
class Email < ActiveRecord::Base
belongs_to :campaign
after_create :schedule_email_dispatch
def schedule_email_dispatch
send_at(campaign.created_at + self.days.days, :send_email)
end
def send_email
end
end
Run the workers using the rake task:
rake jobs:work
Every time a new Email object is created a delayed job item is added to the queue. At the correct interval the email will be sent by the worker.
#campaign = Compaign.new(...)
#campaign.emails.build(:days => 1)
#campaign.emails.build(:days => 2)
#campaign.save # now the delay
In the example above, two delayed job entries will be created after saving the campaign. They are executed 1 and 2 days after the creation date of the campaign.
This solution ensures emails are sent approximately around the expected schedule times. In a cron job based solution, disptaching happens at the cron intervals. There can be several hours delay between the intended dispatch time and the actual dispatch time.
If you want to use the cron approach do the following:
class Email < ActiveRecord::Base
def self.dispatch_emails
# find the emails due for dispatch
Email.all(:conditions => ["created_at <= DATE_SUB(?, INTERVAL days DAY)",
Time.now]).each do |email|
email.send_email
end
end
end
In this solution, most of the processing is done by the DB.
Add email.rake file in lib/tasks directory:
task :dispatch_emails => :environment do
Email.dispatch_emails
end
Configure the cron to execute rake dispatch_emails at regular intervals( in your case < 24 hours)
I would create a rake task in RAILS_ROOT/lib/tasks/email.rake
namespace :email do
desc "send emails to contacts"
task :send do
Email.all.each do |email|
# if start_date is a datetime or timestamp column
contacts = Contact.all(:conditions => ["DATE(start_date) = ?", email.days.days.ago.to_date])
# if start_date is a date column
contacts = Contact.all(:conditions => { :start_date => email.days.days.ago.to_date })
contacts.each do |contact|
#code to send the email
end
end
end
end
Then I would use a cronjob to call this rake task every day at 3 a.m.:
0 3 * * * app_user cd RAILS_APP_FOLDER && RAILS_ENV=production rake email:send
I think it would be much easier and more secure (you don't have to worry on authentication and so on) to create a rake task to send the emails. Also you don't have to worry about a possibly very long running request. Just create a file RAILS_ROOT/lib/tasks/email.rake
namespace :email do
desc "Sends scheduled emails"
task :send_scheduled => :enviroment do
Email.send_scheduled_emails
end
end
and in RAILS_ROOT/app/email.rb
class Email < ActiveRecord::Base
# ...
def self.send_scheduled_emails
#send your emails ...
end
end
Then create a cron job
0 0 * * * user cd /your/rails/app/ && RAILS_ENV=production rake emais:send_scheduled
to send the emails every night at 12:00.
I am using rufus-scheduler for scheduled email and twitter updates. You should check it.
I use ar_mailer gem
http://seattlerb.rubyforge.org/ar_mailer/
http://github.com/adzap/ar_mailer
http://blog.segment7.net/articles/2006/08/15/ar_mailer