Skip to content

Commit 0d8d43f

Browse files
committed
fix task processor args
1 parent 03d8914 commit 0d8d43f

8 files changed

Lines changed: 157 additions & 87 deletions

File tree

api/app_analytics/cache.py

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
from collections import defaultdict
21
from threading import Lock
32

43
from django.conf import settings
54
from django.utils import timezone
65

6+
from app_analytics.mappers import (
7+
map_feature_evaluation_cache_to_track_feature_evaluations_by_environment_kwargs,
8+
)
79
from app_analytics.models import Resource
810
from app_analytics.tasks import (
911
track_feature_evaluations_by_environment,
@@ -12,7 +14,6 @@
1214
from app_analytics.types import (
1315
APIUsageCacheKey,
1416
FeatureEvaluationCacheKey,
15-
FeatureEvaluationKey,
1617
Labels,
1718
)
1819

@@ -69,25 +70,10 @@ def __init__(self) -> None:
6970
self._lock = Lock()
7071

7172
def _flush(self) -> None:
72-
evaluation_data: dict[int, dict[FeatureEvaluationKey, int]] = defaultdict(dict)
73-
for (
74-
cache_key,
75-
eval_count,
76-
) in self._cache.items():
77-
key = FeatureEvaluationKey(
78-
feature_name=cache_key.feature_name,
79-
labels=cache_key.labels,
80-
)
81-
evaluation_data[cache_key.environment_id][key] = eval_count
82-
83-
# Schedule evaluation tracking by environment
84-
for environment_id, feature_evaluations in evaluation_data.items():
85-
track_feature_evaluations_by_environment.delay(
86-
kwargs={
87-
"environment_id": environment_id,
88-
"feature_evaluations": list(feature_evaluations.items()),
89-
}
90-
)
73+
for kwargs in map_feature_evaluation_cache_to_track_feature_evaluations_by_environment_kwargs(
74+
self._cache
75+
):
76+
track_feature_evaluations_by_environment.delay(kwargs=dict(kwargs))
9177

9278
self._cache = {}
9379
self._last_flushed_at = timezone.now()

api/app_analytics/mappers.py

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
from collections import defaultdict
12
from typing import Any, Iterable
23

34
from django.http import HttpRequest
@@ -7,11 +8,14 @@
78

89
from app_analytics.constants import TRACK_HEADERS
910
from app_analytics.dataclasses import FeatureEvaluationData, UsageData
10-
from app_analytics.models import Resource
11+
from app_analytics.models import FeatureEvaluationRaw, Resource
1112
from app_analytics.types import (
1213
AnnotatedAPIUsageBucket,
1314
AnnotatedAPIUsageKey,
15+
FeatureEvaluationCacheKey,
1416
Labels,
17+
TrackFeatureEvaluationsByEnvironmentData,
18+
TrackFeatureEvaluationsByEnvironmentKwargs,
1519
)
1620

1721
_request_header_labels_model_fields: dict[str, Any] = {
@@ -90,3 +94,44 @@ def map_request_to_labels(request: HttpRequest) -> Labels:
9094
exclude_unset=True,
9195
)
9296
return result
97+
98+
99+
def map_feature_evaluation_cache_to_track_feature_evaluations_by_environment_kwargs(
100+
cache: dict[FeatureEvaluationCacheKey, int],
101+
) -> list[TrackFeatureEvaluationsByEnvironmentKwargs]:
102+
feature_evaluations_by_environment: dict[
103+
int, list[TrackFeatureEvaluationsByEnvironmentData]
104+
] = defaultdict(list)
105+
106+
for cache_key, evaluation_count in cache.items():
107+
environment_id = cache_key.environment_id
108+
feature_evaluations_by_environment[environment_id].append(
109+
{
110+
"feature_name": cache_key.feature_name,
111+
"labels": dict(cache_key.labels),
112+
"evaluation_count": evaluation_count,
113+
}
114+
)
115+
116+
return [
117+
{
118+
"environment_id": environment_id,
119+
"feature_evaluations": feature_evaluations,
120+
}
121+
for environment_id, feature_evaluations in feature_evaluations_by_environment.items()
122+
]
123+
124+
125+
def map_feature_evaluation_data_to_feature_evaluation_raw(
126+
environment_id: int,
127+
feature_evaluations: list[TrackFeatureEvaluationsByEnvironmentData],
128+
) -> list[FeatureEvaluationRaw]:
129+
return [
130+
FeatureEvaluationRaw(
131+
environment_id=environment_id,
132+
feature_name=feature_evaluation["feature_name"],
133+
evaluation_count=feature_evaluation["evaluation_count"],
134+
labels=feature_evaluation["labels"],
135+
)
136+
for feature_evaluation in feature_evaluations
137+
]

api/app_analytics/tasks.py

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
from datetime import datetime, timedelta
2-
from typing import Any, List, Tuple
2+
from typing import Any, List, Tuple, Unpack
33

44
from django.conf import settings
55
from django.db.models import Q, Sum
@@ -11,6 +11,7 @@
1111
)
1212

1313
from app_analytics.constants import ANALYTICS_READ_BUCKET_SIZE
14+
from app_analytics.mappers import map_feature_evaluation_data_to_feature_evaluation_raw
1415
from app_analytics.models import (
1516
APIUsageBucket,
1617
APIUsageRaw,
@@ -26,8 +27,8 @@
2627
track_feature_evaluation_influxdb_v2 as track_feature_evaluation_influxdb_v2_service,
2728
)
2829
from app_analytics.types import (
29-
FeatureEvaluationKey,
3030
Labels,
31+
TrackFeatureEvaluationsByEnvironmentKwargs,
3132
)
3233
from environments.models import Environment
3334

@@ -95,21 +96,17 @@ def track_feature_evaluation_v2(
9596

9697
@register_task_handler()
9798
def track_feature_evaluations_by_environment(
98-
environment_id: int,
99-
feature_evaluations: list[tuple[FeatureEvaluationKey, int]],
99+
**kwargs: Unpack[TrackFeatureEvaluationsByEnvironmentKwargs],
100100
) -> None:
101+
environment_id = kwargs["environment_id"]
102+
feature_evaluations = kwargs["feature_evaluations"]
101103
if settings.USE_POSTGRES_FOR_ANALYTICS:
102-
feature_evaluation_objects = []
103-
for (feature_name, labels), evaluation_count in feature_evaluations:
104-
feature_evaluation_objects.append(
105-
FeatureEvaluationRaw(
106-
feature_name=feature_name,
107-
environment_id=environment_id,
108-
evaluation_count=evaluation_count,
109-
labels=dict(labels),
110-
)
104+
FeatureEvaluationRaw.objects.bulk_create(
105+
map_feature_evaluation_data_to_feature_evaluation_raw(
106+
environment_id=environment_id,
107+
feature_evaluations=feature_evaluations,
111108
)
112-
FeatureEvaluationRaw.objects.bulk_create(feature_evaluation_objects)
109+
)
113110
elif settings.INFLUXDB_TOKEN:
114111
track_feature_evaluation_influxdb(
115112
environment_id=environment_id,

api/app_analytics/track.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
from app_analytics.influxdb_wrapper import InfluxDBWrapper
1010
from app_analytics.models import Resource
11-
from app_analytics.types import FeatureEvaluationKey, Labels
11+
from app_analytics.types import Label, Labels, TrackFeatureEvaluationsByEnvironmentData
1212
from environments.models import Environment
1313
from util.util import postpone
1414

@@ -116,7 +116,7 @@ def track_request_influxdb(
116116

117117
def track_feature_evaluation_influxdb(
118118
environment_id: int,
119-
feature_evaluations: list[tuple[FeatureEvaluationKey, int]],
119+
feature_evaluations: list[TrackFeatureEvaluationsByEnvironmentData],
120120
) -> None:
121121
"""
122122
Sends Feature analytics event data to InfluxDB
@@ -126,13 +126,20 @@ def track_feature_evaluation_influxdb(
126126
"""
127127
influxdb = InfluxDBWrapper("feature_evaluation") # type: ignore[no-untyped-call]
128128

129-
for (feature_name, labels), evaluation_count in feature_evaluations:
130-
tags: dict[str, str | int] = {
131-
"feature_id": feature_name,
129+
for feature_evaluation in feature_evaluations:
130+
tags: dict[str | Label, str | int] = {
131+
"feature_id": feature_evaluation["feature_name"],
132132
"environment_id": environment_id,
133-
**dict(labels),
133+
**{
134+
str(label): value
135+
for label, value in feature_evaluation["labels"].items()
136+
},
134137
}
135-
influxdb.add_data_point("request_count", evaluation_count, tags=tags)
138+
influxdb.add_data_point(
139+
"request_count",
140+
feature_evaluation["evaluation_count"],
141+
tags=tags,
142+
)
136143

137144
influxdb.write()
138145

api/app_analytics/types.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,15 @@ class FeatureEvaluationCacheKey(NamedTuple):
2424
labels: tuple[tuple["Label", str], ...]
2525

2626

27-
class FeatureEvaluationKey(NamedTuple):
27+
class TrackFeatureEvaluationsByEnvironmentData(TypedDict):
2828
feature_name: str
29-
labels: tuple[tuple["Label", str], ...]
29+
labels: "Labels"
30+
evaluation_count: int
31+
32+
33+
class TrackFeatureEvaluationsByEnvironmentKwargs(TypedDict):
34+
environment_id: int
35+
feature_evaluations: list[TrackFeatureEvaluationsByEnvironmentData]
3036

3137

3238
class AnnotatedAPIUsageBucket(TypedDict):

api/tests/unit/app_analytics/test_tasks.py

Lines changed: 34 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
track_feature_evaluations_by_environment,
2121
track_request,
2222
)
23-
from app_analytics.types import FeatureEvaluationKey
23+
from app_analytics.types import TrackFeatureEvaluationsByEnvironmentData
2424
from environments.models import Environment
2525

2626
pytestmark = pytest.mark.skip_if_no_analytics_db
@@ -177,6 +177,7 @@ def test_track_request__postgres__inserts_expected(
177177

178178

179179
def test_track_request__influx__calls_expected(
180+
db: None,
180181
settings: SettingsWrapper,
181182
mocker: MockerFixture,
182183
environment: Environment,
@@ -210,12 +211,23 @@ def test_track_feature_evaluation(settings: SettingsWrapper) -> None:
210211
settings.USE_POSTGRES_FOR_ANALYTICS = True
211212
environment_id = 1
212213
feature_evaluations = [
213-
(FeatureEvaluationKey("feature1", ()), 10),
214-
(FeatureEvaluationKey("feature2", ()), 20),
214+
TrackFeatureEvaluationsByEnvironmentData(
215+
feature_name="feature1",
216+
labels={},
217+
evaluation_count=10,
218+
),
219+
TrackFeatureEvaluationsByEnvironmentData(
220+
feature_name="feature2",
221+
labels={},
222+
evaluation_count=20,
223+
),
215224
]
216225

217226
# When
218-
track_feature_evaluations_by_environment(environment_id, feature_evaluations)
227+
track_feature_evaluations_by_environment(
228+
environment_id=environment_id,
229+
feature_evaluations=feature_evaluations,
230+
)
219231

220232
# Then
221233
assert (
@@ -245,12 +257,27 @@ def test_track_feature_evaluation__influx__calls_expected(
245257
)
246258
environment_id = 1
247259
feature_evaluations = [
248-
(FeatureEvaluationKey("feature1", ()), 10),
249-
(FeatureEvaluationKey("feature2", ()), 20),
260+
(
261+
TrackFeatureEvaluationsByEnvironmentData(
262+
feature_name="feature1",
263+
labels={},
264+
evaluation_count=10,
265+
)
266+
),
267+
(
268+
TrackFeatureEvaluationsByEnvironmentData(
269+
feature_name="feature2",
270+
labels={},
271+
evaluation_count=20,
272+
)
273+
),
250274
]
251275

252276
# When
253-
track_feature_evaluations_by_environment(environment_id, feature_evaluations)
277+
track_feature_evaluations_by_environment(
278+
environment_id=environment_id,
279+
feature_evaluations=feature_evaluations,
280+
)
254281

255282
# Then
256283
track_feature_evaluation_influxdb_mock.assert_called_once_with(

api/tests/unit/app_analytics/test_unit_app_analytics_cache.py

Lines changed: 21 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
from app_analytics.cache import APIUsageCache, FeatureEvaluationCache
77
from app_analytics.models import Resource
8-
from app_analytics.types import FeatureEvaluationKey
8+
from app_analytics.types import TrackFeatureEvaluationsByEnvironmentData
99

1010

1111
def test_api_usage_cache(
@@ -170,19 +170,15 @@ def test_feature_evaluation_cache(
170170
kwargs={
171171
"environment_id": 1,
172172
"feature_evaluations": [
173-
(
174-
FeatureEvaluationKey(
175-
feature_name="feature_1_name",
176-
labels=(),
177-
),
178-
11,
173+
TrackFeatureEvaluationsByEnvironmentData(
174+
feature_name="feature_1_name",
175+
labels={},
176+
evaluation_count=11,
179177
),
180-
(
181-
FeatureEvaluationKey(
182-
feature_name="feature_2_name",
183-
labels=(),
184-
),
185-
10,
178+
TrackFeatureEvaluationsByEnvironmentData(
179+
feature_name="feature_2_name",
180+
labels={},
181+
evaluation_count=10,
186182
),
187183
],
188184
}
@@ -191,12 +187,10 @@ def test_feature_evaluation_cache(
191187
kwargs={
192188
"environment_id": 2,
193189
"feature_evaluations": [
194-
(
195-
FeatureEvaluationKey(
196-
feature_name="feature_2_name",
197-
labels=(),
198-
),
199-
10,
190+
TrackFeatureEvaluationsByEnvironmentData(
191+
feature_name="feature_2_name",
192+
labels={},
193+
evaluation_count=10,
200194
)
201195
],
202196
}
@@ -205,19 +199,15 @@ def test_feature_evaluation_cache(
205199
kwargs={
206200
"environment_id": 1,
207201
"feature_evaluations": [
208-
(
209-
FeatureEvaluationKey(
210-
feature_name="feature_1_name",
211-
labels=(("client_application_name", "test-app"),),
212-
),
213-
1,
202+
TrackFeatureEvaluationsByEnvironmentData(
203+
feature_name="feature_1_name",
204+
labels={"client_application_name": "test-app"},
205+
evaluation_count=1,
214206
),
215-
(
216-
FeatureEvaluationKey(
217-
feature_name="feature_1_name",
218-
labels=(),
219-
),
220-
1,
207+
TrackFeatureEvaluationsByEnvironmentData(
208+
feature_name="feature_1_name",
209+
labels={},
210+
evaluation_count=1,
221211
),
222212
],
223213
}

0 commit comments

Comments
 (0)