Skip to content

Commit f99dce6

Browse files
constantiniusandrewshie-sentry
authored andcommitted
feat(demo-mode) also sync ProjectDebugFile and ProguardArtifactRelease (#92731)
Closes https://linear.app/getsentry/issue/TET-528/extend-demo-mode-sync-task-to-handle-debug-files-and-proguard
1 parent cbb94e2 commit f99dce6

File tree

3 files changed

+240
-5
lines changed

3 files changed

+240
-5
lines changed

src/sentry/conf/server.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1307,8 +1307,8 @@ def SOCIAL_AUTH_DEFAULT_USERNAME() -> str:
13071307
# Run every 1 minute
13081308
"schedule": crontab(minute="*/1"),
13091309
},
1310-
"demo_mode_sync_artifact_bundles": {
1311-
"task": "sentry.demo_mode.tasks.sync_artifact_bundles",
1310+
"demo_mode_sync_debug_artifacts": {
1311+
"task": "sentry.demo_mode.tasks.sync_debug_artifacts",
13121312
# Run every hour
13131313
"schedule": crontab(minute="0", hour="*/1"),
13141314
},

src/sentry/demo_mode/tasks.py

Lines changed: 130 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
ProjectArtifactBundle,
1414
ReleaseArtifactBundle,
1515
)
16+
from sentry.models.debugfile import ProguardArtifactRelease, ProjectDebugFile
1617
from sentry.models.files import FileBlobOwner
1718
from sentry.models.organization import Organization
1819
from sentry.models.project import Project
@@ -23,11 +24,11 @@
2324

2425

2526
@instrumented_task(
26-
name="sentry.demo_mode.tasks.sync_artifact_bundles",
27+
name="sentry.demo_mode.tasks.sync_debug_artifacts",
2728
queue="demo_mode",
2829
taskworker_config=TaskworkerConfig(namespace=demomode_tasks),
2930
)
30-
def sync_artifact_bundles():
31+
def sync_debug_artifacts():
3132

3233
if (
3334
not options.get("sentry.demo_mode.sync_artifact_bundles.enable")
@@ -43,6 +44,8 @@ def sync_artifact_bundles():
4344
lookback_days = options.get("sentry.demo_mode.sync_artifact_bundles.lookback_days")
4445

4546
_sync_artifact_bundles(source_org, target_org, lookback_days)
47+
_sync_project_debug_files(source_org, target_org, lookback_days)
48+
_sync_proguard_artifact_releases(source_org, target_org, lookback_days)
4649

4750

4851
def _sync_artifact_bundles(source_org: Organization, target_org: Organization, lookback_days=1):
@@ -67,6 +70,62 @@ def _sync_artifact_bundles(source_org: Organization, target_org: Organization, l
6770
_sync_artifact_bundle(source_artifact_bundle, target_org)
6871

6972

73+
def _sync_project_debug_files(source_org: Organization, target_org: Organization, lookback_days=1):
74+
if not source_org or not target_org:
75+
return
76+
77+
source_project_ids = Project.objects.filter(
78+
organization_id=source_org.id,
79+
).values_list("id", flat=True)
80+
81+
target_project_ids = Project.objects.filter(
82+
organization_id=target_org.id,
83+
).values_list("id", flat=True)
84+
85+
source_project_debug_files = ProjectDebugFile.objects.filter(
86+
project_id__in=source_project_ids,
87+
)
88+
89+
target_project_debug_files = ProjectDebugFile.objects.filter(
90+
project_id__in=target_project_ids,
91+
)
92+
93+
different_project_debug_files = source_project_debug_files.exclude(
94+
debug_id__in=target_project_debug_files.values_list("debug_id", flat=True)
95+
)
96+
97+
for source_project_debug_file in different_project_debug_files:
98+
_sync_project_debug_file(source_project_debug_file, target_org)
99+
100+
101+
def _sync_proguard_artifact_releases(
102+
source_org: Organization, target_org: Organization, lookback_days=1
103+
):
104+
if not source_org or not target_org:
105+
return
106+
107+
cutoff_date = timezone.now() - timedelta(days=lookback_days)
108+
109+
proguard_artifact_releases = ProguardArtifactRelease.objects.filter(
110+
Q(organization_id=source_org.id) | Q(organization_id=target_org.id),
111+
date_added__gte=cutoff_date,
112+
)
113+
114+
source_proguard_artifact_releases = proguard_artifact_releases.filter(
115+
organization_id=source_org.id,
116+
)
117+
target_proguard_artifact_releases = proguard_artifact_releases.filter(
118+
organization_id=target_org.id,
119+
)
120+
121+
different_proguard_artifact_releases = source_proguard_artifact_releases.exclude(
122+
proguard_uuid__in=target_proguard_artifact_releases.values_list("proguard_uuid", flat=True)
123+
)
124+
125+
for source_proguard_artifact_release in different_proguard_artifact_releases:
126+
_sync_proguard_artifact_release(source_proguard_artifact_release, target_org)
127+
128+
70129
def _sync_artifact_bundle(source_artifact_bundle: ArtifactBundle, target_org: Organization):
71130
try:
72131
with atomic_transaction(
@@ -144,6 +203,75 @@ def _sync_release_artifact_bundle(
144203
)
145204

146205

206+
def _sync_project_debug_file(
207+
source_project_debug_file: ProjectDebugFile, target_org: Organization
208+
) -> ProjectDebugFile | None:
209+
try:
210+
with atomic_transaction(using=(router.db_for_write(ProjectDebugFile))):
211+
target_project = _find_matching_project(
212+
source_project_debug_file.project_id,
213+
target_org.id,
214+
)
215+
216+
if not target_project:
217+
return None
218+
219+
return ProjectDebugFile.objects.create(
220+
project_id=target_project.id,
221+
file=source_project_debug_file.file,
222+
checksum=source_project_debug_file.checksum,
223+
object_name=source_project_debug_file.object_name,
224+
cpu_name=source_project_debug_file.cpu_name,
225+
debug_id=source_project_debug_file.debug_id,
226+
code_id=source_project_debug_file.code_id,
227+
data=source_project_debug_file.data,
228+
date_accessed=source_project_debug_file.date_accessed,
229+
)
230+
except IntegrityError as e:
231+
sentry_sdk.capture_exception(e)
232+
return None
233+
234+
235+
def _sync_proguard_artifact_release(
236+
source_proguard_artifact_release: ProguardArtifactRelease, target_org: Organization
237+
):
238+
try:
239+
with atomic_transaction(using=(router.db_for_write(ProguardArtifactRelease))):
240+
target_project = _find_matching_project(
241+
source_proguard_artifact_release.project_id,
242+
target_org.id,
243+
)
244+
245+
if not target_project:
246+
return
247+
248+
# project_debug_file _should_ already be synced, but we'll make sure it is
249+
project_debug_file = ProjectDebugFile.objects.filter(
250+
project_id=target_project.id,
251+
debug_id=source_proguard_artifact_release.project_debug_file.debug_id,
252+
).first()
253+
254+
if not project_debug_file:
255+
project_debug_file = _sync_project_debug_file(
256+
source_proguard_artifact_release.project_debug_file, target_org
257+
)
258+
259+
if not project_debug_file:
260+
# we require a project debug file
261+
return
262+
263+
ProguardArtifactRelease.objects.create(
264+
organization_id=target_org.id,
265+
project_id=target_project.id,
266+
release_name=source_proguard_artifact_release.release_name,
267+
proguard_uuid=source_proguard_artifact_release.proguard_uuid,
268+
project_debug_file=project_debug_file,
269+
date_added=source_proguard_artifact_release.date_added,
270+
)
271+
except IntegrityError as e:
272+
sentry_sdk.capture_exception(e)
273+
274+
147275
def _find_matching_project(project_id, organization_id):
148276
try:
149277
source_project = Project.objects.get(id=project_id)

tests/sentry/demo_mode/test_tasks.py

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
11
from datetime import datetime, timedelta
22
from unittest import mock
3+
from uuid import uuid1
34

45
from django.db.utils import IntegrityError
56
from django.utils import timezone
67

7-
from sentry.demo_mode.tasks import _sync_artifact_bundles
8+
from sentry.demo_mode.tasks import (
9+
_sync_artifact_bundles,
10+
_sync_proguard_artifact_releases,
11+
_sync_project_debug_files,
12+
)
813
from sentry.models.artifactbundle import (
914
ArtifactBundle,
1015
ProjectArtifactBundle,
1116
ReleaseArtifactBundle,
1217
)
18+
from sentry.models.debugfile import ProguardArtifactRelease, ProjectDebugFile
1319
from sentry.models.organization import Organization
1420
from sentry.models.project import Project
1521
from sentry.testutils.cases import TestCase
@@ -52,6 +58,23 @@ def set_up_artifact_bundle(
5258

5359
return artifact_bundle, project_artifact_bundle, release_artifact_bundle
5460

61+
def set_up_proguard_artifact_release(
62+
self,
63+
organization: Organization,
64+
project: Project,
65+
date_added: datetime | None = None,
66+
):
67+
date_added = date_added or timezone.now()
68+
proguard_artifact_release = ProguardArtifactRelease.objects.create(
69+
organization_id=organization.id,
70+
project_id=project.id,
71+
release_name="release",
72+
proguard_uuid=uuid1(),
73+
project_debug_file=self.create_dif_file(project),
74+
date_added=date_added,
75+
)
76+
return proguard_artifact_release
77+
5578
def test_sync_artifact_bundles_no_bundles(self):
5679

5780
_sync_artifact_bundles(source_org=self.source_org, target_org=self.target_org)
@@ -145,3 +168,87 @@ def test_sync_artifact_bundles_rolls_back_on_error(self, _):
145168
assert not ArtifactBundle.objects.filter(organization_id=self.target_org.id).exists()
146169
assert not ProjectArtifactBundle.objects.filter(organization_id=self.target_org.id).exists()
147170
assert not ReleaseArtifactBundle.objects.filter(organization_id=self.target_org.id).exists()
171+
172+
def test_sync_project_debug_files(self):
173+
source_project_debug_file = self.create_dif_file(self.source_proj_foo)
174+
175+
assert not ProjectDebugFile.objects.filter(
176+
project_id=self.target_proj_foo.id,
177+
debug_id=source_project_debug_file.debug_id,
178+
).exists()
179+
180+
_sync_project_debug_files(source_org=self.source_org, target_org=self.target_org)
181+
182+
target_project_debug_file = ProjectDebugFile.objects.get(
183+
project_id=self.target_proj_foo.id,
184+
debug_id=source_project_debug_file.debug_id,
185+
)
186+
187+
assert target_project_debug_file.debug_id == source_project_debug_file.debug_id
188+
assert target_project_debug_file.code_id == source_project_debug_file.code_id
189+
assert target_project_debug_file.cpu_name == source_project_debug_file.cpu_name
190+
191+
def test_sync_project_debug_files_with_old_uploads(self):
192+
source_project_debug_file = self.create_dif_file(
193+
self.source_proj_foo,
194+
date_accessed=timezone.now() - timedelta(days=2),
195+
)
196+
197+
assert not ProjectDebugFile.objects.filter(
198+
project_id=self.target_proj_foo.id,
199+
debug_id=source_project_debug_file.debug_id,
200+
).exists()
201+
202+
_sync_project_debug_files(source_org=self.source_org, target_org=self.target_org)
203+
204+
assert ProjectDebugFile.objects.filter(
205+
project_id=self.target_proj_foo.id,
206+
debug_id=source_project_debug_file.debug_id,
207+
).exists()
208+
209+
def test_sync_proguard_artifact_releases(self):
210+
source_proguard_artifact_release = self.set_up_proguard_artifact_release(
211+
self.source_org,
212+
self.source_proj_foo,
213+
)
214+
215+
assert not ProguardArtifactRelease.objects.filter(
216+
organization_id=self.target_org.id,
217+
proguard_uuid=source_proguard_artifact_release.proguard_uuid,
218+
).exists()
219+
220+
_sync_proguard_artifact_releases(source_org=self.source_org, target_org=self.target_org)
221+
222+
target_proguard_artifact_release = ProguardArtifactRelease.objects.get(
223+
organization_id=self.target_org.id,
224+
proguard_uuid=source_proguard_artifact_release.proguard_uuid,
225+
)
226+
227+
assert (
228+
target_proguard_artifact_release.release_name
229+
== source_proguard_artifact_release.release_name
230+
)
231+
assert (
232+
target_proguard_artifact_release.proguard_uuid
233+
== source_proguard_artifact_release.proguard_uuid
234+
)
235+
assert target_proguard_artifact_release.project_id == self.target_proj_foo.id
236+
237+
def test_sync_proguard_artifact_releases_with_old_uploads(self):
238+
source_proguard_artifact_release = self.set_up_proguard_artifact_release(
239+
self.source_org,
240+
self.source_proj_foo,
241+
date_added=timezone.now() - timedelta(days=2),
242+
)
243+
244+
assert not ProguardArtifactRelease.objects.filter(
245+
organization_id=self.target_org.id,
246+
proguard_uuid=source_proguard_artifact_release.proguard_uuid,
247+
).exists()
248+
249+
_sync_artifact_bundles(source_org=self.source_org, target_org=self.target_org)
250+
251+
assert not ProguardArtifactRelease.objects.filter(
252+
organization_id=self.target_org.id,
253+
proguard_uuid=source_proguard_artifact_release.proguard_uuid,
254+
).exists()

0 commit comments

Comments
 (0)