fix(): fixed bugs with cache invalidation, notifications and guardrails

This commit is contained in:
ITQ
2026-02-24 13:16:29 +03:00
parent b27254e2fb
commit 7bf3ccee5c
14 changed files with 290 additions and 82 deletions
+7 -34
View File
@@ -2,13 +2,13 @@ from datetime import timedelta
from decimal import Decimal
from typing import Any
from django.core.cache import cache
from django.core.exceptions import ValidationError
from django.db import transaction
from django.db.models import Case, QuerySet, Value, When
from django.utils import timezone
from apps.experiments.models import (
STARTED_STATUSES,
Experiment,
ExperimentLog,
ExperimentOutcome,
@@ -17,6 +17,7 @@ from apps.experiments.models import (
OutcomeType,
)
from apps.guardrails.models import (
METRIC_DECIMAL_PLACES,
Guardrail,
GuardrailAction,
GuardrailTrigger,
@@ -38,16 +39,6 @@ def guardrail_create(
observation_window_minutes: int = 60,
action: str = GuardrailAction.PAUSE,
) -> Guardrail:
if experiment.status in STARTED_STATUSES:
raise ValidationError(
{
"experiment": (
"Guardrails cannot be added after the experiment "
"has been started "
f"(status: '{experiment.status}')."
)
}
)
guardrail = Guardrail(
experiment=experiment,
metric=metric,
@@ -65,16 +56,6 @@ def guardrail_update(
**fields: Any,
) -> Guardrail:
guardrail.experiment.refresh_from_db(fields=["status"])
if guardrail.experiment.status in STARTED_STATUSES:
raise ValidationError(
{
"experiment": (
"Guardrails cannot be modified after the experiment "
"has been started "
f"(status: '{guardrail.experiment.status}')."
)
}
)
allowed = {
"threshold",
"observation_window_minutes",
@@ -93,16 +74,6 @@ def guardrail_update(
def guardrail_delete(*, guardrail: Guardrail) -> None:
guardrail.experiment.refresh_from_db(fields=["status"])
if guardrail.experiment.status in STARTED_STATUSES:
raise ValidationError(
{
"experiment": (
"Guardrails cannot be deleted after the experiment "
"has been started "
f"(status: '{guardrail.experiment.status}')."
)
}
)
guardrail.delete()
@@ -138,8 +109,8 @@ def _calculate_guardrail_metric(
metric=guardrail.metric,
experiment_id=experiment.pk,
variant_id=variant.pk,
start_date=window_start,
end_date=now,
event_start_date=window_start,
event_end_date=now,
)
if val is not None:
values.append(val)
@@ -182,7 +153,7 @@ def _execute_guardrail_action(
experiment=experiment,
metric_key=guardrail.metric.key,
threshold=guardrail.threshold,
actual_value=actual_value,
actual_value=round(actual_value, METRIC_DECIMAL_PLACES),
observation_window_minutes=guardrail.observation_window_minutes,
action=guardrail.action,
triggered_at=now,
@@ -191,6 +162,7 @@ def _execute_guardrail_action(
if guardrail.action == GuardrailAction.PAUSE:
experiment.status = ExperimentStatus.PAUSED
experiment.save(update_fields=["status", "updated_at"])
cache.delete(f"active_exp:{experiment.flag_id}")
ExperimentLog.objects.create(
experiment=experiment,
@@ -218,6 +190,7 @@ def _execute_guardrail_action(
experiment.status = ExperimentStatus.COMPLETED
experiment.save(update_fields=["status", "updated_at"])
cache.delete(f"active_exp:{experiment.flag_id}")
ExperimentOutcome.objects.create(
experiment=experiment,