unicorn Ruby/Rack server user+dev discussion/patches/pulls/bugs/help
 help / color / mirror / code / Atom feed
From: Eric Wong <normalperson@yhbt.net>
To: unicorn list <mongrel-unicorn@rubyforge.org>
Cc: Russell Branca <chewbranca@gmail.com>
Subject: Re: Forking off the unicorn master process to create a background worker
Date: Wed, 26 May 2010 14:05:42 -0700	[thread overview]
Message-ID: <20100526210542.GC24211@dcvr.yhbt.net> (raw)
In-Reply-To: <AANLkTinpzdLFf1bWBMRqDepIYQBKeZWb_mC0n-nlPhzD@mail.gmail.com>

Russell Branca <chewbranca@gmail.com> wrote:
> Hello,
> 
> I'm trying to find an efficient way to create a new instance of a
> rails application to perform some background tasks without having to
> load up the entire rails stack every time, so I figured forking off
> the master process would be a good way to go. Now I can easily just
> increment the worker count and then send a web request in, but the new
> worker would be part of the main worker pool, so in the time between
> spawning a new worker and sending the request, another request could
> have come in and snagged that worker. Is it possible to create a new
> worker and not have it enter the main worker pool so I could access it
> directly?

Hi Russell,

You could try having an endpoint in your webapp (with authentication, or
have it reject env['REMOTE_ADDR'] != '127.0.0.1') that runs the
background task for you.  Since it's a background app, you should
probably fork + Process.setsid + fork (or Process.daemon in 1.9), and
return an HTTP response immediately so your app can serve other
requests.

The following example should be enough to get you started (totally
untested)

------------ config.ru -------------
require 'rack/lobster'

map "/.seekrit_endpoint" do
  use Rack::ContentLength
  use Rack::ContentType, 'text/plain'
  run(lambda { |env|
    return [ 403, {}, [] ] if env['REMOTE_ADDR'] != '127.0.0.1'
    pid = fork
    if pid
      Process.waitpid(pid)

      # cheap way to avoid unintentional fd sharing with our children,
      # this causes the current Unicorn worker to exit after sending
      # the response:
      # Otherwise you'd have to be careful to disconnect+reconnect
      # databases/memcached/redis/whatever (in both the parent and
      # child) to avoid unintentional sharing that'll lead to headaches
      Process.kill(:QUIT, $$)

      [ 200, {}, [ "started background process\n" ] ]
    else
      # child, daemonize it so the unicorn master won't need to
      # reap it (that's the job of init)
      Process.setsid
      exit if fork

      begin
        # run your background code here instead of sleeping
        sleep 5
        env["rack.logger"].info "done sleeping"
      rescue => e
        env["rack.logger"].error(e.inspect)
      end
      # make sure we don't enter the normal response cycle back in the
      # worker...
      exit!(0)
    end
  })
end

map "/" do
  run Rack::Lobster.new
end

> I know this is not your typical use case for unicorn, and you're
> probably thinking there is a lot better ways to do this, however, I
> currently have a rails framework that powers a handful of standalone
> applications on a server with limited resources, and I'm trying to
> make a centralized queue that all the applications use, so the queue
> needs to be able to spawn a new worker for each of the applications
> efficiently, and incrementing/decrementing worker counts in unicorn is
> the most efficient way I've found to spawn a new rails instance.

Yeah, it's definitely an odd case and there are ways to shoot yourself
in the foot with it (especially with unintentional fd sharing), but Ruby
exposes all the Unix process management goodies better than most
languages (probably better than anything else I've used).

> Any help, suggestions or insight into this would be greatly appreciated.

Let us know how it goes :)

-- 
Eric Wong
_______________________________________________
Unicorn mailing list - mongrel-unicorn@rubyforge.org
http://rubyforge.org/mailman/listinfo/mongrel-unicorn
Do not quote signatures (like this one) or top post when replying


  reply	other threads:[~2010-05-26 21:11 UTC|newest]

Thread overview: 6+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-05-25 18:53 Forking off the unicorn master process to create a background worker Russell Branca
2010-05-26 21:05 ` Eric Wong [this message]
2010-06-15 17:55   ` Russell Branca
2010-06-15 22:14     ` Eric Wong
2010-06-15 22:51       ` Russell Branca
2010-06-16  0:06         ` Eric Wong

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://yhbt.net/unicorn/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20100526210542.GC24211@dcvr.yhbt.net \
    --to=normalperson@yhbt.net \
    --cc=chewbranca@gmail.com \
    --cc=mongrel-unicorn@rubyforge.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhbt.net/unicorn.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).