2016-03-24 18:04:52 +00:00
|
|
|
import logging
|
|
|
|
|
2016-06-20 15:20:17 +00:00
|
|
|
from redis import RedisError
|
2016-03-24 18:04:52 +00:00
|
|
|
from redlock import RedLock, RedLockError
|
|
|
|
|
|
|
|
from app import app
|
|
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
2016-08-29 15:28:53 +00:00
|
|
|
|
|
|
|
class LockNotAcquiredException(Exception):
|
|
|
|
""" Exception raised if a GlobalLock could not be acquired. """
|
|
|
|
|
|
|
|
|
2016-03-24 18:04:52 +00:00
|
|
|
class GlobalLock(object):
|
|
|
|
""" A lock object that blocks globally via Redis. Note that Redis is not considered a tier-1
|
|
|
|
service, so this lock should not be used for any critical code paths.
|
|
|
|
"""
|
|
|
|
def __init__(self, name, lock_ttl=600):
|
|
|
|
self._lock_name = name
|
|
|
|
self._redis_info = dict(app.config['USER_EVENTS_REDIS'])
|
|
|
|
self._redis_info.update({'socket_connect_timeout': 5, 'socket_timeout': 5})
|
|
|
|
|
|
|
|
self._lock_ttl = lock_ttl
|
|
|
|
self._redlock = None
|
|
|
|
|
|
|
|
def __enter__(self):
|
2016-08-29 15:28:53 +00:00
|
|
|
if not self.acquire():
|
|
|
|
raise LockNotAcquiredException()
|
2016-03-24 18:04:52 +00:00
|
|
|
|
2016-08-29 15:28:53 +00:00
|
|
|
def __exit__(self, type, value, traceback):
|
|
|
|
self.release()
|
2016-03-24 18:04:52 +00:00
|
|
|
|
2016-08-29 15:28:53 +00:00
|
|
|
def acquire(self):
|
2016-03-24 18:04:52 +00:00
|
|
|
logger.debug('Acquiring global lock %s', self._lock_name)
|
|
|
|
try:
|
|
|
|
redlock = RedLock(self._lock_name, connection_details=[self._redis_info],
|
2016-08-29 15:28:53 +00:00
|
|
|
ttl=self._lock_ttl)
|
|
|
|
acquired = redlock.acquire()
|
|
|
|
if not acquired:
|
|
|
|
logger.debug('Was unable to not acquire lock %s', self._lock_name)
|
|
|
|
return False
|
|
|
|
|
2016-03-24 18:04:52 +00:00
|
|
|
self._redlock = redlock
|
|
|
|
logger.debug('Acquired lock %s', self._lock_name)
|
|
|
|
return True
|
|
|
|
except RedLockError:
|
|
|
|
logger.debug('Could not acquire lock %s', self._lock_name)
|
|
|
|
return False
|
2016-06-20 15:20:17 +00:00
|
|
|
except RedisError as re:
|
|
|
|
logger.debug('Could not connect to Redis for lock %s: %s', self._lock_name, re)
|
2016-03-24 18:04:52 +00:00
|
|
|
return False
|
|
|
|
|
2016-08-29 15:28:53 +00:00
|
|
|
def release(self):
|
2016-03-24 18:04:52 +00:00
|
|
|
if self._redlock is not None:
|
|
|
|
logger.debug('Releasing lock %s', self._lock_name)
|
2016-08-29 15:28:53 +00:00
|
|
|
try:
|
|
|
|
self._redlock.release()
|
|
|
|
except RedLockError:
|
|
|
|
logger.debug('Could not release lock %s', self._lock_name)
|
|
|
|
except RedisError as re:
|
|
|
|
logger.debug('Could not connect to Redis for releasing lock %s: %s', self._lock_name, re)
|
|
|
|
|
2016-03-24 18:04:52 +00:00
|
|
|
logger.debug('Released lock %s', self._lock_name)
|
|
|
|
self._redlock = None
|