Closed IlyaSkriblovsky closed 4 years ago
I'd be very interested. My company is still running the code I wrote when trying to address this. I'll be on vacation for two weeks but I can look at this then.
@arnimarj Thanks! Waiting forward for your review.
This PR is also touching places that are unrelated to timeouts, but needed to make unit testing more convenient. For example, I've moved RedisFactory.dbid
, .password
, etc to BaseRedisProtocol.__init__()
arguments in order to be able to use RedisProtocol
alone without RedisFactory
.
Hi.
Sorry for the (very very) late reply, but we've recently had the chance to do some more fork on txredisapi
. Our current efforts are here https://github.com/arnimarj/txredisapi/pull/1
I tried the PR on a test we added, and we are able to make it fail quite reliably. The test is:
class OtherTestTimeout(unittest.TestCase):
@defer.inlineCallbacks
def test_reply_timeout(self):
from twisted.internet import reactor
factory = protocol.ServerFactory()
factory.protocol = PingMocker
handler = reactor.listenTCP(8001, factory)
c = yield redis.Connection(host = "127.0.0.1",
port = 8001, reconnect = False, connectTimeout = 100.0, replyTimeout = 0.001)
calls = [
c.get("foobar"),
c.get("foobar"),
c.get("foobar"),
c.get("foobar"),
c.get("foobar"),
c.get("foobar"),
c.get("foobar"),
c.get("foobar"),
]
for d in calls:
yield self.assertFailure(d, redis.TimeoutError)
yield c.disconnect()
yield handler.stopListening()
class PingMocker(redis.LineReceiver):
def __init__(self):
self.calls = []
def lineReceived(self, line):
from twisted.internet import reactor
if line == 'PING':
self.calls.append(reactor.callLater(0.0, self.transport.write, b"+PONG\r\n"))
def connectionLost(self, reason):
for call in self.calls:
if call.active():
call.cancel()
This is a little bit simpler version of what's in the referenced PR.
I run it like so, in Twisted 19.2.1 and Python 3.7.4
PYTHONPATH=. trial --force-gc -eux -z 10 tests.test_connection.OtherTestTimeout
An example run gives me this:
(tools_venv3) arni@arni-ThinkStation-P320-Tiny:~/dohop/txredis_for_testing$ PYTHONPATH=. trial --force-gc -eux -z 10 tests.test_connection.OtherTestTimeout
Running tests shuffled with seed 10
Test Pass 1
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.022s
PASSED (successes=1)
Test Pass 2
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.016s
PASSED (successes=1)
Test Pass 3
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.016s
PASSED (successes=1)
Test Pass 4
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.016s
PASSED (successes=1)
Test Pass 5
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.020s
PASSED (successes=1)
Test Pass 6
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.018s
PASSED (successes=1)
Test Pass 7
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.015s
PASSED (successes=1)
Test Pass 8
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.015s
PASSED (successes=1)
Test Pass 9
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.015s
PASSED (successes=1)
Test Pass 10
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.015s
PASSED (successes=1)
Test Pass 11
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.016s
PASSED (successes=1)
Test Pass 12
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.021s
PASSED (successes=1)
Test Pass 13
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.021s
PASSED (successes=1)
Test Pass 14
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.021s
PASSED (successes=1)
Test Pass 15
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.021s
PASSED (successes=1)
Test Pass 16
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.019s
PASSED (successes=1)
Test Pass 17
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.019s
PASSED (successes=1)
Test Pass 18
tests.test_connection
OtherTestTimeout
test_reply_timeout ... [OK]
-------------------------------------------------------------------------------
Ran 1 tests in 0.015s
PASSED (successes=1)
Test Pass 19
tests.test_connection
OtherTestTimeout
test_reply_timeout ... Traceback (most recent call last):
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py", line 1417, in _inlineCallbacks
result = result.throwExceptionIntoGenerator(g)
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/python/failure.py", line 512, in throwExceptionIntoGenerator
return g.throw(self.type, self.value, self.tb)
File "/home/arni/dohop/txredis_for_testing/tests/test_connection.py", line 263, in test_reply_timeout
yield self.assertFailure(d, redis.TimeoutError)
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/trial/_asynctest.py", line 75, in _eb
raise self.failureException(output)
twisted.trial.unittest.FailTest:
Expected: (<class 'txredisapi.TimeoutError'>,)
Got:
[Failure instance: Traceback: <class 'txredisapi.ConnectionError'>: Lost connection
/home/arni/dohop/txredis_for_testing/tests/../txredisapi.py:527:replyReceived
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:1873:put
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:460:callback
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:568:_startRunCallbacks
--- <exception caught here> ---
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:654:_runCallbacks
/home/arni/dohop/txredis_for_testing/tests/../txredisapi.py:532:handle_reply
]
[FAIL]
Traceback (most recent call last):
Failure: twisted.trial.util.DirtyReactorAggregateError: Reactor was unclean.
Selectables:
<<class 'twisted.internet.tcp.Port'> of <class 'twisted.internet.protocol.ServerFactory'> on 8001>
[ERROR]
===============================================================================
[FAIL]
Traceback (most recent call last):
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py", line 1417, in _inlineCallbacks
result = result.throwExceptionIntoGenerator(g)
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/python/failure.py", line 512, in throwExceptionIntoGenerator
return g.throw(self.type, self.value, self.tb)
File "/home/arni/dohop/txredis_for_testing/tests/test_connection.py", line 263, in test_reply_timeout
yield self.assertFailure(d, redis.TimeoutError)
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py", line 654, in _runCallbacks
current.result = callback(current.result, *args, **kw)
File "/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/trial/_asynctest.py", line 75, in _eb
raise self.failureException(output)
twisted.trial.unittest.FailTest:
Expected: (<class 'txredisapi.TimeoutError'>,)
Got:
[Failure instance: Traceback: <class 'txredisapi.ConnectionError'>: Lost connection
/home/arni/dohop/txredis_for_testing/tests/../txredisapi.py:527:replyReceived
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:1873:put
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:460:callback
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:568:_startRunCallbacks
--- <exception caught here> ---
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:654:_runCallbacks
/home/arni/dohop/txredis_for_testing/tests/../txredisapi.py:532:handle_reply
]
tests.test_connection.OtherTestTimeout.test_reply_timeout
===============================================================================
[ERROR]
Traceback (most recent call last):
Failure: twisted.trial.util.DirtyReactorAggregateError: Reactor was unclean.
Selectables:
<<class 'twisted.internet.tcp.Port'> of <class 'twisted.internet.protocol.ServerFactory'> on 8001>
tests.test_connection.OtherTestTimeout.test_reply_timeout
-------------------------------------------------------------------------------
Ran 1 tests in 0.017s
FAILED (failures=1, errors=1)
DDDDDDDDDDDDDDDD <twisted.internet.defer.DebugInfo object at 0x7f4c3adabf90> [Failure instance: Traceback: <class 'txredisapi.ConnectionError'>: Lost connection
/home/arni/dohop/txredis_for_testing/tests/../txredisapi.py:527:replyReceived
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:1873:put
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:460:callback
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:568:_startRunCallbacks
--- <exception caught here> ---
/home/arni/.config/dohop/tools_venv3/lib/python3.7/site-packages/twisted/internet/defer.py:654:_runCallbacks
/home/arni/dohop/txredis_for_testing/tests/../txredisapi.py:532:handle_reply
]
Hi! Thanks for your finding. Seems like when one of multiple calls is timed out, all other pending calls get ConnectionError instead of TimeoutError because the first timeout failure aborts connection and there is a race condition.
I've added a quick solution. Could you please recheck this test case with the updated PR?
(Travis' build is failed due to inability to install py2.6. Seems we might exclude testing on py2.6.)
Hi. Impressive turnaround.
I'm happy to report I couldn't make it fail.
Recreated as #139
txredisapi has
replyTimeout
connection argument, but it effectively broken and doesn't work at all.Prior art:
100 Fix reply timeout by @jjappi
105 connectTimeout and replyTimeout by @arnimarj
Both mentioned PRs failed to meet following criteria and were closed (see corresponding discussions):
replyTimeout
seconds.replyTimeout
seconds (including idle connection).This is the third try to implement
replyTimeout
.TimeoutError
is inherited fromConnectionError
to be compatible with existing client code that may expectConnectionError
to be the only exception type that may be raised by query methods.@jjappi, @arnimarj, I would be happy if you find time to review and test this PR (if you are still interested of course)