Skip to content

Commit 531d80a

Browse files
committed
[Fixes #13809] Improve i18n performance - gemini and tests
1 parent 7e66094 commit 531d80a

File tree

3 files changed

+39
-28
lines changed

3 files changed

+39
-28
lines changed

geonode/base/i18n.py

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import logging
2+
import threading
23
import time
34

45
from django.db import connection
@@ -87,39 +88,43 @@ def __init__(self):
8788
# the cache has the lang as key, and I18nCacheEntry as a value:
8889
self.lang_cache = {}
8990
self._last_check = 0
91+
self._lock = threading.Lock()
9092

9193
def get_entry(self, lang, data_key):
9294
"""
9395
returns date:str, data
9496
date is needed for checking the entry freshness when setting info
9597
data may be None if not cached or expired
9698
"""
97-
cached_entry: I18nCacheEntry = self.lang_cache.get(lang, None)
98-
99-
time_now = time.time()
100-
needs_check = time_now - self._last_check > I18nCache.CHECK_INTERVAL
101-
102-
# if not needs_check:
103-
# logger.debug(f"No cache check needed {lang}:{data_key} @ {cached_entry}")
104-
# else:
105-
# logger.debug(f"Cache check needed {lang}:{data_key} @ {cached_entry}")
106-
107-
if needs_check or not cached_entry:
108-
self._last_check = time_now
109-
thesaurus_date = ( # may be none if thesaurus does not exist
110-
Thesaurus.objects.filter(identifier=I18N_THESAURUS_IDENTIFIER).values_list("date", flat=True).first()
111-
)
112-
if cached_entry and cached_entry.date != thesaurus_date:
113-
logger.info(f"Cache for {lang}:{data_key} needs to be recreated")
114-
return thesaurus_date, None
115-
if not cached_entry:
116-
logger.info(f"Cache for {lang}:{data_key} needs to be created")
117-
return thesaurus_date, None
118-
119-
# logger.debug(f"Returning cached entry for {lang}:{data_key} @ {cached_entry.date}")
120-
return cached_entry.date, cached_entry.caches.get(data_key, None)
121-
122-
def set(self, lang: str, data_key: str, data: dict, request_date: str):
99+
with self._lock:
100+
cached_entry: I18nCacheEntry = self.lang_cache.get(lang, None)
101+
102+
time_now = time.time()
103+
needs_check = time_now - self._last_check > I18nCache.CHECK_INTERVAL
104+
105+
# if not needs_check:
106+
# logger.debug(f"No cache check needed {lang}:{data_key} @ {cached_entry}")
107+
# else:
108+
# logger.debug(f"Cache check needed {lang}:{data_key} @ {cached_entry}")
109+
110+
if needs_check or not cached_entry:
111+
self._last_check = time_now
112+
thesaurus_date = ( # may be none if thesaurus does not exist
113+
Thesaurus.objects.filter(identifier=I18N_THESAURUS_IDENTIFIER)
114+
.values_list("date", flat=True)
115+
.first()
116+
)
117+
if cached_entry and cached_entry.date != thesaurus_date:
118+
logger.info(f"Cache for {lang}:{data_key} needs to be recreated")
119+
return thesaurus_date, None
120+
if not cached_entry:
121+
logger.info(f"Cache for {lang}:{data_key} needs to be created")
122+
return thesaurus_date, None
123+
124+
# logger.debug(f"Returning cached entry for {lang}:{data_key} @ {cached_entry.date}")
125+
return cached_entry.date, cached_entry.caches.get(data_key, None)
126+
127+
def set(self, lang: str, data_key: str, data, request_date: str):
123128
# TODO: check if lang is allowed
124129
cached_entry: I18nCacheEntry = self.lang_cache.setdefault(lang, I18nCacheEntry())
125130

@@ -132,15 +137,21 @@ def set(self, lang: str, data_key: str, data: dict, request_date: str):
132137
logger.debug(f"Caching lang:{lang} key:{data_key} date:{request_date}")
133138
cached_entry.date = latest_date
134139
cached_entry.caches[data_key] = data
140+
return True
135141
else:
136142
logger.warning(
137143
f"Cache will not be updated for lang:{lang} key:{data_key} reqdate:{request_date} latest:{latest_date}"
138144
)
145+
return False
139146

140147
def clear(self):
141148
logger.info("Clearing i18n cache")
142149
self.lang_cache.clear()
143150

151+
def force_check(self):
152+
"""For testing: forces a check against the DB on the next get_entry call."""
153+
self._last_check = 0
154+
144155

145156
class LabelResolver:
146157
CACHE_KEY_LABELS = "labels"

geonode/base/signals.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ def _resolve_new_date(old, new):
4545
try:
4646
new_parsed = datetime.fromisoformat(new)
4747
old_parsed = datetime.fromisoformat(old)
48-
except Exception:
48+
except (ValueError, TypeError):
4949
return new
5050

5151
if old == new:

geonode/metadata/tests/test_i18n.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ def _add_label(self, about, lang, label):
5656
if lang and label:
5757
ThesaurusKeywordLabel.objects.create(keyword=tk, label=label, lang=lang)
5858
# this is needed to bypass invalidation optimization
59-
i18nCache._last_check = 0
59+
i18nCache.force_check()
6060

6161
def tearDown(self):
6262
super().tearDown()

0 commit comments

Comments
 (0)