You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Apr 3, 2019. It is now read-only.
If I make a query to redis that needs to open a connection, and that connection blocks (eg. slow network, network partition), then the whole tornado process blocks. No requests can be handled, even requests that do not depend on redis.
Example: here is a simple tornado HTTP service that handles two URLs, /r1 and /r2. /r1 does not depend on redis at all: it immediately returns a hardcoded string. /r2 fetches a value from redis and returns it.
import tornadoredis
import tornado.httpserver
import tornado.web
import tornado.ioloop
import tornado.gen
class R1Handler(tornado.web.RequestHandler):
'''handle a request which does not depend on redis'''
@tornado.web.asynchronous
@tornado.gen.engine
def get(self):
self.write('all is well\n')
self.finish()
class R2Handler(tornado.web.RequestHandler):
'''handle a request which does depend on redis'''
@tornado.web.asynchronous
@tornado.gen.engine
def get(self):
c = tornadoredis.Client()
foo = yield tornado.gen.Task(c.get, 'foo')
self.set_header('Content-Type', 'text/plain')
self.write('foo = %s\n' % (foo,))
self.finish()
application = tornado.web.Application([
(r'/r1', R1Handler),
(r'/r2', R2Handler),
])
if __name__ == '__main__':
# Start the data initialization routine
http_server = tornado.httpserver.HTTPServer(application)
http_server.listen(8888)
print 'Demo is runing at 0.0.0.0:8888\nQuit the demo with CONTROL-C'
tornado.ioloop.IOLoop.instance().start()
(I put that script in demos/mixed/app.py in the tornado-redis source tree.)
Expected outcome: if my tornado process is unable to connect to redis, then I expect queries to /r2 to fail or block, but queries to /r1 should continue to work fine.
Actual outcome: as soon as a request for /r2 blocks, requests for /r1 also block. It appears that the entire process is blocked connecting to redis.
Here's how I reproduced it. First, in one terminal run the tornado server:
$ PYTHONPATH=. python demos/mixed/app.py
Demo is runing at 0.0.0.0:8888
Quit the demo with CONTROL-C
In another window, start polling /r1:
$ while true ; do echo -n `date`": " ; curl http://localhost:8888/r1 ; sleep 1 ; done
Mon Jan 12 13:25:01 EST 2015: all is well
Mon Jan 12 13:25:02 EST 2015: all is well
Mon Jan 12 13:25:03 EST 2015: all is well
[...]
In a third window, ensure that /r2 works:
$ redis-cli
127.0.0.1:6379> set foo "hello world"
OK
$ curl http://localhost:8888/r2
foo = hello world
Now simulate a network partition: make all packets to redis disappear (this assumes Linux):
$ sudo iptables -F # wipe all existing firewall rules
$ sudo iptables -A INPUT --proto tcp --dport 6379 -j DROP # drop packets to redis
Hop over to the window that is polling /r1; it should still be working fine:
[...]
Mon Jan 12 13:27:50 EST 2015: all is well
Mon Jan 12 13:27:51 EST 2015: all is well
Mon Jan 12 13:27:52 EST 2015: all is well
[...]
Now request /r2, which blocks because redis is on the other side of a network partition:
$ curl http://localhost:8888/r2
This request blocks, which is entirely OK. (I'd like it to fail with a timeout eventually, but whatever. Not important.)
However, the /r1 poll is now blocked:
[...]
Mon Jan 12 13:28:28 EST 2015: all is well
Mon Jan 12 13:28:29 EST 2015: all is well
Mon Jan 12 13:28:30 EST 2015: [blocked here]
We can see where it's blocked by hitting Ctrl-C on the tornado process:
^CTraceback (most recent call last):
File "demos/mixed/app.py", line 40, in <module>
tornado.ioloop.IOLoop.instance().start()
File "/usr/lib/python2.7/dist-packages/tornado/ioloop.py", line 607, in start
self._run_callback(callback)
File "/usr/lib/python2.7/dist-packages/tornado/ioloop.py", line 458, in _run_callback
callback()
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 331, in wrapped
raise_exc_info(exc)
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 302, in wrapped
ret = fn(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/tornado/iostream.py", line 341, in wrapper
callback(*args)
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 331, in wrapped
raise_exc_info(exc)
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 302, in wrapped
ret = fn(*args, **kwargs)
File "/usr/lib/python2.7/dist-packages/tornado/httpserver.py", line 327, in _on_headers
self.request_callback(self._request)
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1600, in __call__
handler._execute(transforms, *args, **kwargs)
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1134, in _execute
self._when_complete(self.prepare(), self._execute_method)
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1141, in _when_complete
callback()
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1162, in _execute_method
self._when_complete(method(*self.path_args, **self.path_kwargs),
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1311, in wrapper
return result
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 198, in __exit__
return self.exception_handler(type, value, traceback)
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1115, in _stack_context_handle_exception
raise_exc_info((type, value, traceback))
File "/usr/lib/python2.7/dist-packages/tornado/web.py", line 1298, in wrapper
result = method(self, *args, **kwargs)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 159, in wrapper
deactivate()
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 198, in __exit__
return self.exception_handler(type, value, traceback)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 136, in handle_exception
return runner.handle_exception(typ, value, tb)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 556, in handle_exception
self.run()
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 505, in run
yielded = self.gen.throw(*exc_info)
File "demos/mixed/app.py", line 23, in get
foo = yield tornado.gen.Task(c.get, 'foo')
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 153, in wrapper
runner.run()
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 533, in run
self.yield_point.start(self)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 371, in start
self.func(*self.args, **self.kwargs)
File "/data/src/tornado-redis/tornadoredis/client.py", line 690, in get
self.execute_command('GET', key, callback=callback)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 159, in wrapper
deactivate()
File "/usr/lib/python2.7/dist-packages/tornado/stack_context.py", line 198, in __exit__
return self.exception_handler(type, value, traceback)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 136, in handle_exception
return runner.handle_exception(typ, value, tb)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 556, in handle_exception
self.run()
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 505, in run
yielded = self.gen.throw(*exc_info)
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 153, in wrapper
runner.run()
File "/usr/lib/python2.7/dist-packages/tornado/gen.py", line 507, in run
yielded = self.gen.send(next)
File "/data/src/tornado-redis/tornadoredis/client.py", line 407, in execute_command
self.connection.connect()
File "/data/src/tornado-redis/tornadoredis/connection.py", line 72, in connect
timeout=self.timeout
File "/usr/lib/python2.7/socket.py", line 562, in create_connection
sock.connect(sa)
File "/usr/lib/python2.7/socket.py", line 224, in meth
return getattr(self._sock,name)(*args)
KeyboardInterrupt
The problem appears to be that tornado-redis is using the blocking socket calls to connect to redis. I suspect it should probably use tornado.tcpclient instead. I am not a tornado expert though! I just spotted that module in the docs.
The text was updated successfully, but these errors were encountered:
If I make a query to redis that needs to open a connection, and that connection blocks (eg. slow network, network partition), then the whole tornado process blocks. No requests can be handled, even requests that do not depend on redis.
Example: here is a simple tornado HTTP service that handles two URLs, /r1 and /r2. /r1 does not depend on redis at all: it immediately returns a hardcoded string. /r2 fetches a value from redis and returns it.
(I put that script in demos/mixed/app.py in the tornado-redis source tree.)
Expected outcome: if my tornado process is unable to connect to redis, then I expect queries to /r2 to fail or block, but queries to /r1 should continue to work fine.
Actual outcome: as soon as a request for /r2 blocks, requests for /r1 also block. It appears that the entire process is blocked connecting to redis.
Here's how I reproduced it. First, in one terminal run the tornado server:
In another window, start polling /r1:
In a third window, ensure that /r2 works:
Now simulate a network partition: make all packets to redis disappear (this assumes Linux):
Hop over to the window that is polling /r1; it should still be working fine:
Now request /r2, which blocks because redis is on the other side of a network partition:
This request blocks, which is entirely OK. (I'd like it to fail with a timeout eventually, but whatever. Not important.)
However, the /r1 poll is now blocked:
We can see where it's blocked by hitting Ctrl-C on the tornado process:
The problem appears to be that tornado-redis is using the blocking socket calls to connect to redis. I suspect it should probably use tornado.tcpclient instead. I am not a tornado expert though! I just spotted that module in the docs.
The text was updated successfully, but these errors were encountered: