From 2bbb028681578de183aefbba92689efd96e98928 Mon Sep 17 00:00:00 2001 From: Leonard Techel Date: Wed, 19 Jan 2022 21:47:01 +0100 Subject: [PATCH] perf(backend): Remove N+1 on the parameters endpoint --- backend/dpt_app/trails/models.py | 15 +++++---------- backend/dpt_app/trails/views.py | 8 +++++++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/backend/dpt_app/trails/models.py b/backend/dpt_app/trails/models.py index 8495305..b938239 100644 --- a/backend/dpt_app/trails/models.py +++ b/backend/dpt_app/trails/models.py @@ -68,8 +68,11 @@ class Meta: @property @admin.display(description=_("Maximum Game Duration")) def max_clock_duration(self): - """Get the maximum clock duration, in seconds""" - return Parameter.max_clock_duration(self) + """Get the maximum clock duration in seconds at which the first parameter is zero""" + query = self.parameter.annotate( + dur_when_zero=-1 * (F('initial_value') + F('value')) / ((F('game__clock_speed') / F('game__clock_unit')) * F('rate'))) + + return query.aggregate(Min('dur_when_zero'))['dur_when_zero__min'] or 0 @property @admin.display(description=_("Total Game Duration")) @@ -195,14 +198,6 @@ class Meta: verbose_name=_("Game") ) - @staticmethod - def max_clock_duration(game: Game): - """Get the maximum game clock duration in seconds at which the first parameter is zero""" - query = Parameter.objects.filter(game=game).annotate( - dur_when_zero=-1 * (F('initial_value') + F('value')) / ((F('game__clock_speed') / F('game__clock_unit')) * F('rate'))) - - return query.aggregate(Min('dur_when_zero'))['dur_when_zero__min'] or 0 - def value_at(self, clock_duration): """Get the parameter value at a specific game clock duration""" value = round((self.initial_value + self.value) + clock_duration diff --git a/backend/dpt_app/trails/views.py b/backend/dpt_app/trails/views.py index 13e6529..54eca5d 100644 --- a/backend/dpt_app/trails/views.py +++ b/backend/dpt_app/trails/views.py @@ -226,6 +226,12 @@ def get_bearer(request): def func_parameter_get(game, player): + # By retrieving the duration only once, then call `.value_at(dur)` + # on every parameter instead of `.current_value`, we only need a + # single SQL query for the duration instead of N, where N is the + # number of parameters. + dur = game.total_clock_duration + response = [] for parameter in game.parameter.all(): response.append( @@ -234,7 +240,7 @@ def func_parameter_get(game, player): "id": parameter.name, "attributes": { "scope": parameter.scope, - "value": parameter.current_value, + "value": parameter.value_at(dur), "rate": parameter.rate, "min": parameter.min_value, "max": parameter.max_value