diff --git a/celery_dedupe/__init__.py b/celery_dedupe/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/celery_dedupe/storage/__init__.py b/celery_dedupe/storage/__init__.py new file mode 100644 index 0000000..002d20a --- /dev/null +++ b/celery_dedupe/storage/__init__.py @@ -0,0 +1,3 @@ +from .base import Storage + +__all__ = ['Storage'] diff --git a/celery_dedupe/storage/base.py b/celery_dedupe/storage/base.py new file mode 100644 index 0000000..ef0108b --- /dev/null +++ b/celery_dedupe/storage/base.py @@ -0,0 +1,18 @@ +class Storage(object): + ''' + Defines the public api for Storages + ''' + + default_config = {} + + def __init__(self, connection, **config): + self.connection = connection + self.config = self.default_config.copy() + self.config.update(config) + + def obtain_lock(self, key, value): + ''' + Responsible for obtaining a lock for the task + :returns boolean: Whether lock could be obtained or not + ''' + raise NotImplementedError diff --git a/celery_dedupe/storage/redis.py b/celery_dedupe/storage/redis.py new file mode 100644 index 0000000..e6d667c --- /dev/null +++ b/celery_dedupe/storage/redis.py @@ -0,0 +1,21 @@ +from . import Storage + +class RedisStorage(Storage): + ''' + Storage class for redis. + + Config: + expiry: number of seconds before expiring task, 0 to disable (default: 0) + ''' + + default_config = { + 'expiry': 0, + } + + def obtain_lock(self, key, value): + obtained = self.connection.setnx(key, value) + if self.config['expiry'] and obtained: + self.connection.expire(key, self.config['expiry']) + + return obtained + diff --git a/dev-requirements.txt b/dev-requirements.txt new file mode 100644 index 0000000..4eb8ef7 --- /dev/null +++ b/dev-requirements.txt @@ -0,0 +1,3 @@ +exam==0.10.5 +pytest==2.7.2 +redis==2.10.3 diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/__init__.pyc b/tests/__init__.pyc new file mode 100644 index 0000000..be19f6e Binary files /dev/null and b/tests/__init__.pyc differ diff --git a/tests/__pycache__/test_storages.cpython-27-PYTEST.pyc b/tests/__pycache__/test_storages.cpython-27-PYTEST.pyc new file mode 100644 index 0000000..f529bfa Binary files /dev/null and b/tests/__pycache__/test_storages.cpython-27-PYTEST.pyc differ diff --git a/tests/test_storages.py b/tests/test_storages.py new file mode 100644 index 0000000..5098526 --- /dev/null +++ b/tests/test_storages.py @@ -0,0 +1,30 @@ +from uuid import uuid4 +from redis import StrictRedis + +from celery_dedupe.storage.redis import RedisStorage + +class TestRedisStorage(object): + + @property + def redis(self): + if hasattr(self, '_redis'): + return self._redis + + self._redis = StrictRedis() + return self._redis + + def test_lock_obtained(self): + storage = RedisStorage(self.redis) + assert storage.obtain_lock(uuid4(), '1') + + def test_lock_obtained_with_expiry(self): + key = uuid4() + storage = RedisStorage(self.redis, expiry=10) + assert storage.obtain_lock(key, '1') + assert self.redis.ttl(key) == 10 + + def test_already_locked(self): + key = uuid4() + self.redis.setex(key, '1', 10) + storage = RedisStorage(self.redis, expiry=10) + assert not storage.obtain_lock(key, '1')