1

Following Bate's Railscast (http://railscasts.com/episodes/127-rake-in-background) on background tasks, trying it out with some mailers, not working out for me.

Application controller:

def call_rake(task, options = {})
  options[:rails_env] ||= Rails.env
  args = options.map { |n, v| "#{n.to_s}='#{v}'" }
  system "rake #{task} #{args.join(' ')} --trace 2>&1 >> #{Rails.root}/log/rake.log &"
  # need to add path to rake /usr/bin/rake, etc.
end

Request controller:

call_rake :connect_email, :requestrecord => @requestrecord, :lender_array => @lender_array, :item => item, :quantity => quantity

Rake connect_email.rb:

desc "Send email to connect borrower/lender pairs for items found"

task :connect_email => :environment do 
    RequestMailer.found_email(requestrecord, lender_array, item, quantity).deliver unless     lender_array.blank?
end

Am getting error NameError: undefined local variable or methodlender_array' for main:Object. Funny thing is, on a different mailer where the same parameters are passed except forlender_array, I get an error messageNameError: undefined local variable or method requestrecord' for main:Object.

Any thoughts on why?

And since it's my first time doing background tasks, would love to get additional insight from smart people on:

  1. If the environment is being loaded every time this task runs, then isn't that terribly expensive? I mean that means that the environment could be re-loaded several times per hour...
  2. Ryan mentions it's a good idea to specific the path to rake, e.g., /usr/bin/rake, how do I find what path this is on my local machine and also in production? (using Heroku)
  3. Is it correct to say that what's happening is that Rails is queuing up a list of Rake tasks in the order that they're called by the controller and then once the request is done, just executing them one-by-one?
  4. None of these error messages are registering in the rake.log file I created. Does it only register when the action is completed (i.e., a success)?

I'm just thinking, if person A does the action that calls the rake emails, and then person B does that same action that calls the rake emails a few miliseconds later, will Rails first execute the rake tasks for person A then do the person B request, then do person B's rake tasks, or will it do person A request, person B request, and then do the rake tasks in order received, whenever there is downtime from actual requests?

Thanks!

james
  • 3,989
  • 8
  • 47
  • 102

1 Answers1

0

There are many questions in the problem, I'll first tell you the problem with your code, when you run the rake task, the code that is executed is:

task :connect_email => :environment do 
  RequestMailer.found_email(requestrecord, lender_array, item, quantity).deliver unless     lender_array.blank?
end

so when that code is executed, requesterecord, lender_array, item and quantity are not defined. That is pure ruby code. If you check the screencast in detail, what it pass to the rake task are environment variables/values, not ruby variables, the right code would be:

task :connect_email => :environment do 
  RequestMailer.found_email(ENV["requestrecord"], ENV["lender_array"], ENV["item"], ENV["quantity"]).deliver unless     lender_array.blank?
end

Now, if you do that code, it won't work either, because all the environment variables are received as String by ruby, so you have to convert them to the desired kind of variable. If you call the code:

call_rake :connect_email, lender_array: ['hello', 'world']

it will set the environment variable lender_array to '["hello", "world"]', which will be received by the connect_email task as a ruby string, if you want to convert it to a Ruby Array, you can do something like:

RequestMailer.found_email(ENV["requestrecord"], eval(ENV["lender_array"]))

Because if you eval the string '["hello", "world"]', it will be evaluated as a Ruby Array.


  1. If the environment is being loaded every time this task runs, then isn't that terribly expensive? I mean that means that the environment could be re-loaded several times per hour...

A: Yes, every time you run a task, it will initialize rails from zero in a new process, and yes, it is expensive.

  1. Ryan mentions it's a good idea to specific the path to rake, e.g., /usr/bin/rake, how do I find what path this is on my local machine and also in production? (using Heroku)

A: You can setup a Environment Variable for that, its pretty common to setup Environment Variables in heroku, i.e., you can setup the environment variable RAKE_PATH="/usr/bin/rake" and then on your code you can do:

system "#{ENV['RAKE_PATH'} #{task} #{args.join(' ')} --trace 2>&1 >> #{Rails.root}/log/rake.log &"
  1. Is it correct to say that what's happening is that Rails is queuing up a list of Rake tasks in the order that they're called by the controller and then once the request is done, just executing them one-by-one?

A: No, every time you run a rake task, is a different process being created to run that specific task, not queue at all.

  1. None of these error messages are registering in the rake.log file I created. Does it only register when the action is completed (i.e., a success)?

A: Rake tasks usually logs information, I'm not sure why that exception is not being logged dough, but I usually check the logs and I see the rake tasks running and logging to them.


In order to be able to perform better your tasks on background, you can check delayed_job, which stores the jobs to be run on a database, and then a daemon fetch the tasks and run them in background.

rorra
  • 9,593
  • 3
  • 39
  • 61
  • Whew thanks this is amazingly comprehensive! I'll try the code now, but just a quick follow up on your explanations. So if the rake task is a new process that spins up the environment all over again, is this really the most optimal way to run a background task (especially since I have several mailers)? Or is there something else I should consider completely (or at the very least, spin the environment up one time to run all these mailer tasks)? On the Rake path question, how do I find what the path is? (i.e., before setting it equal to a constant) – james Jul 07 '14 at 04:02
  • Also, with the new `eval()` code, for one mailer I got the same error, for another I got: `TypeError: no implicit conversion of nil into String` am going to do some reading on passing objects thru... – james Jul 07 '14 at 04:13
  • You look for it on your own and you set it on your own, its a operating system task, not really related to ruby, there is not a syntax or something in ruby where you can ask what is the right rake path. – rorra Jul 07 '14 at 17:26
  • Also if you check the screencast, it doesn't pass Objects, like what you pass to the rake task are environment variables, like strings, integers, but not Objects like item, instead of sending item, send the item id like in the screencast. – rorra Jul 07 '14 at 17:27
  • Yeah, are you saying passing Objects is impossible? Because if so, I'll need to change more than just the rake task (i.e., right now my mailers are using the objects to pull various attributes, all of which I now have to set to a string parameter if the rake task can't be passed an Object) – james Jul 07 '14 at 18:09
  • Its not, you can serialize objects into string and get them back, just google for ruby object serialization, but right now what its being passed is not the serialized object, but the result of object.to_s – rorra Jul 07 '14 at 18:44
  • Either way I think that if you explore delajed_job, that gem will solve all your problems – rorra Jul 07 '14 at 18:44
  • Ha I have another question open about that, and whether the gem is better than just using a rake task. Thanks! – james Jul 07 '14 at 18:55