Very basic locking mechanism for quotas

This commit is contained in:
Raphael Michel
2015-02-12 19:14:34 +01:00
parent 6537324394
commit 0d4f461000
3 changed files with 74 additions and 7 deletions
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('pretixbase', '0007_auto_20150212_0939'),
]
operations = [
migrations.AddField(
model_name='quota',
name='locked',
field=models.DateTimeField(null=True, blank=True),
preserve_default=True,
),
]
+35 -3
View File
@@ -1,6 +1,7 @@
from itertools import product
import copy
import uuid
import time
from django.db import models
from django.conf import settings
@@ -1041,6 +1042,10 @@ class Quota(Versionable):
blank=True,
verbose_name=_("Variations")
)
locked = models.DateTimeField(
null=True, blank=True
)
locked_here = False
class Meta:
verbose_name = _("Quota")
@@ -1105,12 +1110,31 @@ class Quota(Versionable):
return Quota.AVAILABILITY_OK, self.size - paid_orders - pending_valid_orders - valid_cart_positions
class LockTimeoutException(Exception):
pass
def lock(self):
"""
Issue a lock on this quota so nobody can take tickets from this quota until
you release the lock
you release the lock.
Raises an Quota.LockTimeoutException if the quota is locked every time we
try to obtain a lock.
"""
pass
retries = 5
for i in range(retries):
dt = now()
updated = Quota.objects.current.filter(
identity=self.identity, locked__isnull=True,
version_end_date__isnull=True
).update(
locked=dt
)
if updated:
self.locked_here = dt
return True
time.sleep(2 ** i / 100)
raise Quota.LockTimeoutException()
def release(self, force=False):
"""
@@ -1118,7 +1142,15 @@ class Quota(Versionable):
the lock will only be released if it was issued in _this_ python
representation of the database object.
"""
pass
if not self.locked_here and not force:
return False
updated = Quota.objects.current.filter(
identity=self.identity,
version_end_date__isnull=True
).update(
locked=None
)
return updated
class Order(Versionable):
+19 -4
View File
@@ -94,13 +94,22 @@ class CartAdd(EventViewMixin, CartActionMixin, View):
price = item.execute_restrictions() if variation is None else variation.execute_restrictions()
if price is False:
msg_some_unavailable = True
messages.error(self.request,
_('Some of the items you selected were no longer available. '
'Please see below for details.'))
if not msg_some_unavailable:
msg_some_unavailable = True
messages.error(self.request,
_('Some of the items you selected were no longer available. '
'Please see below for details.'))
continue
quotas = list(item.quotas.all()) if variation is None else list(variation.quotas.all())
if len(quotas) == 0:
if not msg_some_unavailable:
msg_some_unavailable = True
messages.error(self.request,
_('Some of the items you selected were no longer available. '
'Please see below for details.'))
continue
quota_ok = i[2]
try:
for quota in quotas:
@@ -132,6 +141,12 @@ class CartAdd(EventViewMixin, CartActionMixin, View):
price=price,
expires=now() + timedelta(minutes=30)
)
except Quota.LockTimeoutException:
if not msg_some_unavailable:
msg_some_unavailable = True
messages.error(self.request,
_('We were not able to process your request completely as the '
'server was too busy. Please try again.'))
finally:
for quota in quotas:
quota.release()