Skip to content
This repository was archived by the owner on Jun 13, 2025. It is now read-only.

Commit a4a6e79

Browse files
committed
feat: queue task to cache GCS results in redis
when we miss the redis cache but find the test rollup results in GCS we want to queue up a task to cache it in redis so subsequent calls to the GQL endpoint for the same repo get to hit redis instead of GCS
1 parent 838b0b3 commit a4a6e79

File tree

3 files changed

+50
-36
lines changed

3 files changed

+50
-36
lines changed

graphql_api/tests/test_test_analytics.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,18 @@ def test_get_test_results(
151151
assert results.equals(test_results_table)
152152

153153
def test_get_test_results_no_storage(self, transactional_db, repository):
154-
with pytest.raises(FileNotFoundError):
155-
get_results(repository.repoid, repository.branch, 30)
154+
assert get_results(repository.repoid, repository.branch, 30) is None
156155

157156
def test_get_test_results_no_redis(
158-
self, transactional_db, repository, store_in_storage
157+
self, mocker, transactional_db, repository, store_in_storage
159158
):
159+
m = mocker.patch("services.task.TaskService.cache_test_results_redis")
160160
results = get_results(repository.repoid, repository.branch, 30)
161161
assert results is not None
162162
assert results.equals(test_results_table)
163163

164+
m.assert_called_once_with(repository.repoid, repository.branch)
165+
164166
def test_test_results(self, transactional_db, repository, store_in_redis):
165167
test_results = generate_test_results(
166168
repoid=repository.repoid,

services/task/task.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -418,3 +418,9 @@ def delete_component_measurements(self, repoid: int, component_id: str) -> None:
418418
measurement_id=component_id,
419419
),
420420
).apply_async()
421+
422+
def cache_test_results_redis(self, repoid: int, branch: str) -> None:
423+
self._create_signature(
424+
celery_config.cache_test_rollups_redis_task_name,
425+
kwargs=dict(repoid=repoid, branch=branch),
426+
).apply_async()

utils/test_results.py

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,32 @@
33

44
from services.redis_configuration import get_redis_connection
55
from services.storage import StorageService
6+
from services.task import TaskService
67

78

8-
def get_redis_key(
9+
def redis_key(
910
repoid: int,
1011
branch: str,
1112
interval_start: int,
1213
interval_end: int | None = None,
1314
) -> str:
14-
if interval_end is None:
15-
return f"test_results:{repoid}:{branch}:{interval_start}"
16-
else:
17-
return f"test_results:{repoid}:{branch}:{interval_start}:{interval_end}"
15+
key = f"test_results:{repoid}:{branch}:{interval_start}"
1816

17+
if interval_end is not None:
18+
key = f"{key}:{interval_end}"
1919

20-
def get_storage_key(
20+
return key
21+
22+
23+
def storage_key(
2124
repoid: int, branch: str, interval_start: int, interval_end: int | None = None
2225
) -> str:
23-
if interval_end is None:
24-
return f"test_results/rollups/{repoid}/{branch}/{interval_start}"
25-
else:
26-
return f"test_results/rollups/{repoid}/{branch}/{interval_start}_{interval_end}"
26+
key = f"test_results/rollups/{repoid}/{branch}/{interval_start}"
27+
28+
if interval_end is not None:
29+
key = f"{key}_{interval_end}"
30+
31+
return key
2732

2833

2934
def get_results(
@@ -32,33 +37,34 @@ def get_results(
3237
interval_start: int,
3338
interval_end: int | None = None,
3439
) -> pl.DataFrame | None:
35-
serialized_table = None
36-
40+
"""
41+
try redis
42+
if redis is empty
43+
try storage
44+
if storage is empty
45+
return None
46+
else
47+
cache to redis
48+
deserialize
49+
"""
50+
# try redis
3751
redis_conn = get_redis_connection()
38-
redis_key = get_redis_key(repoid, branch, interval_start, interval_end)
52+
key = redis_key(repoid, branch, interval_start, interval_end)
53+
result: bytes | None = redis_conn.get(key)
3954

40-
redis_result = redis_conn.get(redis_key)
41-
42-
if redis_result is not None:
43-
serialized_table = redis_result
44-
45-
if serialized_table is None:
55+
if result is None:
56+
# try storage
4657
storage_service = StorageService()
47-
storage_key = get_storage_key(repoid, branch, interval_start, interval_end)
48-
58+
key = storage_key(repoid, branch, interval_start, interval_end)
4959
try:
50-
serialized_table = storage_service.read_file(
51-
bucket_name="codecov", path=storage_key
52-
)
53-
except FileNotInStorageError as e:
54-
if interval_end is not None:
55-
return None
56-
else:
57-
raise FileNotFoundError(f"File not found in archive: {e}")
58-
59-
if serialized_table is None:
60-
return None
60+
result = storage_service.read_file(bucket_name="codecov", path=key)
61+
# cache to redis
62+
TaskService().cache_test_results_redis(repoid, branch)
63+
except FileNotInStorageError:
64+
# give up
65+
return None
6166

62-
table = pl.read_ipc(serialized_table)
67+
# deserialize
68+
table = pl.read_ipc(result)
6369

6470
return table

0 commit comments

Comments
 (0)