Skip to content

Commit

Permalink
fix(change): cache project renaming lookup
Browse files Browse the repository at this point in the history
There is no index on the old field so this ends up being index based
scan which is expensive. So better to cache the few results we have and
reuse it.

Fixes WEBLATE-7QC
  • Loading branch information
nijel committed May 3, 2024
1 parent 7f7341d commit 21a4cf2
Show file tree
Hide file tree
Showing 2 changed files with 27 additions and 10 deletions.
12 changes: 2 additions & 10 deletions weblate/middleware.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,16 +112,8 @@ def fixup_project(self, slug, request):
except Project.MultipleObjectsReturned:
return None
except Project.DoesNotExist:
try:
project = (
Change.objects.filter(
action=Change.ACTION_RENAME_PROJECT,
old=slug,
)
.order()[0]
.project
)
except IndexError:
project = Change.objects.lookup_project_rename(slug)
if project is None:
return None

request.user.check_access(project)
Expand Down
25 changes: 25 additions & 0 deletions weblate/trans/models/change.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
from weblate.trans.models import Translation


CHANGE_PROJECT_LOOKUP_KEY = "change:project-lookup"


class ChangeQuerySet(models.QuerySet["Change"]):
def content(self, prefetch=False):
"""Return queryset with content changes."""
Expand Down Expand Up @@ -206,6 +209,25 @@ def filter_projects(self, user):
return self
return self.filter(project__in=user.allowed_projects)

def lookup_project_rename(self, name: str) -> Project:
lookup = cache.get(CHANGE_PROJECT_LOOKUP_KEY)
if lookup is None:
lookup = self.generate_project_rename_lookup()
if name not in lookup:
return None
try:
return Project.objects.get(pk=lookup[name])
except Project.DoesNotExist:
return None

def generate_project_rename_lookup(self) -> dict[str, int]:
lookup = {}
for change in self.filter(action=Change.ACTION_RENAME_PROJECT).order():
if change.old not in lookup:
lookup[change.old] = change.project_id
cache.set(CHANGE_PROJECT_LOOKUP_KEY, lookup, 3600 * 24 * 7)
return lookup


class ChangeManager(models.Manager["Change"]):
def create(self, *, user=None, **kwargs):
Expand Down Expand Up @@ -641,6 +663,9 @@ def save(self, *args, **kwargs) -> None:
# Make sure stats is updated at the end of transaction
self.translation.invalidate_cache()

if self.action == Change.ACTION_RENAME_PROJECT:
Change.objects.generate_project_rename_lookup()

def get_absolute_url(self):
"""Return link either to unit or translation."""
if self.unit is not None:
Expand Down

0 comments on commit 21a4cf2

Please sign in to comment.