|
1 |
| -from typing import Any |
| 1 | +from abc import abstractmethod |
| 2 | +from typing import Any, Literal |
2 | 3 |
|
3 | 4 | from sentry import tagstore
|
4 | 5 | from sentry.eventstore.models import GroupEvent
|
5 | 6 | from sentry.models.environment import Environment
|
6 | 7 | from sentry.models.release import Release
|
7 | 8 | from sentry.rules.filters.latest_release import get_project_release_cache_key
|
8 | 9 | from sentry.search.utils import get_latest_release
|
| 10 | +from sentry.utils import metrics |
9 | 11 | from sentry.utils.cache import cache
|
10 | 12 | from sentry.workflow_engine.models.data_condition import Condition
|
11 | 13 | from sentry.workflow_engine.registry import condition_handler_registry
|
12 | 14 | from sentry.workflow_engine.types import DataConditionHandler, WorkflowEventData
|
13 | 15 |
|
14 | 16 |
|
| 17 | +class CacheAccess[T]: |
| 18 | + """ |
| 19 | + Base class for type-safe naive cache access. |
| 20 | + """ |
| 21 | + |
| 22 | + @abstractmethod |
| 23 | + def key(self) -> str: |
| 24 | + raise NotImplementedError |
| 25 | + |
| 26 | + def get(self) -> T | None: |
| 27 | + return cache.get(self.key()) |
| 28 | + |
| 29 | + def set(self, value: T, timeout: float | None) -> None: |
| 30 | + cache.set(self.key(), value, timeout) |
| 31 | + |
| 32 | + |
| 33 | +class _LatestReleaseCacheAccess(CacheAccess[Release | Literal[False]]): |
| 34 | + """ |
| 35 | + If we have a release for a project in an environment, we cache it. |
| 36 | + If we don't, we cache False. |
| 37 | + """ |
| 38 | + |
| 39 | + def __init__(self, event: GroupEvent, environment: Environment | None): |
| 40 | + self._key = get_project_release_cache_key( |
| 41 | + event.group.project_id, environment.id if environment else None |
| 42 | + ) |
| 43 | + |
| 44 | + def key(self) -> str: |
| 45 | + return self._key |
| 46 | + |
| 47 | + |
15 | 48 | def get_latest_release_for_env(
|
16 | 49 | environment: Environment | None, event: GroupEvent
|
17 | 50 | ) -> Release | None:
|
18 |
| - cache_key = get_project_release_cache_key( |
19 |
| - event.group.project_id, environment.id if environment else None |
20 |
| - ) |
21 |
| - latest_release = cache.get(cache_key) |
| 51 | + cache_access = _LatestReleaseCacheAccess(event, environment) |
| 52 | + latest_release = cache_access.get() |
22 | 53 | if latest_release is not None:
|
| 54 | + if latest_release is False: |
| 55 | + return None |
23 | 56 | return latest_release
|
24 | 57 |
|
25 | 58 | organization_id = event.group.project.organization_id
|
26 | 59 | environments = [environment] if environment else None
|
| 60 | + |
| 61 | + def record_get_latest_release_result(result: str) -> None: |
| 62 | + metrics.incr( |
| 63 | + "workflow_engine.latest_release.get_latest_release", |
| 64 | + tags={ |
| 65 | + "has_environment": str(environment is not None), |
| 66 | + "result": result, |
| 67 | + }, |
| 68 | + ) |
| 69 | + |
27 | 70 | try:
|
28 | 71 | latest_release_version = get_latest_release(
|
29 | 72 | [event.group.project],
|
30 | 73 | environments,
|
31 | 74 | organization_id,
|
32 | 75 | )[0]
|
| 76 | + record_get_latest_release_result("success") |
33 | 77 | except Release.DoesNotExist:
|
| 78 | + record_get_latest_release_result("does_not_exist") |
| 79 | + cache_access.set(False, 600) |
34 | 80 | return None
|
35 | 81 | latest_release = Release.objects.get(
|
36 | 82 | version=latest_release_version, organization_id=organization_id
|
37 | 83 | )
|
38 |
| - cache.set(cache_key, latest_release or False, 600) |
| 84 | + cache_access.set(latest_release or False, 600) |
39 | 85 | return latest_release
|
40 | 86 |
|
41 | 87 |
|
|
0 commit comments