Skip to content

Commit cc4bcc2

Browse files
authored
move commit fetching to celery task (#5275)
* move commit fetching to celery task * fix tests
1 parent a4db7b7 commit cc4bcc2

File tree

7 files changed

+174
-163
lines changed

7 files changed

+174
-163
lines changed

src/sentry/api/endpoints/organization_release_details.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,7 @@ def put(self, request, organization, version):
167167
'refs': ['You must use an authenticated API token to fetch refs']
168168
}, status=400)
169169
fetch_commits = not commit_list
170-
release.set_refs(refs, request.user, fetch_commits=fetch_commits)
170+
release.set_refs(refs, request.user, fetch=fetch_commits)
171171

172172
if (not was_released and release.date_released):
173173
for project in release.projects.all():

src/sentry/api/endpoints/organization_releases.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ def post(self, request, organization):
189189
'refs': ['You must use an authenticated API token to fetch refs']
190190
}, status=400)
191191
fetch_commits = not commit_list
192-
release.set_refs(refs, request.user, fetch_commits=fetch_commits)
192+
release.set_refs(refs, request.user, fetch=fetch_commits)
193193

194194
if not created and not new_projects:
195195
# This is the closest status code that makes sense, and we want

src/sentry/conf/server.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -445,6 +445,7 @@ def env(key, default='', type=None):
445445
'sentry.tasks.auth',
446446
'sentry.tasks.auto_resolve_issues',
447447
'sentry.tasks.beacon',
448+
'sentry.tasks.commits',
448449
'sentry.tasks.check_auth',
449450
'sentry.tasks.clear_expired_snoozes',
450451
'sentry.tasks.collect_project_platforms',
@@ -464,6 +465,7 @@ def env(key, default='', type=None):
464465
CELERY_QUEUES = [
465466
Queue('alerts', routing_key='alerts'),
466467
Queue('auth', routing_key='auth'),
468+
Queue('commits', routing_key='commits'),
467469
Queue('cleanup', routing_key='cleanup'),
468470
Queue('default', routing_key='default'),
469471
Queue('digests.delivery', routing_key='digests.delivery'),

src/sentry/models/release.py

Lines changed: 11 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99

1010
import logging
1111
import re
12-
import six
1312

1413
from django.db import models, IntegrityError, transaction
1514
from django.db.models import F
@@ -19,7 +18,6 @@
1918
from sentry.db.models import (
2019
BoundedPositiveIntegerField, FlexibleForeignKey, Model, sane_repr
2120
)
22-
from sentry.exceptions import InvalidIdentity, PluginError
2321
from sentry.utils.cache import cache
2422
from sentry.utils.hashlib import md5_text
2523

@@ -247,9 +245,9 @@ def add_project(self, project):
247245
else:
248246
return True
249247

250-
def set_refs(self, refs, user, fetch_commits=False):
248+
def set_refs(self, refs, user, fetch=False):
251249
from sentry.models import Commit, ReleaseHeadCommit, Repository
252-
from sentry.plugins import bindings
250+
from sentry.tasks.commits import fetch_commits
253251

254252
# TODO: this does the wrong thing unless you are on the most
255253
# recent release. Add a timestamp compare?
@@ -258,8 +256,6 @@ def set_refs(self, refs, user, fetch_commits=False):
258256
projects__in=self.projects.all(),
259257
).exclude(version=self.version).order_by('-date_added').first()
260258

261-
commit_list = []
262-
263259
for ref in refs:
264260
try:
265261
repo = Repository.objects.get(
@@ -283,43 +279,15 @@ def set_refs(self, refs, user, fetch_commits=False):
283279
'commit': commit,
284280
}
285281
)
286-
if fetch_commits:
287-
try:
288-
provider_cls = bindings.get('repository.provider').get(repo.provider)
289-
except KeyError:
290-
continue
291-
292-
# if previous commit isn't provided, try to get from
293-
# previous release otherwise, give up
294-
if ref.get('previousCommit'):
295-
start_sha = ref['previousCommit']
296-
elif prev_release:
297-
try:
298-
start_sha = Commit.objects.filter(
299-
organization_id=self.organization_id,
300-
releaseheadcommit__release=prev_release,
301-
repository_id=repo.id,
302-
).values_list('key', flat=True)[0]
303-
except IndexError:
304-
continue
305-
else:
306-
continue
307-
308-
end_sha = commit.key
309-
provider = provider_cls(id=repo.provider)
310-
try:
311-
repo_commits = provider.compare_commits(
312-
repo, start_sha, end_sha, actor=user
313-
)
314-
except NotImplementedError:
315-
pass
316-
except (PluginError, InvalidIdentity) as e:
317-
logger.exception(six.text_type(e))
318-
else:
319-
commit_list.extend(repo_commits)
320-
321-
if commit_list:
322-
self.set_commits(commit_list)
282+
if fetch:
283+
fetch_commits.apply_async(
284+
kwargs={
285+
'release_id': self.id,
286+
'user_id': user.id,
287+
'refs': refs,
288+
'prev_release_id': prev_release and prev_release.id,
289+
}
290+
)
323291

324292
def set_commits(self, commit_list):
325293
from sentry.models import (

src/sentry/tasks/commits.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
from __future__ import absolute_import
2+
3+
import logging
4+
import six
5+
6+
from sentry.exceptions import InvalidIdentity, PluginError
7+
from sentry.models import Commit, Release, Repository, User
8+
from sentry.plugins import bindings
9+
from sentry.tasks.base import instrumented_task, retry
10+
11+
logger = logging.getLogger(__name__)
12+
13+
14+
@instrumented_task(name='sentry.tasks.commit.fetch_commits', queue='commits',
15+
default_retry_delay=60 * 5, max_retries=5)
16+
@retry(exclude=(Release.DoesNotExist, User.DoesNotExist,))
17+
def fetch_commits(release_id, user_id, refs, prev_release_id=None, **kwargs):
18+
commit_list = []
19+
20+
release = Release.objects.get(id=release_id)
21+
user = User.objects.get(id=user_id)
22+
23+
prev_release = None
24+
if prev_release_id is not None:
25+
try:
26+
prev_release = Release.objects.get(id=prev_release_id)
27+
except Release.DoesNotExist:
28+
pass
29+
30+
for ref in refs:
31+
try:
32+
repo = Repository.objects.get(
33+
organization_id=release.organization_id,
34+
name=ref['repository'],
35+
)
36+
except Repository.DoesNotExist:
37+
continue
38+
39+
try:
40+
commit = Commit.objects.get(
41+
organization_id=release.organization_id,
42+
repository_id=repo.id,
43+
key=ref['commit'],
44+
)
45+
except Commit.DoesNotExist:
46+
continue
47+
48+
try:
49+
provider_cls = bindings.get('repository.provider').get(repo.provider)
50+
except KeyError:
51+
continue
52+
53+
# if previous commit isn't provided, try to get from
54+
# previous release otherwise, give up
55+
if ref.get('previousCommit'):
56+
start_sha = ref['previousCommit']
57+
elif prev_release:
58+
try:
59+
start_sha = Commit.objects.filter(
60+
organization_id=release.organization_id,
61+
releaseheadcommit__release=prev_release,
62+
repository_id=repo.id,
63+
).values_list('key', flat=True)[0]
64+
except IndexError:
65+
continue
66+
else:
67+
continue
68+
69+
end_sha = commit.key
70+
provider = provider_cls(id=repo.provider)
71+
try:
72+
repo_commits = provider.compare_commits(
73+
repo, start_sha, end_sha, actor=user
74+
)
75+
except NotImplementedError:
76+
pass
77+
except (PluginError, InvalidIdentity) as e:
78+
logger.exception(six.text_type(e))
79+
else:
80+
commit_list.extend(repo_commits)
81+
82+
if commit_list:
83+
release.set_commits(commit_list)

tests/sentry/api/endpoints/test_organization_release_details.py

Lines changed: 29 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from __future__ import absolute_import
22

3+
from mock import patch
34
from datetime import datetime
45
from django.core.urlresolvers import reverse
56

@@ -94,7 +95,8 @@ def test_multiple_projects(self):
9495

9596

9697
class UpdateReleaseDetailsTest(APITestCase):
97-
def test_simple(self):
98+
@patch('sentry.tasks.commits.fetch_commits')
99+
def test_simple(self, mock_fetch_commits):
98100
user = self.create_user(is_staff=False, is_superuser=False)
99101
org = self.organization
100102
org.flags.allow_joinleave = False
@@ -161,28 +163,20 @@ def test_simple(self):
161163
],
162164
})
163165

166+
mock_fetch_commits.apply_async.assert_called_with(
167+
kwargs={
168+
'release_id': release.id,
169+
'user_id': user.id,
170+
'refs': [
171+
{'commit': 'a' * 40, 'repository': repo.name},
172+
{'commit': 'b' * 40, 'repository': repo2.name},
173+
],
174+
'prev_release_id': base_release.id,
175+
}
176+
)
177+
164178
assert response.status_code == 200, response.content
165179
assert response.data['version'] == release.version
166-
assert ReleaseCommit.objects.filter(
167-
commit__repository_id=repo.id,
168-
commit__key='62de626b7c7cfb8e77efb4273b1a3df4123e6216',
169-
release__version=response.data['version'],
170-
).exists()
171-
assert ReleaseCommit.objects.filter(
172-
commit__repository_id=repo.id,
173-
commit__key='58de626b7c7cfb8e77efb4273b1a3df4123e6345',
174-
release__version=response.data['version'],
175-
).exists()
176-
assert ReleaseCommit.objects.filter(
177-
commit__repository_id=repo2.id,
178-
commit__key='62de626b7c7cfb8e77efb4273b1a3df4123e6216',
179-
release__version=response.data['version'],
180-
).exists()
181-
assert ReleaseCommit.objects.filter(
182-
commit__repository_id=repo2.id,
183-
commit__key='58de626b7c7cfb8e77efb4273b1a3df4123e6345',
184-
release__version=response.data['version'],
185-
).exists()
186180

187181
release = Release.objects.get(id=release.id)
188182
assert release.ref == 'master'
@@ -195,7 +189,8 @@ def test_simple(self):
195189
response = self.client.put(url, {'ref': 'master'})
196190
assert response.status_code == 403
197191

198-
def test_deprecated_head_commits(self):
192+
@patch('sentry.tasks.commits.fetch_commits')
193+
def test_deprecated_head_commits(self, mock_fetch_commits):
199194
user = self.create_user(is_staff=False, is_superuser=False)
200195
org = self.organization
201196
org.flags.allow_joinleave = False
@@ -263,28 +258,20 @@ def test_deprecated_head_commits(self):
263258
],
264259
})
265260

261+
mock_fetch_commits.apply_async.assert_called_with(
262+
kwargs={
263+
'release_id': release.id,
264+
'user_id': user.id,
265+
'refs': [
266+
{'commit': 'a' * 40, 'previousCommit': None, 'repository': repo.name},
267+
{'commit': 'b' * 40, 'previousCommit': None, 'repository': repo2.name},
268+
],
269+
'prev_release_id': base_release.id,
270+
}
271+
)
272+
266273
assert response.status_code == 200, response.content
267274
assert response.data['version'] == release.version
268-
assert ReleaseCommit.objects.filter(
269-
commit__repository_id=repo.id,
270-
commit__key='62de626b7c7cfb8e77efb4273b1a3df4123e6216',
271-
release__version=response.data['version'],
272-
).exists()
273-
assert ReleaseCommit.objects.filter(
274-
commit__repository_id=repo.id,
275-
commit__key='58de626b7c7cfb8e77efb4273b1a3df4123e6345',
276-
release__version=response.data['version'],
277-
).exists()
278-
assert ReleaseCommit.objects.filter(
279-
commit__repository_id=repo2.id,
280-
commit__key='62de626b7c7cfb8e77efb4273b1a3df4123e6216',
281-
release__version=response.data['version'],
282-
).exists()
283-
assert ReleaseCommit.objects.filter(
284-
commit__repository_id=repo2.id,
285-
commit__key='58de626b7c7cfb8e77efb4273b1a3df4123e6345',
286-
release__version=response.data['version'],
287-
).exists()
288275

289276
release = Release.objects.get(id=release.id)
290277
assert release.ref == 'master'

0 commit comments

Comments
 (0)