diff --git a/apps/lock_manager/__init__.py b/apps/lock_manager/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/lock_manager/conf/__init__.py b/apps/lock_manager/conf/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/apps/lock_manager/conf/settings.py b/apps/lock_manager/conf/settings.py new file mode 100644 index 0000000000..05f4ae374c --- /dev/null +++ b/apps/lock_manager/conf/settings.py @@ -0,0 +1,5 @@ +from django.conf import settings + +DEFAULT_LOCK_TIMEOUT_VALUE = 10 + +DEFAULT_LOCK_TIMEOUT = getattr(settings, 'LOCK_MANAGER_DEFAULT_LOCK_TIMEOUT', DEFAULT_LOCK_TIMEOUT_VALUE) diff --git a/apps/lock_manager/exceptions.py b/apps/lock_manager/exceptions.py new file mode 100644 index 0000000000..cede7b1642 --- /dev/null +++ b/apps/lock_manager/exceptions.py @@ -0,0 +1,2 @@ +class LockError(Exception): + pass diff --git a/apps/lock_manager/managers.py b/apps/lock_manager/managers.py new file mode 100644 index 0000000000..96c57ca02b --- /dev/null +++ b/apps/lock_manager/managers.py @@ -0,0 +1,52 @@ +try: + from psycopg2 import OperationalError +except ImportError: + class OperationalError(Exception): + pass + +import datetime + +from django.db.utils import DatabaseError +from django.db.utils import IntegrityError +from django.db import transaction +from django.db import models + +from lock_manager.exceptions import LockError + + +class LockManager(models.Manager): + @transaction.commit_manually + def acquire_lock(self, name, timeout=None): + lock = self.model(name=name, timeout=timeout) + try: + lock.save(force_insert=True) + except IntegrityError: + transaction.rollback() + # There is already an existing lock + # Check it's expiration date and if expired, delete it and + # create it again + lock = self.model.objects.get(name=name) + transaction.rollback() + + if datetime.datetime.now() > lock.creation_datetime + datetime.timedelta(seconds=lock.timeout): + self.release_lock(name) + lock.timeout=timeout + lock.save() + transaction.commit() + else: + raise LockError('Unable to acquire lock') + except DatabaseError: + transaction.rollback() + # Special case for ./manage.py syncdb + except (OperationalError, ImproperlyConfigured): + transaction.rollback() + # Special for DjangoZoom, which executes collectstatic media + # doing syncdb and creating the database tables + else: + transaction.commit() + + @transaction.commit_manually + def release_lock(self, name): + lock = self.model.objects.get(name=name) + lock.delete() + transaction.commit() diff --git a/apps/lock_manager/models.py b/apps/lock_manager/models.py new file mode 100644 index 0000000000..53c6b6e49d --- /dev/null +++ b/apps/lock_manager/models.py @@ -0,0 +1,26 @@ +import datetime + +from django.db import models +from django.utils.translation import ugettext_lazy as _ + +from lock_manager.managers import LockManager +from lock_manager.conf.settings import DEFAULT_LOCK_TIMEOUT + + +class Lock(models.Model): + creation_datetime = models.DateTimeField(verbose_name=_(u'creation datetime')) + timeout = models.IntegerField(default=DEFAULT_LOCK_TIMEOUT, verbose_name=_(u'timeout')) + name = models.CharField(max_length=32, verbose_name=_(u'name'), unique=True) + + objects = LockManager() + + def __unicode__(self): + return self.name + + def save(self, *args, **kwargs): + self.creation_datetime = datetime.datetime.now() + super(Lock, self).save(*args, **kwargs) + + class Meta: + verbose_name = _(u'lock') + verbose_name_plural = _(u'locks') diff --git a/apps/lock_manager/tests.py b/apps/lock_manager/tests.py new file mode 100644 index 0000000000..501deb776c --- /dev/null +++ b/apps/lock_manager/tests.py @@ -0,0 +1,16 @@ +""" +This file demonstrates writing tests using the unittest module. These will pass +when you run "manage.py test". + +Replace this with more appropriate tests for your application. +""" + +from django.test import TestCase + + +class SimpleTest(TestCase): + def test_basic_addition(self): + """ + Tests that 1 + 1 always equals 2. + """ + self.assertEqual(1 + 1, 2) diff --git a/apps/lock_manager/views.py b/apps/lock_manager/views.py new file mode 100644 index 0000000000..60f00ef0ef --- /dev/null +++ b/apps/lock_manager/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/settings.py b/settings.py index 32867a3281..baf47dd19a 100644 --- a/settings.py +++ b/settings.py @@ -129,6 +129,7 @@ INSTALLED_APPS = ( 'project_tools', 'smart_settings', 'navigation', + 'lock_manager', 'web_theme', 'common', 'metadata',