Browse Source

feat(migration): Backfill ProjectTransactionThreshold (#26628)

If an organization has set a custom apdex threshold
i.e., `sentry:apdex_threshold` on `OrganizationOption`,
copy the data over to all the projects in the org
to maintain the same user experience when they
switch over to using ProjectTransactionThresholds.
Shruthi 3 years ago
parent
commit
467d9a2b8b

+ 1 - 1
migrations_lockfile.txt

@@ -7,5 +7,5 @@ will then be regenerated, and you should be able to merge without conflicts.
 
 jira_ac: 0001_initial
 nodestore: 0002_nodestore_no_dictfield
-sentry: 0209_avatar_remove_file_fk
+sentry: 0210_backfill_project_transaction_thresholds
 social_auth: 0001_initial

+ 74 - 0
src/sentry/migrations/0210_backfill_project_transaction_thresholds.py

@@ -0,0 +1,74 @@
+# Generated by Django 1.11.29 on 2021-06-17 12:35
+
+import logging
+from enum import Enum
+
+from django.db import migrations
+
+from sentry.utils.query import RangeQuerySetWrapperWithProgressBar
+
+
+class TransactionMetric(Enum):
+    DURATION = 1
+    LCP = 2
+
+
+def migrate_project_transaction_thresholds(apps, schema_editor):
+    """
+    Migrate an org's apdex thresholds (if they exist) to the
+    projects transaction thresholds in the org.
+    """
+    OrganizationOption = apps.get_model("sentry", "OrganizationOption")
+    Project = apps.get_model("sentry", "Project")
+    ProjectTransactionThreshold = apps.get_model("sentry", "ProjectTransactionThreshold")
+
+    for option in RangeQuerySetWrapperWithProgressBar(OrganizationOption.objects.all()):
+        if option.key != "sentry:apdex_threshold":
+            continue
+
+        for project in Project.objects.filter(organization_id=option.organization_id):
+            try:
+                # It is possible that an organization has already set a ProjectTransactionThreshold
+                # and we wouldn't want to overwrite it.
+                ProjectTransactionThreshold.objects.get_or_create(
+                    organization_id=option.organization_id,
+                    project_id=project.id,
+                    defaults={
+                        "threshold": option.value,
+                        "metric": TransactionMetric.DURATION.value,
+                    },
+                )
+            except Exception:
+                logging.exception(
+                    f"Error migrating project {project.id} for organization {option.organization_id}"
+                )
+
+
+class Migration(migrations.Migration):
+    # This flag is used to mark that a migration shouldn't be automatically run in
+    # production. We set this to True for operations that we think are risky and want
+    # someone from ops to run manually and monitor.
+    # General advice is that if in doubt, mark your migration as `is_dangerous`.
+    # Some things you should always mark as dangerous:
+    # - Large data migrations. Typically we want these to be run manually by ops so that
+    #   they can be monitored. Since data migrations will now hold a transaction open
+    #   this is even more important.
+    # - Adding columns to highly active tables, even ones that are NULL.
+    is_dangerous = True
+
+    # This flag is used to decide whether to run this migration in a transaction or not.
+    # By default we prefer to run in a transaction, but for migrations where you want
+    # to `CREATE INDEX CONCURRENTLY` this needs to be set to False. Typically you'll
+    # want to create an index concurrently when adding one to an existing table.
+    # You'll also usually want to set this to `False` if you're writing a data
+    # migration, since we don't want the entire migration to run in one long-running
+    # transaction.
+    atomic = False
+
+    dependencies = [
+        ("sentry", "0209_avatar_remove_file_fk"),
+    ]
+
+    operations = [
+        migrations.RunPython(migrate_project_transaction_thresholds, migrations.RunPython.noop),
+    ]