Skip to content

Commit 68be49b

Browse files
author
Ricardo Alves
committed
Make sure a new connection is established upon retry, before marking dead, to avoid reusing a stale socket.
1 parent 288c159 commit 68be49b

File tree

2 files changed

+52
-4
lines changed

2 files changed

+52
-4
lines changed

memcache.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1048,7 +1048,7 @@ def _unsafe_set():
10481048
except _ConnectionDeadError:
10491049
# retry once
10501050
try:
1051-
if server._get_socket():
1051+
if server._get_socket(reconnect=True):
10521052
return _unsafe_set()
10531053
except (_ConnectionDeadError, socket.error) as msg:
10541054
server.mark_dead(msg)
@@ -1101,7 +1101,7 @@ def _unsafe_get():
11011101
except _ConnectionDeadError:
11021102
# retry once
11031103
try:
1104-
if server.connect():
1104+
if server._get_socket(reconnect=True):
11051105
return _unsafe_get()
11061106
return None
11071107
except (_ConnectionDeadError, socket.error) as msg:
@@ -1386,10 +1386,10 @@ def mark_dead(self, reason):
13861386
self.flush_on_next_connect = 1
13871387
self.close_socket()
13881388

1389-
def _get_socket(self):
1389+
def _get_socket(self, reconnect=False):
13901390
if self._check_dead():
13911391
return None
1392-
if self.socket:
1392+
if self.socket and not reconnect:
13931393
return self.socket
13941394
s = socket.socket(self.family, socket.SOCK_STREAM)
13951395
if hasattr(s, 'settimeout'):

tests/test_memcache.py

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import print_function
22

3+
import socket
34
import unittest
45

56
import six
@@ -167,5 +168,52 @@ def test_disconnect_all_delete_multi(self):
167168
self.assertEqual(ret, 1)
168169

169170

171+
class TestMemcacheMarkDead(unittest.TestCase):
172+
173+
def setUp(self):
174+
self.status = locals()
175+
self.address = ("127.0.0.1", 11213)
176+
self._start_stub_server()
177+
self.client = Client(["127.0.0.1:11213"], debug=1)
178+
179+
def tearDown(self):
180+
self._stop_stub_server()
181+
182+
def _start_stub_server(self):
183+
# setup stub server
184+
stub_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
185+
stub_socket.bind(self.address)
186+
stub_socket.listen(1)
187+
self.stub_socket = stub_socket
188+
189+
def _stop_stub_server(self):
190+
self.stub_socket.close()
191+
192+
def test_mark_server_dead(self):
193+
mc_host = self.client._get_server('foo')[0]
194+
client_socket = mc_host._get_socket()
195+
196+
# make sure the server is not marked dead
197+
self.assertEqual(0, mc_host._check_dead())
198+
199+
# stop the stub server
200+
self._stop_stub_server()
201+
202+
# host is not yet marked as dead
203+
self.assertEqual(0, mc_host._check_dead())
204+
205+
# create a new stub socket again
206+
self._start_stub_server()
207+
208+
# The client will try to re-use the old socket and if it fails
209+
# then should re-establish a new connection
210+
# so the socket must be a new one
211+
new_client_socket = mc_host._get_socket(reconnect=True)
212+
self.assertNotEquals(new_client_socket, client_socket)
213+
214+
# server is not marked dead, because connection succeeded
215+
self.assertEqual(0, mc_host._check_dead())
216+
217+
170218
if __name__ == '__main__':
171219
unittest.main()

0 commit comments

Comments
 (0)