-
Notifications
You must be signed in to change notification settings - Fork 3
/
lock.py
62 lines (49 loc) · 1.54 KB
/
lock.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# -*- coding: utf-8 -*-
from contextlib import contextmanager
from redis import Redis
class Lock(object):
"""Lock implemented on top of redis."""
def __init__(self, name, timeout=60, db=0):
"""
Create, if necessary the lock variable in redis.
We utilize the ``blpop`` command and its blocking behavior.
The ``_key`` variable is used to check, whether the mutex exists or not,
while the ``_mutex`` variable is the actual mutex.
"""
self._key = 'lock:name:%s' % name
self._mutex = 'lock:mutex:%s' % name
self._timeout = timeout
self._r = Redis(db=1)
self._init_mutex()
@property
def mutex_key(self):
return self._mutex
def lock(self):
"""
Lock and block.
Raises:
RuntimeError, in case of synchronization issues.
"""
res = self._r.blpop(self._mutex, self._timeout)
if res is None:
raise RuntimeError
def unlock(self):
self._r.rpush(self._mutex, 1)
def _init_mutex(self):
"""
Initialize the mutex, if necessary.
Use a separate key to check for the existence of the "mutex",
so that we can utilize ``getset``, which is atomic.
"""
exists = self._r.getset(self._key, 1)
if exists is None:
self._r.lpush(self._mutex, 1)
@contextmanager
def lock(name, timeout=60):
"""Lock on name using redis."""
l = Lock(name, timeout)
try:
l.lock()
yield
finally:
l.unlock()