Troubles with Threading

250 views
Skip to first unread message

Creotiv

unread,
Jan 1, 2010, 4:24:31 PM1/1/10
to Tornado Web Server
I made some wrapper for controller methods that run them in threads.
But when i test this with ab and create high load it throws errors:

Exception in thread Thread-8:
Traceback (most recent call last):
File "/usr/lib/python2.6/threading.py", line 525, in
__bootstrap_inner
self.run()
File "./server.py", line 53, in run
func()
File "./server.py", line 203, in index
self.finish()
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
request.py", line 298, in finish
self.flush(include_footers=True)
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
request.py", line 272, in flush
self.request.write(headers + chunk)
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
httpserver.py", line 425, in write
self.connection.write(chunk)
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
httpserver.py", line 263, in write
self.stream.write(chunk, self._on_write_complete)
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
iostream.py", line 111, in write
self._check_closed()
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
iostream.py", line 223, in _check_closed
raise IOError("Stream is closed")
IOError: Stream is closed

ERROR:root:Exception in I/O handler for fd 24
Traceback (most recent call last):
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
ioloop.py", line 195, in start
self._handlers[fd](fd, events)
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
iostream.py", line 161, in _handle_events
self.io_loop.update_handler(self.socket.fileno(), self._state)
File "/home/creotiv/tornado-app/branches/andrew/lib/tornado/
ioloop.py", line 132, in update_handler
self._impl.modify(fd, events | self.ERROR)
IOError: [Errno 9] Bad file descriptor

in thread i run this method:

def get(self):
self.set_header("Content-Type", "text/html;
charset=UTF-8")
#c.type = '13a'
self.render('test.html')
self.write('1')
self.finish()

Thread class is:

class PThread(threading.Thread):

def run(self):
global KillThread, ppool
logging.error("Thread "+self.name+" in work")
while True:
if KillThread:
logging.info("Thread "+self.name+" killed")
break

func = ppool.get()

if callable(func):
func()
else:
raise Exception, "It's not a function"
break

Where ppool is Queue.Queue()

Does anyone know what can cause this errors?

mohaps

unread,
Jan 1, 2010, 8:15:22 PM1/1/10
to Tornado Web Server
use ulimit -n to bump up per process file descriptor limits

set it to 32K or something

--m

Creotiv

unread,
Jan 2, 2010, 5:32:24 AM1/2/10
to Tornado Web Server
i set ulimit -n up to 100k but i get the same error...

Any ideas?

Simo Salminen

unread,
Jan 2, 2010, 8:07:37 PM1/2/10
to Tornado Web Server
I suggest you put a full working sample somewhere on net. Much easier
to reproduce.

mohaps

unread,
Jan 2, 2010, 9:58:37 PM1/2/10
to Tornado Web Server
btw, this might be a stupid question, but worth looking into...

have you added the @tornado.web.asynchronous decorator on your get()
method?

mohaps

unread,
Jan 2, 2010, 10:02:18 PM1/2/10
to Tornado Web Server
the way I did defered execution was simpler

in the get()/post() I added the handler to a queue and returned

@tornado.web.asynchronous
def get(self):
taskQueue.add(('get',self));
def get_defered(self):
self.write('hello world'); # this is where the logic went

and a thread pool of 4 daemon threads was constantly doing
while True:
method, handler = taskQueue.get();
if method == 'get':
handler.get_defered();
elif method == 'post':
handler.post_defered();

Creotiv

unread,
Jan 3, 2010, 5:06:28 AM1/3/10
to Tornado Web Server
I did't add @tornado.web.asynchronous cause with my config all my
contollers methods are async.

I looked at your example.. and how i see it's work in the same way as
mine. And i think you might have the same error.
Try ab -c 1000 -n 1000 and look at your logs.

Creotiv

unread,
Jan 3, 2010, 5:11:50 AM1/3/10
to Tornado Web Server
it's already in the net. You can look at it here:
http://code.google.com/p/tornado-app/source/browse/#svn/branches/andrew
But code is not refactored for now(cause i need to finish this task
with threading first), so it's little dirty))

Creotiv

unread,
Jan 3, 2010, 6:13:40 AM1/3/10
to Tornado Web Server
I figured out how to fix this.. just need to set no_keep_alive param
to True. Now must figure out why)

Creotiv

unread,
Jan 3, 2010, 6:23:40 AM1/3/10
to Tornado Web Server
I was wrong((( no_keep_alive=True doesn't fix this problem((

mohaps

unread,
Jan 3, 2010, 12:02:06 PM1/3/10
to Tornado Web Server
try bumping up ab timeout to 10 seconds

ab -n 1000 -c 1000 -t 10

Creotiv

unread,
Jan 3, 2010, 2:27:01 PM1/3/10
to Tornado Web Server
give the same error.

Jonah Benton

unread,
Jan 4, 2010, 10:58:17 AM1/4/10
to python-...@googlegroups.com
re:

> I made some wrapper for controller methods that run them in threads.

calling tornado functions from multiple threads will cause problems
like file descriptors getting closed unexpectedly and data structures
to get corrupted. all work in tornado is done in the context of a
single thread. it's single-threaded and event-driven, not
multi-threaded.

if the controller functions are cheap, then to use multiple cores,
just run multiple tornado instances.

if the controller functions are expensive, create a thread pool in
process or separate threaded daemon process and use ipc-like
communication mechanisms to have tornado dispatch to the pool, and be
notified when work is done.

Creotiv

unread,
Jan 4, 2010, 3:27:44 PM1/4/10
to Tornado Web Server
I did like you said. In each process i created Thread pool with 8
threads(or more) for data transporting i use Queue in wich i dispatch
controller method and execute it there.
So each controller methods executing in Thread context.
And i don't understand what closing sockets. Cause one request can't
close socket cause socket used for many requests. How i understand the
code of Tornado. For each Tornado Process created IOStream wich work
with one nonblocking socket that handle many requests.
If you tell me what close stream of tornado process, i'll be very glad
(ofcourse if this is not big problem for you)

Jonah Benton

unread,
Jan 4, 2010, 9:19:46 PM1/4/10
to python-...@googlegroups.com
On Mon, Jan 4, 2010 at 3:27 PM, Creotiv <cre...@gmail.com> wrote:
> I did like you said. In each process i created Thread pool with 8
> threads(or more) for data transporting i use Queue in wich i dispatch
> controller method and execute it there.

the controller methods don't expect to be executed from different
threads, they expect to all be executed in the context of one (the
same) thread.

> So each controller methods executing in Thread context.

but there's only one HttpServer instance, so that gets confused when
called from different threads.

> And i don't understand what closing sockets. Cause one request can't
> close socket cause socket used for many requests. How i understand the
> code of Tornado. For each Tornado Process created IOStream wich work
> with one nonblocking socket that handle many requests.
> If you tell me what close stream of tornado process, i'll be very glad
> (ofcourse if this is not big problem for you)
>

what's being closed is a file descriptor, which represents one client
and can be read from and written to. the socket, in effect, creates a
file descriptor when a connection request from a client is accepted.

in tornado, only a single file descriptor is "active" at a particular
time. adding threads and dispatching controller methods to them does
not give each thread its own copy of the appropriate file descriptor;
instead, each controller method stomps over the state of all of the
rest of them. this isn't something to fix, though. in fact- this is a
good thing- you can get rid of 90% of the code you posted. the app
abstraction and the routes and the queue and the dispatching and all
of that is already done by tornado, and it already handles
concurrency- just not with threads.

Creotiv

unread,
Jan 5, 2010, 2:26:12 AM1/5/10
to Tornado Web Server
But what if i need to run some code in front of general process, cause
it block process for a long time. For example DB query?
Cause if there is no async method for function like DB query so
process will be blocked and Tornado will work like Apache. How i can
avoid this?
I tried this thing with multiprocessing, but there was many picklings
exceptions...

On 5 янв, 04:19, Jonah Benton <jo...@jonah.com> wrote:

Jonah Benton

unread,
Jan 5, 2010, 2:15:01 PM1/5/10
to python-...@googlegroups.com
gotcha. so there are 2 problems:

* the db operation may take a long time
* the db driver does not offer asynchronous notifications

the former probably has to be solved with caching- keeping results of
the expensive operation in memcached or something.

friendfeed and other folks don't seem to be concerned about blocking
drivers for short database operations. for long ones, this is really
about the drivers needing to offer an asynchronous callback interface.
the general solution here is complicated and should get into the
drivers, but for simple cases, a few query/result set types, one could
write a buffer/bridge layer.

a more architecturally-heavy approach is to put the data layer behind
its own http server, something threaded like cherrypy or web.py.
tornado talks to this using its non-blocking http client. the data
server bridges http verbs to whatever crud operations are needed on
the db, and returns json to tornado. this is similar to what people
are doing with couchdb, which does http and json natively. i imagine
the pattern will spread to other persistence engines.

Creotiv

unread,
Jan 5, 2010, 2:28:35 PM1/5/10
to Tornado Web Server
i was thinking about this. But before starting implement DB server, i
wanted to try my luck with threading in tornado.

Thank for your replies, they real help me)

> ...
>
> продолжение »

David P. Novakovic

unread,
Jan 5, 2010, 5:04:18 PM1/5/10
to python-...@googlegroups.com
Port twisted's ADBAPI driver to tornado. :)

Mike C. Fletcher

unread,
Jan 5, 2010, 5:50:15 PM1/5/10
to python-...@googlegroups.com
David P. Novakovic wrote:
> Port twisted's ADBAPI driver to tornado. :)
Or port Tornado to Twisted... hmm, that happened already :) .

Have fun,
Mike

--
________________________________________________
Mike C. Fletcher
Designer, VR Plumber, Coder
http://www.vrplumber.com
http://blog.vrplumber.com

Creotiv

unread,
Jan 7, 2010, 11:35:36 AM1/7/10
to Tornado Web Server
Figured one thing with my realization with Threading.
In iostream.py on line 240 there is bug. socket.fileno() can be
executed even if socket closed or fail. This make server goes down and
stop responding.
i add some change to see this.

def _add_io_state(self, state):
if not self._state & state:
self._state = self._state | state
+ if self.socket:
self.io_loop.update_handler(self.socket.fileno(),
self._state)
+ else:
+ logging.error("_add_io_state() line 240 iostrem.py
FAILED")
+ self.close()

With this code server work even after some sockets fail. Will test
farther to see real problem of this.

Stephen Day

unread,
Jan 16, 2010, 1:42:20 AM1/16/10
to python-...@googlegroups.com
You're close to right: when the socket does fail, epoll will remove it and throw the error shown. This looks kind of like this error:


However, I am not sure why you are using threads. The whole goal of tornado is that you aren't using threads, thus the great performance. If you carefully read the documents, you will see that friendfeed doesn't use any kind of threading for db access. 
Reply all
Reply to author
Forward
0 new messages