Skip to content

Commit

Permalink
Update behavior of lock to behave closer to redis lock
Browse files Browse the repository at this point in the history
  • Loading branch information
taylor-cedar committed Sep 17, 2018
1 parent b87e885 commit 924ca01
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 6 deletions.
19 changes: 13 additions & 6 deletions fakeredis.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@
from datetime import datetime, timedelta
import operator
import sys
import threading
import time
import types
import re
import functools
from itertools import count, islice
from uuid import uuid4

import redis
from redis.exceptions import ResponseError
Expand Down Expand Up @@ -315,8 +315,8 @@ class _Lock(object):
def __init__(self, redis, name, timeout):
self.redis = redis
self.name = name
self.lock = threading.Lock()
redis.set(name, self, ex=timeout)
self.timeout = timeout
self.id = str(uuid4())

def __enter__(self):
self.acquire()
Expand All @@ -326,11 +326,18 @@ def __exit__(self, exc_type, exc_value, traceback):
self.release()

def acquire(self, blocking=True, blocking_timeout=None):
return self.lock.acquire(blocking)
owner_id = _decode(self.redis.get(self.name))
can_acquire = not owner_id or owner_id == self.id
if can_acquire:
self.redis.set(self.name, self.id, ex=self.timeout)
elif blocking:
raise ValueError('fakeredis can\'t do blocking locks')

return can_acquire

def release(self):
self.lock.release()
self.redis.delete(self.name)
if _decode(self.redis.get(self.name)) == self.id:
self.redis.delete(self.name)


def _check_conn(func):
Expand Down
31 changes: 31 additions & 0 deletions test_fakeredis.py
Original file line number Diff line number Diff line change
Expand Up @@ -3916,6 +3916,37 @@ def test_lock(self):
self.assertTrue(self.redis.exists('bar'))
self.assertFalse(self.redis.exists('bar'))

def test_acquiring_lock_twice(self):
lock = self.redis.lock('foo')
self.assertTrue(lock.acquire(blocking=False))
self.assertTrue(lock.acquire(blocking=False))

def test_acquiring_lock_different_lock(self):
lock1 = self.redis.lock('foo')
lock2 = self.redis.lock('foo')
self.assertTrue(lock1.acquire(blocking=False))
self.assertFalse(lock2.acquire(blocking=False))

def test_acquiring_lock_different_lock_release(self):
lock1 = self.redis.lock('foo')
lock2 = self.redis.lock('foo')
self.assertTrue(lock1.acquire(blocking=False))
self.assertFalse(lock2.acquire(blocking=False))

# Test only releasing lock1 actually releases the lock
lock2.release()
self.assertFalse(lock2.acquire(blocking=False))
lock1.release()

# Locking with lock2 now has the lock
self.assertTrue(lock2.acquire(blocking=False))
self.assertFalse(lock1.acquire(blocking=False))

def test_nested_lock(self):
with self.redis.lock('bar'):
acquired = self.redis.lock('bar').acquire(blocking=False)
self.assertFalse(acquired)


class DecodeMixin(object):
decode_responses = True
Expand Down

0 comments on commit 924ca01

Please sign in to comment.