Skip to content

Commit 81c2007

Browse files
ref: use LegacyTextJSONField for large tables (#97571)
these tables are too large to convert to models.JSONField :( <!-- Describe your PR here. -->
1 parent b964a45 commit 81c2007

15 files changed

+132
-35
lines changed

migrations_lockfile.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ preprod: 0014_commitcomparisons_fk
2727

2828
replays: 0006_add_bulk_delete_job
2929

30-
sentry: 0966_groupopenperiod_data_pending_inc_detector_id_index
30+
sentry: 0967_large_tables_legacy_json_field
3131

3232
social_auth: 0003_social_auth_json_field
3333

src/sentry/integrations/models/external_issue.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,9 @@
88

99
from sentry.backup.scopes import RelocationScope
1010
from sentry.constants import ObjectStatus
11-
from sentry.db.models import FlexibleForeignKey, JSONField, Model, region_silo_model, sane_repr
11+
from sentry.db.models import FlexibleForeignKey, Model, region_silo_model, sane_repr
1212
from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey
13+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
1314
from sentry.db.models.manager.base import BaseManager
1415
from sentry.services.eventstore.models import GroupEvent
1516

@@ -70,7 +71,7 @@ class ExternalIssue(Model):
7071
date_added = models.DateTimeField(default=timezone.now)
7172
title = models.TextField(null=True)
7273
description = models.TextField(null=True)
73-
metadata = JSONField(null=True)
74+
metadata = LegacyTextJSONField(default=dict, null=True)
7475

7576
objects: ClassVar[ExternalIssueManager] = ExternalIssueManager()
7677

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Generated by Django 5.2.1 on 2025-08-11 14:46
2+
3+
from django.db import migrations
4+
5+
import sentry.db.models.fields.jsonfield
6+
from sentry.new_migrations.migrations import CheckedMigration
7+
8+
9+
class Migration(CheckedMigration):
10+
# This flag is used to mark that a migration shouldn't be automatically run in production.
11+
# This should only be used for operations where it's safe to run the migration after your
12+
# code has deployed. So this should not be used for most operations that alter the schema
13+
# of a table.
14+
# Here are some things that make sense to mark as post deployment:
15+
# - Large data migrations. Typically we want these to be run manually so that they can be
16+
# monitored and not block the deploy for a long period of time while they run.
17+
# - Adding indexes to large tables. Since this can take a long time, we'd generally prefer to
18+
# run this outside deployments so that we don't block them. Note that while adding an index
19+
# is a schema change, it's completely safe to run the operation after the code has deployed.
20+
# Once deployed, run these manually via: https://develop.sentry.dev/database-migrations/#migration-deployment
21+
22+
is_post_deployment = False
23+
24+
dependencies = [
25+
("sentry", "0966_groupopenperiod_data_pending_inc_detector_id_index"),
26+
]
27+
28+
operations = [
29+
migrations.AlterField(
30+
model_name="controlfile",
31+
name="headers",
32+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
33+
),
34+
migrations.AlterField(
35+
model_name="externalissue",
36+
name="metadata",
37+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict, null=True),
38+
),
39+
migrations.AlterField(
40+
model_name="featureadoption",
41+
name="data",
42+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
43+
),
44+
migrations.AlterField(
45+
model_name="file",
46+
name="headers",
47+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
48+
),
49+
migrations.AlterField(
50+
model_name="grouphashmetadata",
51+
name="hashing_metadata",
52+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(null=True),
53+
),
54+
migrations.AlterField(
55+
model_name="groupinbox",
56+
name="reason_details",
57+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict, null=True),
58+
),
59+
migrations.AlterField(
60+
model_name="organizationonboardingtask",
61+
name="data",
62+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
63+
),
64+
migrations.AlterField(
65+
model_name="projectdebugfile",
66+
name="data",
67+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict, null=True),
68+
),
69+
migrations.AlterField(
70+
model_name="projectkey",
71+
name="data",
72+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
73+
),
74+
migrations.AlterField(
75+
model_name="promptsactivity",
76+
name="data",
77+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
78+
),
79+
migrations.AlterField(
80+
model_name="pullrequestcomment",
81+
name="reactions",
82+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(null=True),
83+
),
84+
migrations.AlterField(
85+
model_name="release",
86+
name="data",
87+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
88+
),
89+
migrations.AlterField(
90+
model_name="repository",
91+
name="config",
92+
field=sentry.db.models.fields.jsonfield.LegacyTextJSONField(default=dict),
93+
),
94+
]

src/sentry/models/debugfile.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,11 @@
2727
from sentry.db.models import (
2828
BoundedBigIntegerField,
2929
FlexibleForeignKey,
30-
JSONField,
3130
Model,
3231
region_silo_model,
3332
sane_repr,
3433
)
34+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
3535
from sentry.db.models.manager.base import BaseManager
3636
from sentry.models.files.file import File
3737
from sentry.models.files.utils import clear_cached_files
@@ -126,7 +126,7 @@ class ProjectDebugFile(Model):
126126
project_id = BoundedBigIntegerField(null=True, db_index=True)
127127
debug_id = models.CharField(max_length=64, db_column="uuid")
128128
code_id = models.CharField(max_length=64, null=True)
129-
data: models.Field[dict[str, Any] | None, dict[str, Any] | None] = JSONField(null=True)
129+
data = LegacyTextJSONField(default=dict, null=True)
130130
date_accessed = models.DateTimeField(default=timezone.now, db_default=Now())
131131

132132
objects: ClassVar[ProjectDebugFileManager] = ProjectDebugFileManager()

src/sentry/models/featureadoption.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from sentry.adoption import manager
1111
from sentry.adoption.manager import UnknownFeature
1212
from sentry.backup.scopes import RelocationScope
13-
from sentry.db.models import FlexibleForeignKey, JSONField, Model, region_silo_model, sane_repr
13+
from sentry.db.models import FlexibleForeignKey, Model, region_silo_model, sane_repr
14+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
1415
from sentry.db.models.manager.base import BaseManager
1516
from sentry.utils.redis import (
1617
get_dynamic_cluster_from_options,
@@ -235,7 +236,7 @@ class FeatureAdoption(Model):
235236
date_completed = models.DateTimeField(default=timezone.now)
236237
complete = models.BooleanField(default=False)
237238
applicable = models.BooleanField(default=True) # Is this feature applicable to this team?
238-
data = JSONField()
239+
data = LegacyTextJSONField(default=dict)
239240

240241
objects: ClassVar[FeatureAdoptionManager] = FeatureAdoptionManager()
241242

src/sentry/models/files/abstractfile.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919

2020
from sentry.backup.scopes import RelocationScope
2121
from sentry.celery import SentryTask
22-
from sentry.db.models import JSONField, Model, WrappingU32IntegerField
22+
from sentry.db.models import Model, WrappingU32IntegerField
23+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
2324
from sentry.models.files.abstractfileblob import AbstractFileBlob
2425
from sentry.models.files.abstractfileblobindex import AbstractFileBlobIndex
2526
from sentry.models.files.utils import DEFAULT_BLOB_SIZE, AssembleChecksumMismatch, nooplogger
@@ -219,7 +220,7 @@ class AbstractFile(Model, _Parent[BlobIndexType, BlobType]):
219220
name = models.TextField()
220221
type = models.CharField(max_length=64)
221222
timestamp = models.DateTimeField(default=timezone.now, db_index=True)
222-
headers: models.Field[dict[str, Any], dict[str, Any]] = JSONField()
223+
headers = LegacyTextJSONField(default=dict)
223224
size = WrappingU32IntegerField(null=True)
224225
checksum = models.CharField(max_length=40, null=True, db_index=True)
225226

src/sentry/models/grouphashmetadata.py

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,7 @@
88
from sentry.db.models import Model, region_silo_model
99
from sentry.db.models.base import sane_repr
1010
from sentry.db.models.fields.foreignkey import FlexibleForeignKey
11-
from sentry.db.models.fields.jsonfield import JSONField
12-
from sentry.types.grouphash_metadata import HashingMetadata
11+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
1312

1413
# The current version of the metadata schema. Any record encountered whose schema version is earlier
1514
# than this will have its data updated and its version set to this value. Stored as a string for
@@ -96,9 +95,7 @@ class GroupHashMetadata(Model):
9695
# Metadata about the inputs to the hashing process and the hashing process itself (what
9796
# fingerprinting rules were matched? did we parameterize the message? etc.). For the specific
9897
# data stored, see the class definitions of the `HashingMetadata` subtypes.
99-
hashing_metadata: models.Field[HashingMetadata | None, HashingMetadata | None] = JSONField(
100-
null=True
101-
)
98+
hashing_metadata = LegacyTextJSONField(null=True)
10299

103100
# SEER
104101

src/sentry/models/groupinbox.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@
1010
from django.utils import timezone
1111

1212
from sentry.backup.scopes import RelocationScope
13-
from sentry.db.models import FlexibleForeignKey, JSONField, Model, region_silo_model
13+
from sentry.db.models import FlexibleForeignKey, Model, region_silo_model
14+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
1415
from sentry.db.models.manager.base_query_set import BaseQuerySet
1516
from sentry.models.activity import Activity
1617
from sentry.models.group import Group
@@ -57,7 +58,7 @@ class GroupInbox(Model):
5758
project = FlexibleForeignKey("sentry.Project", null=True, db_constraint=False)
5859
organization = FlexibleForeignKey("sentry.Organization", null=True, db_constraint=False)
5960
reason = models.PositiveSmallIntegerField(null=False, default=GroupInboxReason.NEW.value)
60-
reason_details = JSONField(null=True)
61+
reason_details = LegacyTextJSONField(default=dict, null=True)
6162
date_added = models.DateTimeField(default=timezone.now, db_index=True)
6263

6364
class Meta:
@@ -71,9 +72,6 @@ def add_group_to_inbox(
7172
reason: GroupInboxReason,
7273
reason_details: InboxReasonDetails | None = None,
7374
) -> GroupInbox:
74-
if reason_details is not None and reason_details["until"] is not None:
75-
reason_details["until"] = reason_details["until"].replace(microsecond=0)
76-
7775
group_inbox, _ = GroupInbox.objects.get_or_create(
7876
group=group,
7977
defaults={
@@ -137,7 +135,7 @@ def bulk_remove_groups_from_inbox(
137135

138136

139137
class InboxReasonDetails(TypedDict):
140-
until: datetime | None
138+
until: str | None # datetime str
141139
count: int | None
142140
window: int | None
143141
user_count: int | None

src/sentry/models/organizationonboardingtask.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
from __future__ import annotations
22

33
import enum
4-
from typing import Any, ClassVar
4+
from typing import ClassVar
55

66
from django.conf import settings
77
from django.core.cache import cache
@@ -13,12 +13,12 @@
1313
from sentry.db.models import (
1414
BoundedPositiveIntegerField,
1515
FlexibleForeignKey,
16-
JSONField,
1716
Model,
1817
region_silo_model,
1918
sane_repr,
2019
)
2120
from sentry.db.models.fields.hybrid_cloud_foreign_key import HybridCloudForeignKey
21+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
2222
from sentry.db.models.manager.base import BaseManager
2323

2424

@@ -111,7 +111,7 @@ class AbstractOnboardingTask(Model):
111111
"sentry.Project", db_constraint=False, null=True, on_delete=SET_NULL
112112
)
113113
# INVITE_MEMBER { invited_member: user.id }
114-
data: models.Field[dict[str, Any], dict[str, Any]] = JSONField()
114+
data = LegacyTextJSONField(default=dict)
115115

116116
# abstract
117117
TASK_LOOKUP_BY_KEY: dict[str, int]

src/sentry/models/projectkey.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import enum
44
import re
55
import secrets
6-
from typing import Any, ClassVar
6+
from typing import ClassVar
77
from urllib.parse import urlparse
88

99
import petname
@@ -22,11 +22,11 @@
2222
from sentry.db.models import (
2323
BoundedPositiveIntegerField,
2424
FlexibleForeignKey,
25-
JSONField,
2625
Model,
2726
region_silo_model,
2827
sane_repr,
2928
)
29+
from sentry.db.models.fields.jsonfield import LegacyTextJSONField
3030
from sentry.db.models.manager.base import BaseManager
3131
from sentry.silo.base import SiloMode
3232
from sentry.tasks.relay import schedule_invalidate_project_config
@@ -120,7 +120,7 @@ class roles(TypedClassBitField):
120120
cache_ttl=60 * 30,
121121
)
122122

123-
data: models.Field[dict[str, Any], dict[str, Any]] = JSONField()
123+
data = LegacyTextJSONField(default=dict)
124124

125125
use_case = models.CharField(
126126
max_length=32,

0 commit comments

Comments
 (0)