Skip to content

Instantly share code, notes, and snippets.

@bubba-h57
Created May 1, 2018 20:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save bubba-h57/9f31756f154b217c9fbd93efab4eb9b1 to your computer and use it in GitHub Desktop.
Save bubba-h57/9f31756f154b217c9fbd93efab4eb9b1 to your computer and use it in GitHub Desktop.
Simple Distributed Advisory Locking Class for AWS Lambda
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