Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: API usage alerting in production #3507

Merged
merged 28 commits into from
Mar 28, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
f0c6406
WIP: use flagsmith to filter organisations to include in usage alerting
matthewelwell Feb 27, 2024
781c474
Move get client to outside of loop
zachaysan Feb 28, 2024
5989a60
Implement local evaluation for flagsmith client
zachaysan Feb 28, 2024
888022c
Fix influxdb query call
zachaysan Feb 28, 2024
3715eb3
Merge branch 'main' into fix/api-usage-alerting-in-production
zachaysan Feb 28, 2024
a2b75c5
Trigger build
zachaysan Feb 28, 2024
a5dc209
Trigger build
zachaysan Feb 28, 2024
c8b39f8
Update tests to pass CI
zachaysan Mar 1, 2024
761c372
Add comment with PR
zachaysan Mar 13, 2024
3947f6a
Use a local for easier debugging
zachaysan Mar 13, 2024
f52b9b3
Create test with feature flag being toggled off and add fine grain cl…
zachaysan Mar 13, 2024
c882d34
Merge branch 'main' into fix/api-usage-alerting-in-production
zachaysan Mar 13, 2024
833a17a
Remove temporary code
zachaysan Mar 14, 2024
8157896
Update dependencies
zachaysan Mar 15, 2024
268c46d
Trigger build
zachaysan Mar 15, 2024
6db904a
Trigger build
zachaysan Mar 15, 2024
32c7b06
Reduce whitespace to test CI
zachaysan Mar 15, 2024
0210383
Fix conflicts and merge branch 'main' into fix/api-usage-alerting-in-…
zachaysan Mar 15, 2024
352f299
Update due to library versioning
zachaysan Mar 15, 2024
031449f
Add new data since upgrade
zachaysan Mar 15, 2024
a69929c
Add composite key
zachaysan Mar 15, 2024
77b3e1d
Freeze time
zachaysan Mar 15, 2024
091b010
Set environment id to string for pydantic
zachaysan Mar 15, 2024
585710f
Add IdentityFeaturesList to test
zachaysan Mar 18, 2024
eedcc37
Avoid double iteration
zachaysan Mar 18, 2024
1f07ee1
Fix conflicts, add fixture, and merge branch 'main' into fix/api-usag…
zachaysan Mar 18, 2024
136bdd1
Merge branch 'fix/api-usage-alerting-in-production' of github.com:Fla…
zachaysan Mar 18, 2024
2d3b46c
Fix conflicts and merge branch 'main' into fix/api-usage-alerting-in-…
zachaysan Mar 27, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions api/app_analytics/influxdb_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,7 @@ def get_current_api_usage(organisation_id: int, date_range: str) -> int:
),
drop_columns=("_start", "_stop", "_time"),
extra='|> sum() \
|> group() \
|> sort(columns: ["_value"], desc: true) ',
|> sort(columns: ["_value"], desc: true) ',
)

for result in results:
Expand All @@ -346,7 +345,7 @@ def get_current_api_usage(organisation_id: int, date_range: str) -> int:
return 0

# There should only be one matching result due to the
# group part of the query.
# sum part of the query.
assert len(result.records) == 1
return result.records[0].get_value()

Expand Down
24 changes: 16 additions & 8 deletions api/integrations/flagsmith/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
environment_flags = get_client().get_environment_flags()
identity_flags = get_client().get_identity_flags()
```

Possible extensions:
- Allow for multiple clients?
"""
import typing
from time import sleep

from django.conf import settings
from flagsmith import Flagsmith
Expand All @@ -21,14 +19,24 @@
from integrations.flagsmith.exceptions import FlagsmithIntegrationError
from integrations.flagsmith.flagsmith_service import ENVIRONMENT_JSON_PATH

_flagsmith_client: typing.Optional[Flagsmith] = None
_flagsmith_clients: dict[str, Flagsmith] = {}


def get_client() -> Flagsmith:
global _flagsmith_client
def get_client(name: str = "default", local_eval: bool = False) -> Flagsmith:
global _flagsmith_clients

if not _flagsmith_client:
_flagsmith_client = Flagsmith(**_get_client_kwargs())
try:
_flagsmith_client = _flagsmith_clients[name]
except (KeyError, TypeError):
kwargs = _get_client_kwargs()
kwargs["enable_local_evaluation"] = local_eval
_flagsmith_client = Flagsmith(**kwargs)
# TODO: Remove this once client has been fixed.
# If the client is loaded then used too fast the flags
# returned from the method will be empty.
if local_eval:
sleep(0.5)
_flagsmith_clients[name] = _flagsmith_client

return _flagsmith_client

Expand Down
9 changes: 9 additions & 0 deletions api/organisations/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
from django.template.loader import render_to_string
from django.utils import timezone

from integrations.flagsmith.client import get_client
from organisations import subscription_info_cache
from organisations.models import (
OranisationAPIUsageNotification,
Expand Down Expand Up @@ -164,12 +165,20 @@ def _handle_api_usage_notifications(organisation: Organisation):


def handle_api_usage_notifications():
flagsmith_client = get_client("local", local_eval=True)

for organisation in Organisation.objects.filter(
subscription_information_cache__current_billing_term_starts_at__isnull=False,
subscription_information_cache__current_billing_term_ends_at__isnull=False,
).select_related(
"subscription_information_cache",
):
if not flagsmith_client.get_identity_flags(
f"org.{organisation.id}.{organisation.name}",
traits={"organisation_id": organisation.id},
).is_feature_enabled("api_usage_alerting"):
continue

try:
_handle_api_usage_notifications(organisation)
except RuntimeError:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

@pytest.fixture(autouse=True)
def reset_globals(mocker: MockerFixture) -> None:
mocker.patch("integrations.flagsmith.client._flagsmith_client", None)
mocker.patch("integrations.flagsmith.client._flagsmith_clients", {})
yield


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,8 @@ def test_handle_api_usage_notifications_below_100(
"organisations.tasks.get_current_api_usage",
)
mock_api_usage.return_value = 91
mocker.patch("organisations.tasks.get_client")

assert not OranisationAPIUsageNotification.objects.filter(
organisation=organisation,
).exists()
Expand Down Expand Up @@ -334,6 +336,7 @@ def test_handle_api_usage_notifications_above_100(
)
mock_api_usage.return_value = 105

mocker.patch("organisations.tasks.get_client")
assert not OranisationAPIUsageNotification.objects.filter(
organisation=organisation,
).exists()
Expand Down
Loading