-
Notifications
You must be signed in to change notification settings - Fork 429
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(pg-usage-data): Add cache to batch tracking data (#4308)
- Loading branch information
1 parent
691590d
commit 117f72a
Showing
8 changed files
with
197 additions
and
16 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
from app_analytics.tasks import track_request | ||
from django.utils import timezone | ||
|
||
CACHE_FLUSH_INTERVAL = 60 # seconds | ||
|
||
|
||
class APIUsageCache: | ||
def __init__(self): | ||
self._cache = {} | ||
self._last_flushed_at = timezone.now() | ||
|
||
def _flush(self): | ||
for key, value in self._cache.items(): | ||
track_request.delay( | ||
kwargs={ | ||
"resource": key[0], | ||
"host": key[1], | ||
"environment_key": key[2], | ||
"count": value, | ||
} | ||
) | ||
|
||
self._cache = {} | ||
self._last_flushed_at = timezone.now() | ||
|
||
def track_request(self, resource: int, host: str, environment_key: str): | ||
key = (resource, host, environment_key) | ||
if key not in self._cache: | ||
self._cache[key] = 1 | ||
else: | ||
self._cache[key] += 1 | ||
if (timezone.now() - self._last_flushed_at).seconds > CACHE_FLUSH_INTERVAL: | ||
self._flush() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
# Generated by Django 3.2.25 on 2024-07-08 09:12 | ||
|
||
from django.db import migrations, models | ||
|
||
|
||
class Migration(migrations.Migration): | ||
|
||
dependencies = [ | ||
('app_analytics', '0003_add_feature_name_index'), | ||
] | ||
|
||
operations = [ | ||
migrations.AddField( | ||
model_name='apiusageraw', | ||
name='count', | ||
field=models.PositiveIntegerField(default=1), | ||
), | ||
] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
api/tests/unit/app_analytics/test_unit_app_analytics_cache.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from app_analytics.cache import CACHE_FLUSH_INTERVAL, APIUsageCache | ||
from app_analytics.models import Resource | ||
from django.utils import timezone | ||
from freezegun import freeze_time | ||
from pytest_mock import MockerFixture | ||
|
||
|
||
def test_api_usage_cache(mocker: MockerFixture) -> None: | ||
# Given | ||
cache = APIUsageCache() | ||
now = timezone.now() | ||
mocked_track_request_task = mocker.patch("app_analytics.cache.track_request") | ||
host = "host" | ||
environment_key_1 = "environment_key_1" | ||
environment_key_2 = "environment_key_2" | ||
|
||
with freeze_time(now) as frozen_time: | ||
# Make some tracking requests | ||
for _ in range(10): | ||
for resource in Resource: | ||
cache.track_request(resource, host, environment_key_1) | ||
cache.track_request(resource, host, environment_key_2) | ||
|
||
# make sure track_request task was not called | ||
assert not mocked_track_request_task.called | ||
|
||
# Now, let's move the time forward | ||
frozen_time.tick(CACHE_FLUSH_INTERVAL + 1) | ||
|
||
# let's track another request(to trigger flush) | ||
cache.track_request( | ||
Resource.FLAGS, | ||
host, | ||
environment_key_1, | ||
) | ||
|
||
# Then - track request lambda was called for every resource and environment_key combination | ||
expected_calls = [] | ||
for resource in Resource: | ||
expected_calls.append( | ||
mocker.call( | ||
kwargs={ | ||
"resource": resource, | ||
"host": host, | ||
"environment_key": environment_key_1, | ||
"count": 11 if resource == Resource.FLAGS else 10, | ||
} | ||
) | ||
) | ||
expected_calls.append( | ||
mocker.call( | ||
kwargs={ | ||
"resource": resource, | ||
"host": host, | ||
"environment_key": environment_key_2, | ||
"count": 10, | ||
} | ||
) | ||
) | ||
mocked_track_request_task.delay.assert_has_calls(expected_calls) | ||
|
||
# Next, let's reset the mock | ||
mocked_track_request_task.reset_mock() | ||
|
||
# and track another request | ||
cache.track_request( | ||
Resource.FLAGS, | ||
host, | ||
environment_key_1, | ||
) | ||
|
||
# finally, make sure track_request task was not called | ||
assert not mocked_track_request_task.called |