Skip to content

Commit

Permalink
Merge pull request #36 from vikingco/VD-463/automatic-unlock-updates
Browse files Browse the repository at this point in the history
VD-463: document and test LOCK_MAX_AGE
  • Loading branch information
fredvdvd authored Jun 19, 2017
2 parents 15dcd93 + 4f8a1b3 commit 007f6a5
Show file tree
Hide file tree
Showing 6 changed files with 68 additions and 10 deletions.
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,12 @@ coverage.xml
.mr.developer.cfg

# Virtualenvs
/venv
.venv*
.cache/

# IDE junk
/.idea

# SQLite test db
/db.sqlite3
9 changes: 2 additions & 7 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -35,20 +35,15 @@ yourself, you can do that too::
do_something()
lock.release()

Note that locks can expire automatically. There is a `LOCK_MAX_AGE` settings where you can specify a default lock release value for locks in your entire Django codebase. This value can be overridden per lock by setting the `max_age` parameter.

Test
-----
You can run the tests with
::

tox

Wishlist
--------
- Add lock time-out (try to aquire a lock for up to a given TIME_OUT), like
`lockfile's <http://packages.python.org/lockfile/>`_ `FileLock.aquire
<http://packages.python.org/lockfile/lockfile.html#lockfile.FileLock.acquire>`_
- Global locks (for instance on a whole Model not just an object)

Releases
--------
v2.0.0:
Expand Down
20 changes: 20 additions & 0 deletions locking/migrations/0006_auto_20170609_1342.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11.2 on 2017-06-09 13:42
from __future__ import unicode_literals

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('locking', '0005_merge_20170504_1024'),
]

operations = [
migrations.AlterField(
model_name='nonblockinglock',
name='max_age',
field=models.PositiveIntegerField(default=0, help_text='The age of a lock before it can be overwritten. 0 means indefinitely.', verbose_name='Maximum lock age'),
),
]
2 changes: 1 addition & 1 deletion locking/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ class NonBlockingLock(models.Model):
#: The age of a lock before it can be overwritten. If it's ``MAX_AGE_FOREVER``, it will
#: never expire.
max_age = models.PositiveIntegerField(
default=DEFAULT_MAX_AGE, verbose_name=_('Maximum lock age'),
default=MAX_AGE_FOREVER, verbose_name=_('Maximum lock age'),
help_text=_('The age of a lock before it can be overwritten. '
'%s means indefinitely.' % MAX_AGE_FOREVER)
)
Expand Down
38 changes: 37 additions & 1 deletion locking/tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,13 @@
from __future__ import absolute_import
import uuid

from datetime import datetime, timedelta
from django.conf import settings

from freezegun import freeze_time

from django.contrib.auth.models import User
from django.test import TestCase
from django.test import TestCase, override_settings

from .exceptions import AlreadyLocked, RenewalError, NonexistentLock, NotLocked, Expired
from .models import NonBlockingLock, _get_lock_name
Expand Down Expand Up @@ -143,3 +146,36 @@ def test_clean(self):
# Only the non-expired lock should remain
clean_expired_locks()
self.assertEqual(NonBlockingLock.objects.get(), lock_to_be_released)

def test_implicit_cleaning_disabled(self):
"""If no max_age parameter is given and locks aren't configured to autoexpire, don't clean them up."""
assert hasattr(settings, 'LOCK_MAX_AGE') is False
initial_timestamp = datetime(2017, 1, 1)

with freeze_time(initial_timestamp):
lock_to_remain = NonBlockingLock.objects.acquire_lock(self.user)
with freeze_time(initial_timestamp + timedelta(days=9600)):
clean_expired_locks()
assert NonBlockingLock.objects.get() == lock_to_remain

@override_settings(LOCK_MAX_AGE=0)
def test_implicit_cleaning_set_to_zero(self):
"""If the LOCK_MAX_AGE setting is set to 0, no locks should autoexpire."""
initial_timestamp = datetime(2017, 1, 1)

with freeze_time(initial_timestamp):
lock_to_remain = NonBlockingLock.objects.acquire_lock(self.user)
with freeze_time(initial_timestamp + timedelta(days=9600)):
clean_expired_locks()
assert NonBlockingLock.objects.get() == lock_to_remain

@override_settings(LOCK_MAX_AGE=1)
def test_implicit_cleaning_set_to_nonzero(self):
"""If the LOCK_MAX_AGE setting is bigger than 0, locks should autoexpire."""
initial_timestamp = datetime(2017, 1, 1)

with freeze_time(initial_timestamp):
NonBlockingLock.objects.acquire_lock(self.user)
with freeze_time(initial_timestamp + timedelta(seconds=1)):
clean_expired_locks()
assert NonBlockingLock.objects.count() == 0
2 changes: 1 addition & 1 deletion pytest.ini
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[pytest]
DJANGO_SETTINGS_MODULE=test_project.settings
python_files = test*.py
norecursedirs = .* *.egg *.egg-info wheel dist build artifacts
norecursedirs = .* *.egg *.egg-info wheel dist build artifacts venv

0 comments on commit 007f6a5

Please sign in to comment.