forked from CGM_Public/pretix_original
Offload more work to database replica
This commit is contained in:
@@ -164,7 +164,7 @@ class EventMixin:
|
|||||||
def annotated(cls, qs, channel='web'):
|
def annotated(cls, qs, channel='web'):
|
||||||
from pretix.base.models import Item, ItemVariation, Quota
|
from pretix.base.models import Item, ItemVariation, Quota
|
||||||
|
|
||||||
sq_active_item = Item.objects.filter_available(channel=channel).filter(
|
sq_active_item = Item.objects.using(settings.DATABASE_REPLICA).filter_available(channel=channel).filter(
|
||||||
Q(variations__isnull=True)
|
Q(variations__isnull=True)
|
||||||
& Q(quotas__pk=OuterRef('pk'))
|
& Q(quotas__pk=OuterRef('pk'))
|
||||||
).order_by().values_list('quotas__pk').annotate(
|
).order_by().values_list('quotas__pk').annotate(
|
||||||
@@ -186,7 +186,7 @@ class EventMixin:
|
|||||||
Prefetch(
|
Prefetch(
|
||||||
'quotas',
|
'quotas',
|
||||||
to_attr='active_quotas',
|
to_attr='active_quotas',
|
||||||
queryset=Quota.objects.annotate(
|
queryset=Quota.objects.using(settings.DATABASE_REPLICA).annotate(
|
||||||
active_items=Subquery(sq_active_item, output_field=models.TextField()),
|
active_items=Subquery(sq_active_item, output_field=models.TextField()),
|
||||||
active_variations=Subquery(sq_active_variation, output_field=models.TextField()),
|
active_variations=Subquery(sq_active_variation, output_field=models.TextField()),
|
||||||
).exclude(
|
).exclude(
|
||||||
|
|||||||
@@ -1290,7 +1290,8 @@ class Quota(LoggedModel):
|
|||||||
'cached_availability_state', 'cached_availability_number', 'cached_availability_time',
|
'cached_availability_state', 'cached_availability_number', 'cached_availability_time',
|
||||||
'cached_availability_paid_orders'
|
'cached_availability_paid_orders'
|
||||||
],
|
],
|
||||||
clear_cache=False
|
clear_cache=False,
|
||||||
|
using='default'
|
||||||
)
|
)
|
||||||
|
|
||||||
if _cache is not None:
|
if _cache is not None:
|
||||||
|
|||||||
@@ -76,3 +76,21 @@ class GroupConcat(Aggregate):
|
|||||||
function='string_agg',
|
function='string_agg',
|
||||||
template="%(function)s(%(field)s::text, '%(separator)s')",
|
template="%(function)s(%(field)s::text, '%(separator)s')",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
class ReplicaRouter:
|
||||||
|
|
||||||
|
def db_for_read(self, model, **hints):
|
||||||
|
return 'default'
|
||||||
|
|
||||||
|
def db_for_write(self, model, **hints):
|
||||||
|
return 'default'
|
||||||
|
|
||||||
|
def allow_relation(self, obj1, obj2, **hints):
|
||||||
|
db_list = ('default', 'replica')
|
||||||
|
if obj1._state.db in db_list and obj2._state.db in db_list:
|
||||||
|
return True
|
||||||
|
return None
|
||||||
|
|
||||||
|
def allow_migrate(self, db, app_label, model_name=None, **hintrs):
|
||||||
|
return True
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
|||||||
if hasattr(request, '_event_detected'):
|
if hasattr(request, '_event_detected'):
|
||||||
return
|
return
|
||||||
|
|
||||||
|
db = 'default'
|
||||||
|
if request.method == 'GET':
|
||||||
|
db = settings.DATABASE_REPLICA
|
||||||
|
|
||||||
url = resolve(request.path_info)
|
url = resolve(request.path_info)
|
||||||
try:
|
try:
|
||||||
if hasattr(request, 'organizer_domain'):
|
if hasattr(request, 'organizer_domain'):
|
||||||
@@ -31,24 +35,24 @@ def _detect_event(request, require_live=True, require_plugin=None):
|
|||||||
path = "/" + request.get_full_path().split("/", 2)[-1]
|
path = "/" + request.get_full_path().split("/", 2)[-1]
|
||||||
return redirect(path)
|
return redirect(path)
|
||||||
|
|
||||||
request.event = request.organizer.events\
|
request.event = request.organizer.events.using(db).get(
|
||||||
.get(
|
slug=url.kwargs['event'],
|
||||||
slug=url.kwargs['event'],
|
organizer=request.organizer,
|
||||||
organizer=request.organizer,
|
)
|
||||||
)
|
|
||||||
request.organizer = request.organizer
|
request.organizer = request.organizer
|
||||||
else:
|
else:
|
||||||
# We are on our main domain
|
# We are on our main domain
|
||||||
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
if 'event' in url.kwargs and 'organizer' in url.kwargs:
|
||||||
request.event = Event.objects\
|
request.event = Event.objects\
|
||||||
.select_related('organizer')\
|
.select_related('organizer')\
|
||||||
|
.using(db)\
|
||||||
.get(
|
.get(
|
||||||
slug=url.kwargs['event'],
|
slug=url.kwargs['event'],
|
||||||
organizer__slug=url.kwargs['organizer']
|
organizer__slug=url.kwargs['organizer']
|
||||||
)
|
)
|
||||||
request.organizer = request.event.organizer
|
request.organizer = request.event.organizer
|
||||||
elif 'organizer' in url.kwargs:
|
elif 'organizer' in url.kwargs:
|
||||||
request.organizer = Organizer.objects.get(
|
request.organizer = Organizer.objects.using(db).get(
|
||||||
slug=url.kwargs['organizer']
|
slug=url.kwargs['organizer']
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -50,32 +50,34 @@ def item_group_by_category(items):
|
|||||||
|
|
||||||
|
|
||||||
def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
|
def get_grouped_items(event, subevent=None, voucher=None, channel='web'):
|
||||||
items = event.items.filter_available(channel=channel, voucher=voucher).select_related(
|
items = event.items.using(settings.DATABASE_REPLICA).filter_available(channel=channel, voucher=voucher).select_related(
|
||||||
'category', 'tax_rule', # for re-grouping
|
'category', 'tax_rule', # for re-grouping
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
Prefetch('quotas',
|
Prefetch('quotas',
|
||||||
to_attr='_subevent_quotas',
|
to_attr='_subevent_quotas',
|
||||||
queryset=event.quotas.filter(subevent=subevent)),
|
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||||
Prefetch('bundles',
|
Prefetch('bundles',
|
||||||
queryset=ItemBundle.objects.prefetch_related(
|
queryset=ItemBundle.objects.using(settings.DATABASE_REPLICA).prefetch_related(
|
||||||
Prefetch('bundled_item',
|
Prefetch('bundled_item',
|
||||||
queryset=event.items.select_related('tax_rule').prefetch_related(
|
queryset=event.items.using(settings.DATABASE_REPLICA).select_related('tax_rule').prefetch_related(
|
||||||
Prefetch('quotas',
|
Prefetch('quotas',
|
||||||
to_attr='_subevent_quotas',
|
to_attr='_subevent_quotas',
|
||||||
queryset=event.quotas.filter(subevent=subevent)),
|
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||||
)),
|
)),
|
||||||
Prefetch('bundled_variation',
|
Prefetch('bundled_variation',
|
||||||
queryset=ItemVariation.objects.select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
|
queryset=ItemVariation.objects.using(
|
||||||
|
settings.DATABASE_REPLICA
|
||||||
|
).select_related('item', 'item__tax_rule').filter(item__event=event).prefetch_related(
|
||||||
Prefetch('quotas',
|
Prefetch('quotas',
|
||||||
to_attr='_subevent_quotas',
|
to_attr='_subevent_quotas',
|
||||||
queryset=event.quotas.filter(subevent=subevent)),
|
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent)),
|
||||||
)),
|
)),
|
||||||
)),
|
)),
|
||||||
Prefetch('variations', to_attr='available_variations',
|
Prefetch('variations', to_attr='available_variations',
|
||||||
queryset=ItemVariation.objects.filter(active=True, quotas__isnull=False).prefetch_related(
|
queryset=ItemVariation.objects.using(settings.DATABASE_REPLICA).filter(active=True, quotas__isnull=False).prefetch_related(
|
||||||
Prefetch('quotas',
|
Prefetch('quotas',
|
||||||
to_attr='_subevent_quotas',
|
to_attr='_subevent_quotas',
|
||||||
queryset=event.quotas.filter(subevent=subevent))
|
queryset=event.quotas.using(settings.DATABASE_REPLICA).filter(subevent=subevent))
|
||||||
).distinct()),
|
).distinct()),
|
||||||
).annotate(
|
).annotate(
|
||||||
quotac=Count('quotas'),
|
quotac=Count('quotas'),
|
||||||
@@ -229,7 +231,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
|||||||
|
|
||||||
if request.event.has_subevents:
|
if request.event.has_subevents:
|
||||||
if 'subevent' in kwargs:
|
if 'subevent' in kwargs:
|
||||||
self.subevent = request.event.subevents.filter(pk=kwargs['subevent'], active=True).first()
|
self.subevent = request.event.subevents.using(settings.DATABASE_REPLICA).filter(pk=kwargs['subevent'], active=True).first()
|
||||||
if not self.subevent:
|
if not self.subevent:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
return super().get(request, *args, **kwargs)
|
return super().get(request, *args, **kwargs)
|
||||||
@@ -287,7 +289,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
|||||||
|
|
||||||
ebd = defaultdict(list)
|
ebd = defaultdict(list)
|
||||||
add_subevents_for_days(
|
add_subevents_for_days(
|
||||||
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request),
|
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel).using(settings.DATABASE_REPLICA), self.request),
|
||||||
before, after, ebd, set(), self.request.event,
|
before, after, ebd, set(), self.request.event,
|
||||||
kwargs.get('cart_namespace')
|
kwargs.get('cart_namespace')
|
||||||
)
|
)
|
||||||
@@ -297,7 +299,7 @@ class EventIndex(EventViewMixin, EventListMixin, CartMixin, TemplateView):
|
|||||||
context['years'] = range(now().year - 2, now().year + 3)
|
context['years'] = range(now().year - 2, now().year + 3)
|
||||||
else:
|
else:
|
||||||
context['subevent_list'] = self.request.event.subevents_sorted(
|
context['subevent_list'] = self.request.event.subevents_sorted(
|
||||||
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel), self.request)
|
filter_qs_by_attr(self.request.event.subevents_annotated(self.request.sales_channel).using(settings.DATABASE_REPLICA), self.request)
|
||||||
)
|
)
|
||||||
|
|
||||||
context['show_cart'] = (
|
context['show_cart'] = (
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ class EventListMixin:
|
|||||||
|
|
||||||
def _get_event_queryset(self):
|
def _get_event_queryset(self):
|
||||||
query = Q(is_public=True) & Q(live=True)
|
query = Q(is_public=True) & Q(live=True)
|
||||||
qs = self.request.organizer.events.filter(query)
|
qs = self.request.organizer.events.using(settings.DATABASE_REPLICA).filter(query)
|
||||||
qs = qs.annotate(
|
qs = qs.annotate(
|
||||||
min_from=Min('subevents__date_from'),
|
min_from=Min('subevents__date_from'),
|
||||||
min_to=Min('subevents__date_to'),
|
min_to=Min('subevents__date_to'),
|
||||||
@@ -126,7 +126,7 @@ class EventListMixin:
|
|||||||
|
|
||||||
def _set_month_to_next_subevent(self):
|
def _set_month_to_next_subevent(self):
|
||||||
tz = pytz.timezone(self.request.event.settings.timezone)
|
tz = pytz.timezone(self.request.event.settings.timezone)
|
||||||
next_sev = self.request.event.subevents.filter(
|
next_sev = self.request.event.subevents.using(settings.DATABASE_REPLICA).filter(
|
||||||
active=True,
|
active=True,
|
||||||
is_public=True,
|
is_public=True,
|
||||||
date_from__gte=now()
|
date_from__gte=now()
|
||||||
@@ -141,14 +141,14 @@ class EventListMixin:
|
|||||||
self.month = now().month
|
self.month = now().month
|
||||||
|
|
||||||
def _set_month_to_next_event(self):
|
def _set_month_to_next_event(self):
|
||||||
next_ev = filter_qs_by_attr(Event.objects.filter(
|
next_ev = filter_qs_by_attr(Event.objects.using(settings.DATABASE_REPLICA).filter(
|
||||||
organizer=self.request.organizer,
|
organizer=self.request.organizer,
|
||||||
live=True,
|
live=True,
|
||||||
is_public=True,
|
is_public=True,
|
||||||
date_from__gte=now(),
|
date_from__gte=now(),
|
||||||
has_subevents=False
|
has_subevents=False
|
||||||
), self.request).order_by('date_from').first()
|
), self.request).order_by('date_from').first()
|
||||||
next_sev = filter_qs_by_attr(SubEvent.objects.filter(
|
next_sev = filter_qs_by_attr(SubEvent.objects.using(settings.DATABASE_REPLICA).filter(
|
||||||
event__organizer=self.request.organizer,
|
event__organizer=self.request.organizer,
|
||||||
event__is_public=True,
|
event__is_public=True,
|
||||||
event__live=True,
|
event__live=True,
|
||||||
@@ -353,14 +353,14 @@ class CalendarView(OrganizerViewMixin, EventListMixin, TemplateView):
|
|||||||
def _events_by_day(self, before, after):
|
def _events_by_day(self, before, after):
|
||||||
ebd = defaultdict(list)
|
ebd = defaultdict(list)
|
||||||
timezones = set()
|
timezones = set()
|
||||||
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web'), before, after, ebd, timezones)
|
add_events_for_days(self.request, Event.annotated(self.request.organizer.events, 'web').using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
|
||||||
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
|
add_subevents_for_days(filter_qs_by_attr(SubEvent.annotated(SubEvent.objects.filter(
|
||||||
event__organizer=self.request.organizer,
|
event__organizer=self.request.organizer,
|
||||||
event__is_public=True,
|
event__is_public=True,
|
||||||
event__live=True,
|
event__live=True,
|
||||||
).prefetch_related(
|
).prefetch_related(
|
||||||
'event___settings_objects', 'event__organizer___settings_objects'
|
'event___settings_objects', 'event__organizer___settings_objects'
|
||||||
)), self.request), before, after, ebd, timezones)
|
)), self.request).using(settings.DATABASE_REPLICA), before, after, ebd, timezones)
|
||||||
self._multiple_timezones = len(timezones) > 1
|
self._multiple_timezones = len(timezones) > 1
|
||||||
return ebd
|
return ebd
|
||||||
|
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ if config.has_section('replica'):
|
|||||||
'COLLATION': 'utf8mb4_unicode_ci',
|
'COLLATION': 'utf8mb4_unicode_ci',
|
||||||
} if 'mysql' in db_backend else {}
|
} if 'mysql' in db_backend else {}
|
||||||
}
|
}
|
||||||
|
DATABASE_ROUTERS = ['pretix.helpers.database.ReplicaRouter']
|
||||||
|
|
||||||
STATIC_URL = config.get('urls', 'static', fallback='/static/')
|
STATIC_URL = config.get('urls', 'static', fallback='/static/')
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user