Created
May 1, 2018 20:18
-
-
Save bubba-h57/9f31756f154b217c9fbd93efab4eb9b1 to your computer and use it in GitHub Desktop.
Simple Distributed Advisory Locking Class for AWS Lambda
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import os | |
import redis | |
import logging | |
import string | |
import random | |
from collections import namedtuple | |
from redis.exceptions import RedisError | |
Lock = namedtuple("Lock", ("valid", "resource", "key", "ttl")) | |
class LockerRoom: | |
default_retry_count = 3 | |
default_retry_delay = 0.2 | |
clock_drift_factor = 0.01 | |
unlock_script = """ | |
if redis.call("get",KEYS[1]) == ARGV[1] then | |
return redis.call("del",KEYS[1]) | |
else | |
return 0 | |
end""" | |
def __init__(self, host='localhost', port=6379, db=0, retry_count=3, retry_delay=0.2): | |
self.logger = logging.getLogger(os.environ.get('APP_NAME'), 'LockerRoom') | |
self.server = redis.StrictRedis(host, port, db) | |
self.retry_count = retry_count | |
self.retry_delay = retry_delay | |
def lock(self, resource, ttl): | |
lock = Lock(False, 'locker:{}'.format(resource), self.generate_key(), ttl) | |
return self.lock_locker(lock) | |
def unlock(self, lock): | |
return self.unlock_locker(lock) | |
def lock_locker(self, lock): | |
try: | |
self.logger.debug('We were asked to lock {} with the key {} for {}'.format(lock.resource, lock.key, lock.ttl)) | |
result = self.server.set(lock.resource, lock.key, nx=True, px=lock.ttl) | |
self.logger.debug('Locking Result {}'.format(result)) | |
if result: | |
# Named Tuples are immutable, need a new one. | |
return Lock(True, lock.resource, lock.key, lock.ttl) | |
except RedisError as e: | |
self.logger.exception("Error %s locking resource %s in server %s", str(e), lock.resource, str(self.server)) | |
return lock | |
def unlock_locker(self, lock): | |
try: | |
self.server.eval(self.unlock_script, 1, lock.resource, lock.key) | |
except RedisError as e: | |
self.logger.exception("Error %s unlocking resource %s in server %s", str(e),lock.resource, str(self.server)) | |
return Lock(False, lock.resource, lock.key, lock.ttl) | |
@staticmethod | |
def generate_key(): | |
characters = string.ascii_letters + string.digits | |
return ''.join(random.choice(characters) for _ in range(22)).encode() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment