diff --git a/src/pretix/base/models.py b/src/pretix/base/models.py index 35cc908add..b0169acb31 100644 --- a/src/pretix/base/models.py +++ b/src/pretix/base/models.py @@ -1559,8 +1559,6 @@ class Order(Versionable): def _is_still_available(self): error_messages = { '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( 'item', 'variation' @@ -1570,31 +1568,26 @@ class Order(Versionable): )) quota_cache = {} try: - with self.event.lock(): - for i, op in enumerate(positions): - quotas = list(op.item.quotas.all()) if op.variation is None else list(op.variation.quotas.all()) - if len(quotas) == 0: - raise Quota.QuotaExceededException(error_messages['unavailable']) + for i, op in enumerate(positions): + quotas = list(op.item.quotas.all()) if op.variation is None else list(op.variation.quotas.all()) + if len(quotas) == 0: + raise Quota.QuotaExceededException(error_messages['unavailable']) - for quota in quotas: - # Lock the quota, so no other thread is allowed to perform sales covered by this - # quota while we're doing so. - if quota.identity not in quota_cache: - quota_cache[quota.identity] = quota - quota.cached_availability = quota.availability()[1] - else: - # Use cached version - quota = quota_cache[quota.identity] - quota.cached_availability -= 1 - if quota.cached_availability < 0: - # This quota is sold out/currently unavailable, so do not sell this at all - raise Quota.QuotaExceededException(error_messages['unavailable']) + for quota in quotas: + # Lock the quota, so no other thread is allowed to perform sales covered by this + # quota while we're doing so. + if quota.identity not in quota_cache: + quota_cache[quota.identity] = quota + quota.cached_availability = quota.availability()[1] + else: + # Use cached version + quota = quota_cache[quota.identity] + quota.cached_availability -= 1 + if quota.cached_availability < 0: + # This quota is sold out/currently unavailable, so do not sell this at all + raise Quota.QuotaExceededException(error_messages['unavailable']) except Quota.QuotaExceededException as 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 @property diff --git a/src/pretix/base/services/locking.py b/src/pretix/base/services/locking.py index b87acc3470..bdc8c5a0db 100644 --- a/src/pretix/base/services/locking.py +++ b/src/pretix/base/services/locking.py @@ -52,7 +52,7 @@ def release_event(event): :raises EventLock.LockReleaseException: if we do not own the 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: return release_event_redis(event) else: diff --git a/src/pretix/control/views/orders.py b/src/pretix/control/views/orders.py index 7fbb9d2d10..d9f97e917e 100644 --- a/src/pretix/control/views/orders.py +++ b/src/pretix/control/views/orders.py @@ -12,7 +12,9 @@ from django.utils.timezone import now from django.utils.translation import ugettext_lazy as _ 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.orders import mark_order_paid from pretix.base.services.stats import order_overview @@ -247,12 +249,17 @@ class OrderExtend(OrderView): if oldvalue > now(): self.form.save() else: - is_available = self.order._is_still_available() - if is_available is True: - self.form.save() - messages.success(self.request, _('The payment term has been changed.')) - else: - messages.error(self.request, is_available) + try: + with self.order.event.lock(): + is_available = self.order._is_still_available() + if is_available is True: + self.form.save() + 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() else: return self.get(*args, **kwargs)