Skip to content

Commit

Permalink
[#2516] Scope ZGW client cache keys by endpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
swrichards committed Jun 5, 2024
1 parent efe3de2 commit 54f2d72
Show file tree
Hide file tree
Showing 2 changed files with 75 additions and 35 deletions.
34 changes: 22 additions & 12 deletions src/open_inwoner/openzaak/clients.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ def fetch_cases(
return []

@cache_result(
"cases:{user_bsn}:{max_requests}:{identificatie}",
"{self.base_url}:cases:{user_bsn}:{max_requests}:{identificatie}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_cases_by_bsn(
Expand Down Expand Up @@ -114,7 +114,7 @@ def fetch_cases_by_bsn(
return cases

@cache_result(
"cases:{kvk_or_rsin}:{vestigingsnummer}:{max_requests}:{zaak_identificatie}",
"{self.base_url}:cases:{kvk_or_rsin}:{vestigingsnummer}:{max_requests}:{zaak_identificatie}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_cases_by_kvk_or_rsin(
Expand Down Expand Up @@ -174,7 +174,10 @@ def fetch_cases_by_kvk_or_rsin(

return cases

@cache_result("single_case:{case_uuid}", timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT)
@cache_result(
"{self.base_url}:single_case:{case_uuid}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_single_case(self, case_uuid: str) -> Zaak | None:
try:
response = self.get(f"zaken/{case_uuid}", headers=CRS_HEADERS)
Expand All @@ -200,7 +203,8 @@ def fetch_case_by_url_no_cache(self, case_url: str) -> Zaak | None:
return case

@cache_result(
"single_case_information_object:{url}", timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT
"{self.base_url}:single_case_information_object:{url}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_single_case_information_object(
self, url: str
Expand Down Expand Up @@ -246,11 +250,14 @@ def fetch_status_history_no_cache(self, case_url: str) -> list[Status]:

return statuses

@cache_result("status_history:{case_url}", timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT)
@cache_result(
"{self.base_url}:status_history:{case_url}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_status_history(self, case_url: str) -> list[Status]:
return self.fetch_status_history_no_cache(case_url)

@cache_result("status:{status_url}", timeout=60 * 60)
@cache_result("{self.base_url}:status:{status_url}", timeout=60 * 60)
def fetch_single_status(self, status_url: str) -> Status | None:
try:
response = self.get(url=status_url)
Expand All @@ -264,7 +271,7 @@ def fetch_single_status(self, status_url: str) -> Status | None:
return status

@cache_result(
"case_roles:{case_url}:{role_desc_generic}",
"{self.base_url}:case_roles:{case_url}:{role_desc_generic}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_case_roles(
Expand Down Expand Up @@ -385,7 +392,8 @@ def fetch_case_information_objects_for_case_and_info(
return case_info_objects

@cache_result(
"single_result:{result_url}", timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT
"{self.base_url}:single_result:{result_url}",
timeout=settings.CACHE_ZGW_ZAKEN_TIMEOUT,
)
def fetch_single_result(self, result_url: str) -> Resultaat | None:
try:
Expand Down Expand Up @@ -453,7 +461,8 @@ def fetch_result_types_no_cache(self, case_type_url: str) -> list[ResultaatType]
return result_types

@cache_result(
"status_type:{status_type_url}", timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT
"{self.base_url}:status_type:{status_type_url}",
timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT,
)
def fetch_single_status_type(self, status_type_url: str) -> StatusType | None:
try:
Expand All @@ -468,7 +477,7 @@ def fetch_single_status_type(self, status_type_url: str) -> StatusType | None:
return status_type

@cache_result(
"resultaat_type:{resultaat_type_url}",
"{self.base_url}:resultaat_type:{resultaat_type_url}",
timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT,
)
def fetch_single_resultaat_type(
Expand Down Expand Up @@ -522,7 +531,8 @@ def fetch_case_types_by_identification_no_cache(
return zaak_types

@cache_result(
"case_type:{case_type_url}", timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT
"{self.base_url}:case_type:{case_type_url}",
timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT,
)
def fetch_single_case_type(self, case_type_url: str) -> ZaakType | None:
try:
Expand Down Expand Up @@ -553,7 +563,7 @@ def fetch_catalogs_no_cache(self) -> list[Catalogus]:
return catalogs

@cache_result(
"information_object_type:{information_object_type_url}",
"{self.base_url}:information_object_type:{information_object_type_url}",
timeout=settings.CACHE_ZGW_CATALOGI_TIMEOUT,
)
def fetch_single_information_object_type(
Expand Down
76 changes: 53 additions & 23 deletions src/open_inwoner/openzaak/tests/test_cases_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,13 +196,17 @@ def test_case_types_are_cached(self, m):
self._setUpMocks(m)

# Cache is empty before the request
self.assertIsNone(cache.get(f"case_type:{self.zaaktype['url']}"))
self.assertIsNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.zaaktype['url']}")
)

self.client.force_login(user=self.user)
self.client.get(self.inner_url, HTTP_HX_REQUEST="true")

# Case type is cached after the request
self.assertIsNotNone(cache.get(f"case_type:{self.zaaktype['url']}"))
self.assertIsNotNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.zaaktype['url']}")
)

def test_cached_case_types_are_deleted_after_one_day(self, m):
self._setUpMocks(m)
Expand All @@ -213,30 +217,42 @@ def test_cached_case_types_are_deleted_after_one_day(self, m):

# After one day the results should be deleted
frozen_time.tick(delta=datetime.timedelta(days=1))
self.assertIsNone(cache.get(f"case_type:{self.zaaktype['url']}"))
self.assertIsNone(
cache.get(f"{ZAKEN_ROOT}:case_type:{self.zaaktype['url']}")
)

def test_cached_case_types_in_combination_with_new_ones(self, m):
self._setUpMocks(m)

with freeze_time("2022-01-01 12:00") as frozen_time:
# First attempt
self.assertIsNone(cache.get(f"case_type:{self.zaaktype['url']}"))
self.assertIsNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.zaaktype['url']}")
)

self.client.force_login(user=self.user)
self.client.get(self.inner_url, HTTP_HX_REQUEST="true")

self.assertIsNotNone(cache.get(f"case_type:{self.zaaktype['url']}"))
self.assertIsNotNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.zaaktype['url']}")
)

# Second attempt with new case and case type
self._setUpNewMock(m)
# Wait 3 minutes for the list cases cache to expire
frozen_time.tick(delta=datetime.timedelta(minutes=3))
self.assertIsNone(cache.get(f"case_type:{self.new_zaaktype['url']}"))
self.assertIsNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.new_zaaktype['url']}")
)

self.client.get(self.inner_url, HTTP_HX_REQUEST="true")

self.assertIsNotNone(cache.get(f"case_type:{self.zaaktype['url']}"))
self.assertIsNotNone(cache.get(f"case_type:{self.new_zaaktype['url']}"))
self.assertIsNotNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.zaaktype['url']}")
)
self.assertIsNotNone(
cache.get(f"{CATALOGI_ROOT}:case_type:{self.new_zaaktype['url']}")
)

def test_cached_status_types_are_deleted_after_one_day(self, m):
self._setUpMocks(m)
Expand All @@ -248,25 +264,29 @@ def test_cached_status_types_are_deleted_after_one_day(self, m):
# After one day the results should be deleted
frozen_time.tick(delta=datetime.timedelta(hours=24))
self.assertIsNone(
cache.get(f"status_types_for_case_type:{self.zaaktype['url']}")
cache.get(
f"{ZAKEN_ROOT}:status_types_for_case_type:{self.zaaktype['url']}"
)
)
self.assertIsNone(
cache.get(f"status_types_for_case_type:{self.zaaktype['url']}")
cache.get(
f"{ZAKEN_ROOT}:status_types_for_case_type:{self.zaaktype['url']}"
)
)

def test_statuses_are_cached(self, m):
self._setUpMocks(m)

# Cache is empty before the request
self.assertIsNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNone(cache.get(f"status:{self.status2['url']}"))
self.assertIsNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status1['url']}"))
self.assertIsNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status2['url']}"))

self.client.force_login(user=self.user)
self.client.get(self.inner_url, HTTP_HX_REQUEST="true")

# Status is cached after the request
self.assertIsNotNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNotNone(cache.get(f"status:{self.status2['url']}"))
self.assertIsNotNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status1['url']}"))
self.assertIsNotNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status2['url']}"))

def test_cached_statuses_are_deleted_after_one_hour(self, m):
self._setUpMocks(m)
Expand All @@ -277,22 +297,26 @@ def test_cached_statuses_are_deleted_after_one_hour(self, m):

# After one hour the results should be deleted
frozen_time.tick(delta=datetime.timedelta(hours=1))
self.assertIsNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNone(cache.get(f"status:{self.status2['url']}"))
self.assertIsNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status1['url']}"))
self.assertIsNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status2['url']}"))

def test_cached_statuses_in_combination_with_new_ones(self, m):
self._setUpMocks(m)

with freeze_time("2022-01-01 12:00") as frozen_time:
# First attempt
self.assertIsNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNone(cache.get(f"status:{self.status2['url']}"))
self.assertIsNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status1['url']}"))
self.assertIsNone(cache.get(f"{ZAKEN_ROOT}:status:{self.status2['url']}"))

self.client.force_login(user=self.user)
self.client.get(self.inner_url, HTTP_HX_REQUEST="true")

self.assertIsNotNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNotNone(cache.get(f"status:{self.status2['url']}"))
self.assertIsNotNone(
cache.get(f"{ZAKEN_ROOT}:status:{self.status1['url']}")
)
self.assertIsNotNone(
cache.get(f"{ZAKEN_ROOT}:status:{self.status2['url']}")
)

# Second attempt with new case and status type
self._setUpNewMock(m)
Expand All @@ -302,6 +326,12 @@ def test_cached_statuses_in_combination_with_new_ones(self, m):

self.client.get(self.inner_url, HTTP_HX_REQUEST="true")

self.assertIsNotNone(cache.get(f"status:{self.new_status['url']}"))
self.assertIsNotNone(cache.get(f"status:{self.status1['url']}"))
self.assertIsNotNone(cache.get(f"status:{self.status2['url']}"))
self.assertIsNotNone(
cache.get(f"{ZAKEN_ROOT}:status:{self.new_status['url']}")
)
self.assertIsNotNone(
cache.get(f"{ZAKEN_ROOT}:status:{self.status1['url']}")
)
self.assertIsNotNone(
cache.get(f"{ZAKEN_ROOT}:status:{self.status2['url']}")
)

0 comments on commit 54f2d72

Please sign in to comment.