Jacob Patton
2006-Jul-14 00:22 UTC
[Backgroundrb-devel] Using BackgrounDRb to replace nested loops?
In response to my Rails Weenie post today[1], someone mentioned that
I try BackgrounDRb to help take the strain off my application.
1: http://rails.techno-weenie.net/question/2006/7/13/nested-loops-
best-way-to-send-messages-to-blog-subscribers
I''ve just tried to install and use BackgrounDRb with my app, but
I''m
afraid that I''m not too versed in running processes like these, and I
can''t get the caching to work--I get this error: private method
`cache'' called for #<BackgrounDRb::MiddleMan:
0x254f9b0>:BackgrounDRb::MiddleMan
Would someone help me by looking over my Rails Weenie question,
below, and recommend to me whether BackgrounDRb will help me with my
problem?
(Thanks for the great site, as always, Ezra, and it was good meeting
you after the Workshop for Good Event, too!)
From the Rails Weenie post:
I run a members-only blog where subscribers can opt to receive
messages in given categories via email in real-time or daily/weekly
digests. What?s the best way to deliver the digest messages without
pushing my app into an ugly, processor-hungry loop?
Each message belongs to a given category, and each subscriber can
subscribe to 0-n categories to receive updates. Right now the daily
emails are sent as soon as a message is posted:
1. # within the create action...after the message is saved:
2.
3. # get subscribers to this category
4. @subscribers = @msg.category.subscribers
5.
6. # loop through the subscribers, delivering the message to each
one
7. for subscriber in @subscribers
8. MsgNotifier.deliver_update(@msg)
9. end
This causes a slight delay upon posting--the message has to be sent
to all the subscribers for this category--but it works. (I''m not
terribly concerned about the delay in this part, for now, but if
anyone has a suggestion on how to improve things, I''d love to hear
it.) The real problem arises, however, when I''m trying to compile the
messages for daily and weekly digests:
1. # get all real-time subscribers
2. @subscribers = User.find_all_by_freq(''real-time'')
3.
4. # loop through subscribers
5. for subscriber in @subscribers
6.
7. @text = nil
8.
9. # get categories for subscriber
10. @categories = subscriber.categories
11.
12. # loop through categories
13. for category in @categories
14.
15. # get messages posted in that category in the past day
16. @msgs = category.messages(:conditions => "[created_at > ?],
1.day.ago")
17.
18. # loop through messages
19. for msg in @msgs
20.
21. # concatenate the messages for that category
22. @text += msg.body
23. end
24. end
25. end
26.
27. # deliver subscriber''s update containing all of the past days
messages
28. MsgNotifier.deliver_daily(@subscriber, @text)
29.
30. # unset @text
31. @text = nil
32. end
As you can see, this is really, really ugly. Is there a better way to
gather together the content for the digest messages? I''m open to any
suggestions--this code is very, very slow right now...
Best,
Jacob Patton
Ezra Zygmuntowicz
2006-Jul-14 03:11 UTC
[Backgroundrb-devel] Using BackgrounDRb to replace nested loops?
Hi- Answers inline: On Jul 13, 2006, at 5:22 PM, Jacob Patton wrote:> In response to my Rails Weenie post today[1], someone mentioned that > I try BackgrounDRb to help take the strain off my application. > > 1: http://rails.techno-weenie.net/question/2006/7/13/nested-loops- > best-way-to-send-messages-to-blog-subscribers > > I''ve just tried to install and use BackgrounDRb with my app, but I''m > afraid that I''m not too versed in running processes like these, and I > can''t get the caching to work--I get this error: private method > `cache'' called for #<BackgrounDRb::MiddleMan: > 0x254f9b0>:BackgrounDRb::MiddleManI apologize for this. A recent change introduced this bug. Please svn up or co the plugin again and try again. Caching will work as advertised now. But I don''t think caching is the answer to your problem. I think you would be best served by creating a worker class that did the compilation of messages and sent the emails in the backgroundrb server. I also think that every 5 or 10 minutes is real time enough for most things like this. So you might be better served by building and sending out the email digests at intervals and not with every time a post gets updated. So since you need to use ActionMailer, which in turn needs ActionView, you will want to pull most of rails into your drb server so you can make all this happen. So add this line to your script/ backgroundrb/start script at the top after the boot.rb require line: require File.dirname(__FILE__) + "/../../config/environment.rb" Then build a worker class that does the processing and sending of emails in the do_work method. class EmailWorker < BackgrounDRb::Rails def do_work(args) # loop over emails and build digest # loop over subscribers and send email # end end You will need a cron job to fire off this workers method every 5 or 10 minutes like this: #!/usr/bin/env ruby require "drb" DRb.start_service MiddleMan = DRbObject.new(nil, "druby://localhost:22222") # setting :ttl to 600 seconds/10 minutes. Make sure you set this for long enough time for your worker to finish the emails # but you need to set this so the workers will get garbage collected after they are done. MiddleMan.new_worker(:class => :email_worker, :ttl => 600) See if this gets you started and ask if you get stuck. Cheers- -Ezra> <snip code> > Best, > > Jacob Patton > _______________________________________________ > Backgroundrb-devel mailing list > Backgroundrb-devel at rubyforge.org > http://rubyforge.org/mailman/listinfo/backgroundrb-devel >
Jacob Patton
2006-Jul-17 14:09 UTC
[Backgroundrb-devel] Using BackgrounDRb to replace nested loops?
Ezra thanks for your help and advice. I''ll try out the steps you suggest, and I''ll report back to the list. Best, Jacob On Jul 13, 2006, at 11:11 PM, Ezra Zygmuntowicz wrote:> Hi- > > Answers inline: > > On Jul 13, 2006, at 5:22 PM, Jacob Patton wrote: > >> In response to my Rails Weenie post today[1], someone mentioned that >> I try BackgrounDRb to help take the strain off my application. >> >> 1: http://rails.techno-weenie.net/question/2006/7/13/nested-loops- >> best-way-to-send-messages-to-blog-subscribers >> >> I''ve just tried to install and use BackgrounDRb with my app, but I''m >> afraid that I''m not too versed in running processes like these, and I >> can''t get the caching to work--I get this error: private method >> `cache'' called for #<BackgrounDRb::MiddleMan: >> 0x254f9b0>:BackgrounDRb::MiddleMan > > I apologize for this. A recent change introduced this bug. Please > svn up or co the plugin again and try again. Caching will work as > advertised now. But I don''t think caching is the answer to your > problem. > > I think you would be best served by creating a worker class that > did the compilation of messages and sent the emails in the > backgroundrb server. I also think that every 5 or 10 minutes is > real time enough for most things like this. So you might be better > served by building and sending out the email digests at intervals > and not with every time a post gets updated. > > So since you need to use ActionMailer, which in turn needs > ActionView, you will want to pull most of rails into your drb > server so you can make all this happen. So add this line to your > script/backgroundrb/start script at the top after the boot.rb > require line: > > require File.dirname(__FILE__) + "/../../config/environment.rb" > > Then build a worker class that does the processing and sending of > emails in the do_work method. > > class EmailWorker < BackgrounDRb::Rails > > def do_work(args) > # loop over emails and build digest > # loop over subscribers and send email > # > end > > end > > You will need a cron job to fire off this workers method every 5 or > 10 minutes like this: > > #!/usr/bin/env ruby > require "drb" > DRb.start_service > MiddleMan = DRbObject.new(nil, "druby://localhost:22222") > # setting :ttl to 600 seconds/10 minutes. Make sure you set this > for long enough time for your worker to finish the emails > # but you need to set this so the workers will get garbage > collected after they are done. > MiddleMan.new_worker(:class => :email_worker, :ttl => 600) > > > > See if this gets you started and ask if you get stuck. > > Cheers- > -Ezra > > >> <snip code> >> Best, >> >> Jacob Patton >> _______________________________________________ >> Backgroundrb-devel mailing list >> Backgroundrb-devel at rubyforge.org >> http://rubyforge.org/mailman/listinfo/backgroundrb-devel >> >
Jacob Patton
2006-Jul-18 18:24 UTC
[Backgroundrb-devel] Using BackgrounDRb to replace nested loops?
Hi Ezra, Thanks for your advice regarding using BackgrounDRb to handle long- running emailing processes for my blog application. I''m having a little trouble getting BackgrounDRb to work--I''m just using it to post a message to the logs for now, to make sure I can get the code running. My questions are inline:> [You said:] I think you would be best served by creating a worker > class that did the compilation of messages and sent the emails in > the backgroundrb server. I also think that every 5 or 10 minutes is > real time enough for most things like this. So you might be better > served by building and sending out the email digests at intervals > and not with every time a post gets updated. > > So since you need to use ActionMailer, which in turn needs > ActionView, you will want to pull most of rails into your drb > server so you can make all this happen. So add this line to your > script/backgroundrb/start script at the top after the boot.rb > require line: > > require File.dirname(__FILE__) + "/../../config/environment.rb"I''ve done this...> Then build a worker class that does the processing and sending of > emails in the do_work method. > > class EmailWorker < BackgrounDRb::Rails > > def do_work(args) > # loop over emails and build digest > # loop over subscribers and send email > # > end > > endI did this, too, but my file looks like: # in lib/workers/checker_worker.rb class CheckerWorker < BackgrounDRb::Rails def do_work(args) logger.debug "BackgrounDRb run at #{Time.now}" end end> You will need a cron job to fire off this workers method every 5 or > 10 minutes like this: > > #!/usr/bin/env ruby > require "drb" > DRb.start_service > MiddleMan = DRbObject.new(nil, "druby://localhost:22222")Here is the file I''m using # in lib/checker.rb #!/usr/bin/env ruby require "drb" DRb.start_service MiddleMan = DRbObject.new(nil, "druby://localhost:22222") MiddleMan.new_worker(:class => :checker_worker, :ttl => 600) I think I''ve set up everything correctly, and that by running checker.rb by typing ruby checker.rb while within the lib directory, the BackgrounDRb object should fire, and the debug message should be posted to the log. When I run the command, though, I get this error message: (druby://localhost:22222) /usr/local/lib/ruby/gems/1.8/gems/ activesupport-1.3.1/ lib/active_support/dependencies.rb:100:in `const_missing'': uninitialized constan t CheckerWorker (NameError) ... Can you, Ezra, or anyone else help me discover where I''ve gone wrong? Thanks very much for your help, Jacob Patton
Ezra Zygmuntowicz
2006-Jul-18 19:14 UTC
[Backgroundrb-devel] Using BackgrounDRb to replace nested loops?
On Jul 18, 2006, at 11:24 AM, Jacob Patton wrote:> Hi Ezra, > > Thanks for your advice regarding using BackgrounDRb to handle long- > running emailing processes for my blog application. > > I''m having a little trouble getting BackgrounDRb to work--I''m just > using it to post a message to the logs for now, to make sure I can > get the code running. > > My questions are inline: > >> [You said:] I think you would be best served by creating a worker >> class that did the compilation of messages and sent the emails in >> the backgroundrb server. I also think that every 5 or 10 minutes >> is real time enough for most things like this. So you might be >> better served by building and sending out the email digests at >> intervals and not with every time a post gets updated. >> >> So since you need to use ActionMailer, which in turn needs >> ActionView, you will want to pull most of rails into your drb >> server so you can make all this happen. So add this line to your >> script/backgroundrb/start script at the top after the boot.rb >> require line: >> >> require File.dirname(__FILE__) + "/../../config/environment.rb" > > I''ve done this... > >> Then build a worker class that does the processing and sending of >> emails in the do_work method. >> >> class EmailWorker < BackgrounDRb::Rails >> >> def do_work(args) >> # loop over emails and build digest >> # loop over subscribers and send email >> # >> end >> >> end > > I did this, too, but my file looks like: > > # in lib/workers/checker_worker.rb > > class CheckerWorker < BackgrounDRb::Rails > > def do_work(args) > logger.debug "BackgrounDRb run at #{Time.now}"needs to be @logger> end > > end > >> You will need a cron job to fire off this workers method every 5 >> or 10 minutes like this: >> >> #!/usr/bin/env ruby >> require "drb" >> DRb.start_service >> MiddleMan = DRbObject.new(nil, "druby://localhost:22222") > > Here is the file I''m using > > # in lib/checker.rb > > #!/usr/bin/env ruby > require "drb" > DRb.start_service > MiddleMan = DRbObject.new(nil, "druby://localhost:22222") > MiddleMan.new_worker(:class => :checker_worker, :ttl => 600) > > I think I''ve set up everything correctly, and that by running > checker.rb by typing ruby checker.rb while within the lib > directory, the BackgrounDRb object should fire, and the debug > message should be posted to the log. > > When I run the command, though, I get this error message: > > (druby://localhost:22222) /usr/local/lib/ruby/gems/1.8/gems/ > activesupport-1.3.1/ > lib/active_support/dependencies.rb:100:in `const_missing'': > uninitialized constan > t CheckerWorker (NameError) > ...Hrmm.. I wonder if this is caused when you require the config/ environment.rb file? Can you test that for me? comment out the require File.dirname(__FILE__) + "/../../config/environment.rb" line and stop/start the drb server. Then try this again and see if you get errors. Also you need to realize that Backgroundrb requires all your worker classes in lib/workers when it starts up. So if you didn''t restart the drb server after you put the checker_worker.rb file in lib/ workers then that would cause this exact problem. So first restart your drb server. If that doesnt fix it then try what I stated above about the environemtn.rb require. Then report back if you still have errors. THanks -Ezra> > Can you, Ezra, or anyone else help me discover where I''ve gone wrong? > > Thanks very much for your help, > > Jacob Patton