Moved locking out of the model layer

This commit is contained in:
Raphael Michel
2015-09-28 23:30:25 +02:00
parent 24c76baec2
commit 28437771cf
3 changed files with 32 additions and 32 deletions

View File

@@ -1559,8 +1559,6 @@ class Order(Versionable):
def _is_still_available(self): def _is_still_available(self):
error_messages = { error_messages = {
'unavailable': _('Some of the ordered products were no longer available.'), 'unavailable': _('Some of the ordered products were no longer available.'),
'busy': _('We were not able to process the request completely as the '
'server was too busy.'),
} }
positions = list(self.positions.all().select_related( positions = list(self.positions.all().select_related(
'item', 'variation' 'item', 'variation'
@@ -1570,31 +1568,26 @@ class Order(Versionable):
)) ))
quota_cache = {} quota_cache = {}
try: try:
with self.event.lock(): for i, op in enumerate(positions):
for i, op in enumerate(positions): quotas = list(op.item.quotas.all()) if op.variation is None else list(op.variation.quotas.all())
quotas = list(op.item.quotas.all()) if op.variation is None else list(op.variation.quotas.all()) if len(quotas) == 0:
if len(quotas) == 0: raise Quota.QuotaExceededException(error_messages['unavailable'])
raise Quota.QuotaExceededException(error_messages['unavailable'])
for quota in quotas: for quota in quotas:
# Lock the quota, so no other thread is allowed to perform sales covered by this # Lock the quota, so no other thread is allowed to perform sales covered by this
# quota while we're doing so. # quota while we're doing so.
if quota.identity not in quota_cache: if quota.identity not in quota_cache:
quota_cache[quota.identity] = quota quota_cache[quota.identity] = quota
quota.cached_availability = quota.availability()[1] quota.cached_availability = quota.availability()[1]
else: else:
# Use cached version # Use cached version
quota = quota_cache[quota.identity] quota = quota_cache[quota.identity]
quota.cached_availability -= 1 quota.cached_availability -= 1
if quota.cached_availability < 0: if quota.cached_availability < 0:
# This quota is sold out/currently unavailable, so do not sell this at all # This quota is sold out/currently unavailable, so do not sell this at all
raise Quota.QuotaExceededException(error_messages['unavailable']) raise Quota.QuotaExceededException(error_messages['unavailable'])
except Quota.QuotaExceededException as e: except Quota.QuotaExceededException as e:
return str(e) return str(e)
except EventLock.LockTimeoutException:
# Is raised when there are too many threads asking for quota locks and we were
# unaible to get one
return error_messages['busy']
return True return True
@property @property

View File

@@ -52,7 +52,7 @@ def release_event(event):
:raises EventLock.LockReleaseException: if we do not own the lock :raises EventLock.LockReleaseException: if we do not own the lock
""" """
if not hasattr(event, '_lock') or not event._lock: if not hasattr(event, '_lock') or not event._lock:
raise EventLock.LockReleaseException('') raise EventLock.LockReleaseException('Lock is not owned by this thread')
if settings.HAS_REDIS: if settings.HAS_REDIS:
return release_event_redis(event) return release_event_redis(event)
else: else:

View File

@@ -12,7 +12,9 @@ from django.utils.timezone import now
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import DetailView, ListView, TemplateView, View from django.views.generic import DetailView, ListView, TemplateView, View
from pretix.base.models import CachedFile, CachedTicket, Item, Order, Quota from pretix.base.models import (
CachedFile, CachedTicket, EventLock, Item, Order, Quota,
)
from pretix.base.services.export import export from pretix.base.services.export import export
from pretix.base.services.orders import mark_order_paid from pretix.base.services.orders import mark_order_paid
from pretix.base.services.stats import order_overview from pretix.base.services.stats import order_overview
@@ -247,12 +249,17 @@ class OrderExtend(OrderView):
if oldvalue > now(): if oldvalue > now():
self.form.save() self.form.save()
else: else:
is_available = self.order._is_still_available() try:
if is_available is True: with self.order.event.lock():
self.form.save() is_available = self.order._is_still_available()
messages.success(self.request, _('The payment term has been changed.')) if is_available is True:
else: self.form.save()
messages.error(self.request, is_available) messages.success(self.request, _('The payment term has been changed.'))
else:
messages.error(self.request, is_available)
except EventLock.LockTimeoutException:
messages.error(self.request, _('We were not able to process the request completely as the '
'server was too busy.'))
return self._redirect_back() return self._redirect_back()
else: else:
return self.get(*args, **kwargs) return self.get(*args, **kwargs)