Skip to content
This repository has been archived by the owner on May 5, 2021. It is now read-only.

fix: fix "redlock may crash while some redis-server crash" #27

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 10 additions & 14 deletions redlock/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
from collections import namedtuple

import redis
from redis.exceptions import RedisError

# Python 3 compatibility
string_type = getattr(__builtins__, 'basestring', str)
Expand Down Expand Up @@ -59,14 +58,15 @@ def __init__(self, connection_list, retry_count=None, retry_delay=None):
server = connection_info
self.servers.append(server)
except Exception as e:
raise Warning(str(e))
self.quorum = (len(connection_list) // 2) + 1
pass

self.quorum = (len(connection_list) // 2) + 1
if len(self.servers) < self.quorum:
raise CannotObtainLock(
"Failed to connect to the majority of redis servers")
self.retry_count = retry_count or self.default_retry_count
self.retry_delay = retry_delay or self.default_retry_delay
logging.error("server num:{}".format(len(self.servers)))

def lock_instance(self, server, resource, val, ttl):
try:
Expand All @@ -79,7 +79,7 @@ def unlock_instance(self, server, resource, val):
try:
server.eval(self.unlock_script, 1, resource, val)
except Exception as e:
logging.exception("Error unlocking resource %s in server %s", resource, str(server))
pass

def get_unique_id(self):
CHARACTERS = string.ascii_letters + string.digits
Expand All @@ -94,29 +94,27 @@ def lock(self, resource, ttl):
# drift for small TTLs.
drift = int(ttl * self.clock_drift_factor) + 2

redis_errors = list()
while retry < self.retry_count:
n = 0
start_time = int(time.time() * 1000)
del redis_errors[:]
for server in self.servers:
try:
if self.lock_instance(server, resource, val, ttl):
n += 1
except RedisError as e:
redis_errors.append(e)
except:
pass

elapsed_time = int(time.time() * 1000) - start_time
validity = int(ttl - elapsed_time - drift)
if validity > 0 and n >= self.quorum:
if redis_errors:
raise MultipleRedlockException(redis_errors)
return Lock(validity, resource, val)
else:
for server in self.servers:
try:
self.unlock_instance(server, resource, val)
except:
pass

retry += 1
time.sleep(self.retry_delay)
return False
Expand All @@ -126,7 +124,5 @@ def unlock(self, lock):
for server in self.servers:
try:
self.unlock_instance(server, lock.resource, lock.key)
except RedisError as e:
redis_errors.append(e)
if redis_errors:
raise MultipleRedlockException(redis_errors)
except:
pass
30 changes: 28 additions & 2 deletions tests/test_redlock.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
# coding: utf-8

import sys
sys.path.append('.')
import unittest

from redlock import Redlock, MultipleRedlockException
Expand All @@ -6,7 +10,26 @@
class TestRedlock(unittest.TestCase):

def setUp(self):
self.redlock = Redlock([{"host": "localhost"}])
try:
self.redlock = Redlock([{"host": "localhost"}])
self.dstlock = Redlock([{"host": "localhost", "port": 6379, "socket_timeout": 0.5},
{"host": "localhost", "port": 6380, "socket_timeout": 0.5},
{"host": "localhost", "port": 6381, "socket_timeout": 0.5}])
except Exception as e:
pass

'''
def test_distribute_locks(self):

import time
while True:
if not self.dstlock.lock("fizz", 1000):
print ("lock failed")
else:
print("lock success")

time.sleep(1)
'''

def test_lock(self):
lock = self.redlock.lock("pants", 100)
Expand All @@ -28,7 +51,7 @@ def test_bad_connection_info(self):
def test_py3_compatible_encoding(self):
lock = self.redlock.lock("pants", 1000)
key = self.redlock.servers[0].get("pants")
self.assertEquals(lock.key, key)
self.assertEqual(lock.key, key)

def test_ttl_not_int_trigger_exception_value_error(self):
with self.assertRaises(ValueError):
Expand All @@ -41,3 +64,6 @@ def test_multiple_redlock_exception(self):
exc_str = str(exc)
self.assertIn('connection error', exc_str)
self.assertIn('command timed out', exc_str)

if __name__ == '__main__':
unittest.main()