#171
Jul 20, 2009

Delayed Job

Is there a long running task which should be handled in the background? One of the best ways is using the delayed_job plugin like I show in this episode.
Tags: plugins
Download (17.8 MB, 10:56)
alternative download for iPod & Apple TV (12.8 MB, 10:56)

Resources

script/plugin install git://github.com/collectiveidea/delayed_job.git
script/generate delayed_job
rake db:migrate
rake jobs:work
# mailings_controller.rb
mailing = Mailing.find(mailing_id)
mailing.send_later(:deliver)

# or
Delayed::Job.enqueue(MailingJob.new(params[:id]), -3, 3.days.from_now)

# lib/mailing_job.rb
class MailingJob < Struct.new(:mailing_id)
  def perform
    mailing = Mailing.find(mailing_id)
    mailing.deliver
  end
end

RSS Feed for Episode Comments 35 comments

1. Luke Jul 20, 2009 at 00:18

Awesometastic as always! :) Thx Ryan for your hard work!


2. Vlad Zloteanu Jul 20, 2009 at 01:00

Ryan,

Can you comment on why is DelayedJob better than BackgroundRb or Workling?

Here is a great resource on handling bg file upload on Amazon S3 with starling/workling: http://aaronvb.com/blog/2009/7/19/paperclip-amazon-s3-background-upload-using-starling-and-workling


3. Igor Jul 20, 2009 at 01:04

I've been using workling, backgroundDRb, but delayed_job is much better, it just works without any problems

thanks


4. Michael Jul 20, 2009 at 04:03

Thanks a lot for the awesome, handy and easy screencast!


5. @andrewkalek Jul 20, 2009 at 06:32

Great Screencast, I love how you always do screencasts on the features I'm looking for ;)


6. Ryan Bates Jul 20, 2009 at 07:14

@Vlad, delayed_job is simpler than both Workling and BackgroundRb. It can also scale really well (it's used on GitHub). And overall it feels more stable.


7. Larry Sprock Jul 20, 2009 at 07:26

THANK YOU!!! I needed this right now! I have been having so many problems with workling lately and it seems as though the developers have abandon support. I was working on a fix at this very moment and here it is! I love that this is the queue and worker all rolled into one!


8. minikin Jul 20, 2009 at 08:44

thank you!


9. Michael Hasenstein Jul 20, 2009 at 09:38

The one piece of information I was looking for: how are the workers informed about jobs - actively by asking (polling), or passively by being called? Turns out it's polling - the SAME as all the other options I've seen.

That makes it just as bad as all those other options. On a 2009(!) computer system it should be possible to implement something like that WITHOUT wasting CPU cycles on polling.

Why can't the piece of code that pushes the new job information into the database (delayed job) or into memcache (workling/starling) also directly wake a thus far sleeping worker? Why does everyone only write code that actively asks "is there new work for me?" "is there new work for me?" "is there new work for me?" as often as several times per second (here, delayed job, uses 5s by default I just read)?

Okay, on a modern CPU one might argue that it really does not matter. However, what troubles me is that the same kind of "oh it does not matter in the grand scheme of things" is applied a little too often - which is why today we need so much CPU power in the first place to run a library using a library using a library using a library running in a virtual system in a virtual machine in a virtual machine (this term does not only apply to vmware/xen/etc.) fully simulated PCs).

Yes, I'm ranting. I've just been informed about the n+1-th "even greater" background job processing plugin for rails - which STILL uses polling.


10. Brian Armstrong Jul 20, 2009 at 11:57

Nice!

If anyone has additional info on one of the following, please post it!

1. saving how far the job made it so that it can resume from a failure without duplicating work (particularly useful for newsletters as Ryan mentioned)

2. running the delayed_job worker process from a different server

Thanks!
Brian


11. taelor Jul 20, 2009 at 12:31

Ryan,

I have been watching this screencast series for along time now, but you still keep nailing episodes. This is no exception.

Thanks for the Structs, never seen that before.


12. iGEL Jul 20, 2009 at 12:39

It's really useful. Thank you!

But I'll guess, I'm gonna create a app/jobs directory instead using the lib dir.


13. iGEL Jul 20, 2009 at 13:03

Is it possible to create reoccurring jobs like for every hour?


14. Ralph Jul 20, 2009 at 16:15

Ryan, great episode. I've been using Spawn:

http://github.com/tra/spawn/tree/master

It's quite a bit easier to set up and use compared to delayed_job - no migration and no rake task necessary. I was wondering, what do you see as the main advantages of delayed_job over Spawn?


15. Agustin Jul 20, 2009 at 21:56

pretty good screen cast as always, thanks!


16. John Jul 21, 2009 at 00:40

You can also use the following to invoke Rake tasks from Delayed Job.

http://geminstallthat.wordpress.com/2008/02/25/run-rake-tasks-with-delayedjob-dj/


17. Evan Jul 21, 2009 at 01:13

Hi Ryan/All,

I've been working on some background tasks lately and was hoping delayed_job would be a solution, but it doesn't seem like it will be.

1) Does delayed_job handle job dependencies? Sometimes you need to spawn 2 jobs, where job 2 depends on job 1 being completed

2) How does delayed_job deal with concurrency, specifically in the development environment using SQLite3? We've found issues using SQLite3 with our current background job framework. So I'm wondering what will happen if you have some background jobs that actively query/update active record classes? I would expect SQLite::BusyExceptions to occur in heavy traffic/many job scenarios.

3) I also share the question from Brian about the ability to run jobs on a remote server. Our current solution is to have the job itself invoke the commands on the remote server, and then collect the results via POpen4.

Thanks for any additional info. I would love to use a community supported gem to handle my background jobs, but our home baked solution is the only thing that has worked so far.


18. Pierre Jul 23, 2009 at 02:33

I have been using Workling+Sparkling for a while in production and I have just tested DelayedJob. D.J is really easier to use and run. I would recommend that.


19. Ivan Jul 23, 2009 at 20:35

Hello Ryan,

I've tried starting up the job processes in production with the command:

script/delayed_job -e production -n 1 start

but I get this error:

/usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': no such file to load -- daemons (LoadError)
from /usr/local/lib/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from ./script/../vendor/plugins/delayed_job/lib/delayed/command.rb:2
from script/delayed_job:6:in `require'
from script/delayed_job:6

any ideas on how to fix it?

Thanks a lot.

Ivan


20. Carl Jul 23, 2009 at 22:54

@Michael H.

I think the reason it doesn't work that way is that they are trying to separate the different processes. Waking a process would require the different processes to know about each other, and this way they all work independently of each other. The amount of cpu time consumed by each process to poll when there is no work is really insignificant. The resources consumed when they are actually doing something is far more important, and that wouldn't be improved by a 'wake on demand' system. Also, the more direct interaction you have between the different processes, the greater the fragility of the whole system.

There are a few good reasons all of these systems all use this polling method. Your question is a good one, but there are reasons they are done this way in general. For certain specific cases, a 'wake on demand' system will make more sense, but in general, a polling system is going to be better.


21. Ryan Jul 24, 2009 at 01:07

Is there no built in way to let the user know when a job has finished processing?

Great tutorial Cheers

Ryan T


22. James R. Jul 26, 2009 at 18:57

Can anyone provide an example of using delayed_job with ActionMailer (deliver method with params)?


23. Patrick Tulskie Jul 27, 2009 at 10:43

@James R. I think that would just be send_later(:deliver, your_params_here). That would be the same as Object.deliver(your_params_here). Is that what you were asking?


24. chebyte Jul 31, 2009 at 23:06

Hi guys, nice screen but I prefer job_fu more clean and easier

http://github.com/jnstq/job_fu/tree/master

cheers!


25. Nathan L. Walls Aug 01, 2009 at 13:15

Ryan;

Thanks for covering Delayed Job.

I spent a few evenings trying to get workling/starling up (based on an earlier Railscast) and running but ultimately passed on completing the work.

Delayed Job fit in nicely to my existing, was easy to deploy. Taken care of in a couple of hours.


26. Gavin Laking Aug 03, 2009 at 07:40

@tiffanyjewelry, @Ivan: You need to install the daemon gem:

sudo gem install daemons


27. Martin Aug 05, 2009 at 03:11

Awesome work again Ryan!

Ryan, could you make a cast that combines delayed_job with multiple ajax actions (redirects or updates)?

See my problem is the following: In the index action of the controller I show all entries of a database. Background jobs are started within the index action. Now on every entry listed I have a .gif that shows loading. After a job is finished this .gif image should be replaced by an arrow image for success or by a X image for failed.

Now obviosly I would have to call ajax for every lib job, but umfortunately I wasn't able to achive this until now cause its tricky or probably not possible to call an action or a rjs file from outside the controller itself that would replace the corresponding div id.

Would be nice to see how you would solve that!


28. Stian Berg Aug 14, 2009 at 12:43

Does anyone know how to get the "worker" started automatically instead of having to type "rake jobs:work" in a console? Ie. make it start when the server starts or something.

Nice tutorial btw, it was exactly what I was looking for! :-)


29. Anderson Dias Aug 14, 2009 at 16:24

I just created a plug in that uses delayed_job to send emails asynchronously by default.
Check it out at http://github.com/andersondias/delayed_job_mailer


30. Andrew Boag Aug 17, 2009 at 05:16

Thanks Ryan. Great cast as always.

This looks like a very useful bit of kit for queue processing.

I think it might have paid to mention how often queue processing logic gets complicated (no mater how stable of scalable the technical implication of the queue processor is). It just does in the real world.

I think it would have been good to talk at least a little about the best way to execute the queue processor script itself.

While the daemon approach will probably give you slicker results, there is quite a bit of hidden overhead with daemons on production systems.

 - they HAVE to be running. There has to be a monitoring test to make sure they're running.
 - they must gracefully start and stop with the system. We have to be sure that this is going to work.
 - nice if they handle non-graceful shutdown of server (i.e. power failure). Often there is a lockfile/pidfile somewhere that may prevent this.

We all do a lot of web development where this side of a server's BAU behaviour is hidden behind tried and tested daemon applications (Apache, MySQL, MTAs) that take care of all of this for us.

Although a little more primative, a cron job might be a better option for those who are not ready to be writing init scripts on a production server.

Thanks


31. zhon Aug 21, 2009 at 17:24

Great 'Cast as always. It looks like your source code is pointing to episode 170 rather than 171.


32. Mark Dodwell Sep 30, 2009 at 16:32

I used delayed_job to process resizing in the background on Heroku:

http://blog.madeofcode.com/post/201282903/paperclip-s3-delayed-job-in-rails


33. Philip Oct 08, 2009 at 17:18

Hello Ryan,

I saw that you have now several episodes on background-processing options: system call+rake, whenever/cron + rake/runner, starling+workling and now delayed_job. Would you have one on BackgroundJob and how it fares against all these options you surveyed? It will be a great summary comparison reference. Besides, even though delayed_job is getting popular (e.g. github and what not), it seems that BackgroundJob is also heartily recommended (e.g. EngineYard) among the serious, production-ready contenders. It would be great to see what your take is on this. Thanks.


34. Dave Oct 22, 2009 at 17:15

Hi

How can I determine if a job is finished ? Ideally there would be a way for my rails app to - upon request, determine if a specific job is complete. It seems to me that in order to do that you would need to get some unique id upon creating a delayed job, return that to the user - who could then pass that id to the app and ask 'is "this" (aka insert delayed job id here) job done yet ?"

Is this possible and if so - how ?

Thanks


35. Michael Nov 05, 2009 at 20:52

I noticed that I was getting the error nil.now when starting the rake jobs:work so I had to apply the patch from this link

http://github.com/kossnocorp/delayed_job/blob/master/lib/delayed/job.rb#L251


36. Filip Tepper Nov 24, 2009 at 07:59

Anyone experiencing issues with model.update_attributes command inside delayed jobs? I can't seem to get them working.


37. Marc Dec 01, 2009 at 06:53

Hey Ryan,

I'm using a hosting company (Dreamhost) that doesn't allow me running daemons in the background all the time. Eventually the just stop the processes. What would my options be to run actionmailer in the background without a daemon running?

thanks so much!


38. BuGo Dec 30, 2009 at 01:31

Hi, Ryan
I want to ask what problems may occur if when i need to do some long lasting job i create new Thread and let it handle all the work and i continue with my request. So basically:

action start
Thread.new{long action}
action end


39. einar Jan 15, 2010 at 07:20

Hello, thanks for the cast :)
Unfortunately i get the following error:

can't dump anonymous class Class
i have the following code:
def datei_speichern(pfad)
   name = File.basename(pfad)
   teile = pfad.split("?")
   puts teile.size
   puts pfad
   if teile.size == 2
   puts "PHP-Skript"
   else
   if db_vergleich_neu(name)
datei = @agent.get(pfad)
datei.save_as('l://trebble//teufel//' + @titel + "/" + name)
end
end
rescue
puts "fehler"
end
handle_asynchronously :datei_speichern

Any Idea whats wrong?


40. Mikhail Jan 25, 2010 at 17:07

einar, I have such error to:
can't dump anonymous class Class (TypeError)

I fix it. See http://d.hatena.ne.jp/milk1000cc/20091002/1254442030

Seems need remove any code under initialize to perform function in job

Error code:
class CrawlJob
  def initialize
    @agent = WWW::Mechanize.new
  end

  def perform
    ...
  end
end

True code:
class CrawlJob
  def perform
    @agent = WWW::Mechanize.new
    ...
  end
end


41. Mikhail Jan 27, 2010 at 16:58

Oh... It is not work on lib folder. On controller it is work ok


42. Midwire Mar 04, 2010 at 14:51

FYI: send_later will not work when calling it on an ActionMailer class, when using the collectiveidea version of delayed_job. If you want to call something like UserMailer.send_later ... you can use Tobi's original version of the gem: http://github.com/tobi/delayed_job

This likely changed since last year when Ryan did this screencast.

PS: Thanks Ryan for the awesome work you do!


43. pduersteler Mar 08, 2010 at 14:41

I'm just wondering.. how would it look like when you want to notify the user if the task was done successful or not? I miss a link between the jobs table and my table. Clearly, i could do something like that myself but I'm wondering if someone has an elegant solution.


44. laugart Mar 21, 2010 at 14:02

Hi

How can I determine if a job is finished ?


45. Kenny Johnston May 26, 2010 at 17:45

The delayed_job plugin is amazing, I use it in several apps. The biggest problem with it is the lack of monitoring of the daemonized process and the jobs themselves.

To address these issues I wrote the delayed_job_tracer plugin, which will monitor your delayed job process and send you an e-mail if delayed_job crashes or if one of its jobs fail.

Here's an article that describes how it works:

http://modernagility.com/articles/5-monitoring-delayed_job-and-its-jobs


46. StuFF mc Jun 16, 2010 at 04:52

Ryan, it might be interesing to modify your example

mailing.send_later(:deliver)

is deprecated. It looks like you now have to use

mailing.delay.deliver

Also for those who asked how to know when the job is finished one solution is to "do something" (in my case, call *again* the delay to restart again) at the end of the delayed method.


47. free directory list Aug 11, 2010 at 22:36

Keep the Rails 3 intro-casts coming, Ryan! Super great stuff.


48. Srikanth Aug 19, 2010 at 04:37

Thanks Ryan :-) very helpful..


49. wholesale new era caps Aug 20, 2010 at 20:29

I recently came across your blog and have been reading along.
I thought I would leave my first comment. I don’t know what to say except that I have enjoyed reading.Nice blog,I will keep visiting this blog very often.


50. jordan retro shoes Aug 23, 2010 at 23:06

Thanks a lot for the awesome, handy and easy screencast! Now obviosly I would have to call ajax for every lib job, but umfortunately I wasn't able to achive this until now cause its tricky or probably not possible to call an action or a rjs file from outside the controller itself that would replace the corresponding div id.


51. Wholesale Electronics Aug 25, 2010 at 01:48

Discount Wholesale Electronics, Wholesale Cell Phones, Electronic Gadgets and More from the Best Dropship Wholesaler


52. louis vuitton shoes Aug 26, 2010 at 23:20

Thanks for sharing your article. I really enjoyed it. I put a link to my site to here so other people can read it. My readers have about the same interets


53. Computer Assembly Parts Aug 30, 2010 at 20:21

thanks for your post, it is nice and i like it very much.


54. snow boots Aug 30, 2010 at 20:53

Great Screencast, I love how you always do screencasts on the features I'm looking for ;)


55. levis belts Sep 01, 2010 at 21:12

I agree with your Blog and I will be back to check it more in the future so please keep up your work. I love your content & the way that you write. It looks like you’ve been doing this for a while now, how long have you been blogging for?

Add your comment:

(SKIP THIS ONE)

(required)

(not shown)


(use pastie or gist for code)

sponsored by:
if you want to help:
required:
Get Quicktime Player
Give Back to Open Source