Skip to content

Commit 5d20e9a

Browse files
yashikakhuranaYashika Khurana
andauthored
feat(nimbus): For complete experiment do not update monitoring data as unenrollment spikes (#15455)
Because - when the experiment ends clients will all start unenrolling so now every summary page shows 'unexpectedly high unenrollment' after the experiment ends because all the clients are unenrolling at the end, we can do is stop notifying or updating the experiment monitoring data once the experiment is ended, if we stop updating it, it will never notify it This commit - Shows last updated time at summary page - Does not update monitoring data for the completed experiments. Fixes #15441 Co-authored-by: Yashika Khurana <yashikakhurana@Yashikas-MBP.home.local>
1 parent d2e6aa8 commit 5d20e9a

6 files changed

Lines changed: 48 additions & 17 deletions

File tree

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 5.2.13 on 2026-04-23
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('experiments', '0328_alter_nimbusexperiment_firefox_labs_group'),
10+
]
11+
12+
operations = [
13+
migrations.AddField(
14+
model_name='nimbusexperiment',
15+
name='monitoring_data_updated_at',
16+
field=models.DateTimeField(blank=True, null=True, verbose_name='Monitoring Data Last Updated'),
17+
),
18+
]

experimenter/experimenter/experiments/models.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,11 @@ def default_firefox_version_parsed():
351351
"(enrollment/unenrollment counts, reasons, branches)"
352352
),
353353
)
354+
monitoring_data_updated_at = models.DateTimeField(
355+
"Monitoring Data Last Updated",
356+
blank=True,
357+
null=True,
358+
)
354359
risk_partner_related = models.BooleanField(
355360
"Is a Partner Related Risk Flag", default=None, blank=True, null=True
356361
)

experimenter/experimenter/experiments/tests/test_changelog_utils.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ def test_outputs_expected_schema_for_empty_experiment(self):
8888
"locales": [],
8989
"localizations": None,
9090
"monitoring_data": None,
91+
"monitoring_data_updated_at": None,
9192
"name": "",
9293
"next_steps": None,
9394
"owner": owner.email,
@@ -220,6 +221,7 @@ def test_outputs_expected_schema_for_complete_experiment(self):
220221
"legal_signoff": False,
221222
"localizations": experiment.localizations,
222223
"monitoring_data": experiment.monitoring_data,
224+
"monitoring_data_updated_at": experiment.monitoring_data_updated_at,
223225
"name": experiment.name,
224226
"next_steps": None,
225227
"owner": experiment.owner.email,

experimenter/experimenter/jetstream/tasks.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from celery.utils.log import get_task_logger
55
from django.conf import settings
66
from django.core.cache import cache
7+
from django.utils import timezone
78

89
from experimenter.celery import app
910
from experimenter.experiments.changelog_utils import generate_nimbus_changelog
@@ -149,15 +150,13 @@ def fetch_monitoring_data():
149150
try:
150151
experiment = NimbusExperiment.objects.get(
151152
slug=exp_slug,
152-
status__in=[
153-
NimbusConstants.Status.LIVE,
154-
NimbusConstants.Status.COMPLETE,
155-
],
153+
status=NimbusConstants.Status.LIVE,
156154
)
157155

158156
# Only update if data has changed
159157
if experiment.monitoring_data != monitoring_data:
160158
experiment.monitoring_data = monitoring_data
159+
experiment.monitoring_data_updated_at = timezone.now()
161160
experiment.save()
162161
generate_nimbus_changelog(
163162
experiment,

experimenter/experimenter/jetstream/tests/test_tasks.py

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3457,15 +3457,9 @@ def test_fetch_monitoring_data_no_data(self, return_value):
34573457

34583458
self.mock_get_monitoring_data.assert_called_once()
34593459

3460-
@parameterized.expand(
3461-
[
3462-
(NimbusExperiment.Status.LIVE,),
3463-
(NimbusExperiment.Status.COMPLETE,),
3464-
]
3465-
)
3466-
def test_fetch_monitoring_data_updates_live_or_complete_experiment(self, status):
3460+
def test_fetch_monitoring_data_updates_live_experiment(self):
34673461
experiment = NimbusExperimentFactory.create(
3468-
status=status,
3462+
status=NimbusExperiment.Status.LIVE,
34693463
monitoring_data={},
34703464
)
34713465
self.mock_get_monitoring_data.return_value = {
@@ -3476,14 +3470,16 @@ def test_fetch_monitoring_data_updates_live_or_complete_experiment(self, status)
34763470

34773471
experiment.refresh_from_db()
34783472
self.assertEqual(experiment.monitoring_data, self.monitoring_data)
3473+
self.assertIsNotNone(experiment.monitoring_data_updated_at)
34793474

34803475
@parameterized.expand(
34813476
[
34823477
(NimbusExperiment.Status.DRAFT,),
34833478
(NimbusExperiment.Status.PREVIEW,),
3479+
(NimbusExperiment.Status.COMPLETE,),
34843480
]
34853481
)
3486-
def test_fetch_monitoring_data_skips_non_live_or_complete_experiment(self, status):
3482+
def test_fetch_monitoring_data_skips_non_live_experiment(self, status):
34873483
experiment = NimbusExperimentFactory.create(
34883484
status=status,
34893485
monitoring_data={},
@@ -3496,6 +3492,7 @@ def test_fetch_monitoring_data_skips_non_live_or_complete_experiment(self, statu
34963492

34973493
experiment.refresh_from_db()
34983494
self.assertEqual(experiment.monitoring_data, {})
3495+
self.assertIsNone(experiment.monitoring_data_updated_at)
34993496

35003497
def test_fetch_monitoring_data_skips_nonexistent_experiment(self):
35013498
self.mock_get_monitoring_data.return_value = {

experimenter/experimenter/nimbus_ui/templates/nimbus_experiments/detail_card_monitoring.html

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,23 @@
99
<h4 class="mb-0">
1010
<a href="#monitoring" class="text-decoration-none text-body">{{ NimbusUIConstants.MONITORING_CARD_TITLE }}</a>
1111
</h4>
12-
<a href="{{ NimbusUIConstants.VALIDATING_EXPERIMENTS_URL }}"
13-
target="_blank"
14-
rel="noopener noreferrer"
15-
class="small">Learn more</a>
12+
<div class="d-flex align-items-center gap-3">
13+
{% if experiment.monitoring_data_updated_at %}
14+
<span class="text-muted small">Last updated: {{ experiment.monitoring_data_updated_at|date:"N j, Y" }}</span>
15+
{% endif %}
16+
<a href="{{ NimbusUIConstants.VALIDATING_EXPERIMENTS_URL }}"
17+
target="_blank"
18+
rel="noopener noreferrer"
19+
class="small">Learn more</a>
20+
</div>
1621
</div>
1722
{% endblock %}
1823
{% block card_body %}
24+
{% if experiment.is_complete %}
25+
<div class="alert alert-secondary mx-3 mt-3 mb-0" role="alert">
26+
This experiment has ended. Monitoring data reflects the last update before the experiment concluded.
27+
</div>
28+
{% endif %}
1929
{% with summary=experiment.monitoring_summary %}
2030
<div class="mb-3">
2131
{% if summary.is_unenrollment_spike %}

0 commit comments

Comments
 (0)