Single use forks

105 views
Skip to first unread message

SDQL

unread,
Apr 18, 2012, 1:49:37 PM4/18/12
to Tornado Web Server
Hi,

Is it possible to use a new fork for each client request?

My problem is that some client requests require a lot of RAM which is
not recovered after the request finishes.

Thanks in advance for any clues!


Joe

Ben Darnell

unread,
Apr 18, 2012, 6:33:59 PM4/18/12
to python-...@googlegroups.com
There's not a way to do this out of the box - forking per-request
isn't really compatible with the idea of a shared event loop. If you
want to use the full IOLoop stack you'll need to devise some way
(probably using the multiprocessing module) to farm work out to a
child process. If you don't need IOLoop you may be able to use
WSGIApplication in a fork-based WSGI container.

-Ben

Samuel

unread,
Apr 19, 2012, 2:10:02 PM4/19/12
to Tornado Web Server
I don't know if my approach is the best but I choose to use 2 tornado
server instances. One for very light weight requests or ones that can
be easily async'ed. Then I have another process based tornado
server. For my lightweight instance for calls I deem long running I
make an async call to the process server and wait for the response.

Personally I strongly dislike maintaining two separate servers like
this, but honestly it works like a champ. I get the best of both
worlds even if it is a little bit more to manage.

Regards,
Samuel

Shane Spencer

unread,
Apr 19, 2012, 7:19:01 PM4/19/12
to python-...@googlegroups.com
I have found appreciation of inetd for this task.

SDQL

unread,
Apr 20, 2012, 11:53:47 AM4/20/12
to Tornado Web Server
Tornado friends,

Thanks for the good thoughts.

My solution to the problem of RAM accumulation in long running process
is to kill forks that use more than (eg) 20% of their starting RAM
with:

def on_finish(self):
if resource.getrusage(resource.RUSAGE_SELF).ru_maxrss > 1.2 *
START_RU_MAXRSS:
sys.exit(1)

Where I define the global START_RU_MAXRSS on spin up.

Since I exit with a non zero signal, tornado is kind enough to restart
a fork for me.

In order to have this work past the default of 100 restarts, I added a
max_restarts parameter to netutils.start and modified one line in
process.fork_process to read:

if max_restarts and num_restarts > max_restarts:
raise RuntimeError("Too many child restarts, giving up")

So that when I start my server like this:

server.start(options.forks,max_restarts=None)

it spawns new forks forever.

It seems kind of bad to use sys.exit(1) like this but it works great
so far!

Cheers,

Joe

http://SportsDataBase.com

jeremy

unread,
Apr 20, 2012, 3:55:45 PM4/20/12
to python-...@googlegroups.com
Joe,

This is an interesting idea, but wouldn't it kill any jobs on the
ioloop prior to their callback and therefore prematurely close
connections?

Just curious. Am I missing something?

-jeremy

--
jeremy kelley - ceo, nerd, etc.
snoball - encouraging a lifestyle of giving.

e: jer...@snoball.com
o: 1.855.sno.fort
c: 979.820.8202
t: @nod

SDQL

unread,
Apr 21, 2012, 2:15:19 PM4/21/12
to Tornado Web Server
Good thinking!

From earlier discussion here, this looks like a more graceful way to
restart a bloated tornado fork:

def on_finish(self):
if resource.getrusage(resource.RUSAGE_SELF).ru_maxrss > 1.2 *
START_RU_MAXRSS:
server.stop()
tornado.ioloop.IOLoop.instance().add_timeout( time.time()
+ SPINDOWN_TIME, lambda :sys.exit(1) )


Cheers,

Joe

http://SportsDataBase.com
Reply all
Reply to author
Forward
0 new messages