Repository: fedora-infra/bodhi

Issue:
Exception in the celery update handler.
The following exception is raised in the celery worker.

```
[2019-12-13 11:47:59,878: INFO/ForkPoolWorker-2] Updates Handler handling  FEDORA-2019-9274c72fc0, testing
[2019-12-13 11:48:01,020: ERROR/ForkPoolWorker-2] Task handle_update[44ca5f8d-d8c0-45af-bdaa-4ac1532fe88c] raised unexpected: BodhiException("Couldn't find alias 'FEDORA-2019-9274c72fc0' in DB")
Traceback (most recent call last):
  File "/usr/lib/python3.7/site-packages/celery/app/trace.py", line 382, in trace_task
    R = retval = fun(*args, **kwargs)
  File "/usr/lib/python3.7/site-packages/celery/app/trace.py", line 641, in __protected_call__
    return self.run(*args, **kwargs)
  File "/usr/lib/python3.7/site-packages/bodhi/server/tasks/__init__.py", line 85, in handle_update
    handler.run(api_version=api_version, data=kwargs)
  File "/usr/lib/python3.7/site-packages/bodhi/server/tasks/updates.py", line 96, in run
    raise BodhiException("Couldn't find alias '%s' in DB" % alias)
bodhi.server.exceptions.BodhiException: Couldn't find alias 'FEDORA-2019-9274c72fc0' in DB
```


Discussion:

Resolving patch:
diff --git a/bodhi/server/models.py b/bodhi/server/models.py
--- a/bodhi/server/models.py
+++ b/bodhi/server/models.py
@@ -1829,7 +1829,7 @@ class Update(Base):
         backref=backref('updates', passive_deletes=True))
 
     # Many-to-many relationships
-    bugs = relationship('Bug', secondary=update_bug_table, backref='updates')
+    bugs = relationship('Bug', secondary=update_bug_table, backref='updates', order_by='Bug.bug_id')
 
     user_id = Column(Integer, ForeignKey('users.id'))
 
@@ -2265,15 +2265,15 @@ def new(cls, request, data):
                                f"release value of {up.mandatory_days_in_testing} days"
             })
 
+        log.debug("Adding new update to the db.")
+        db.add(up)
+        log.debug("Triggering db commit for new update.")
+        db.commit()
+
         if not data.get("from_tag"):
             log.debug("Setting request for new update.")
             up.set_request(db, req, request.user.name)
 
-        log.debug("Adding new update to the db.")
-        db.add(up)
-        log.debug("Triggering db flush for new update.")
-        db.flush()
-
         if config.get('test_gating.required'):
             log.debug(
                 'Test gating required is enforced, marking the update as waiting on test gating')
@@ -2420,14 +2420,18 @@ def edit(cls, request, data):
 
         up.date_modified = datetime.utcnow()
 
+        # Commit the changes in the db before calling a celery task.
+        db.commit()
+
+        notifications.publish(update_schemas.UpdateEditV1.from_dict(
+            message={'update': up, 'agent': request.user.name, 'new_bugs': new_bugs}))
+
         handle_update.delay(
             api_version=1, action='edit',
             update=up.__json__(request=request),
             agent=request.user.name,
             new_bugs=new_bugs
         )
-        notifications.publish(update_schemas.UpdateEditV1.from_dict(
-            message={'update': up, 'agent': request.user.name, 'new_bugs': new_bugs}))
 
         return up, caveats
 
@@ -2860,12 +2864,9 @@ def set_request(self, db, action, username):
             )
         self.comment(db, comment_text, author=u'bodhi')
 
-        if action == UpdateRequest.testing:
-            handle_update.delay(
-                api_version=1, action="testing",
-                update=self.__json__(),
-                agent=username
-            )
+        # Commit the changes in the db before calling a celery task.
+        db.commit()
+
         action_message_map = {
             UpdateRequest.revoke: update_schemas.UpdateRequestRevokeV1,
             UpdateRequest.stable: update_schemas.UpdateRequestStableV1,
@@ -2875,6 +2876,12 @@ def set_request(self, db, action, username):
         notifications.publish(action_message_map[action].from_dict(
             dict(update=self, agent=username)))
 
+        if action == UpdateRequest.testing:
+            handle_update.delay(
+                api_version=1, action="testing",
+                update=self.__json__(),
+                agent=username)
+
     def waive_test_results(self, username, comment=None, tests=None):
         """
         Attempt to waive test results for this update.


Test patch:
diff --git a/bodhi/tests/server/services/test_updates.py b/bodhi/tests/server/services/test_updates.py
--- a/bodhi/tests/server/services/test_updates.py
+++ b/bodhi/tests/server/services/test_updates.py
@@ -1475,7 +1475,8 @@ def test_provenpackager_request_privs(self, *args):
         update = Build.query.filter_by(nvr=nvr).one().update
         self.assertEqual(update.karma, 2)
         self.assertEqual(update.request, None)
-        update.comment(self.db, "foo", 1, 'biz')
+        with fml_testing.mock_sends(api.Message, api.Message):
+            update.comment(self.db, "foo", 1, 'biz')
         update = Build.query.filter_by(nvr=nvr).one().update
         self.assertEqual(update.karma, 3)
         self.assertEqual(update.request, UpdateRequest.stable)
@@ -3308,7 +3309,8 @@ def test_pending_update_on_stable_karma_reached_autopush_enabled(self, *args):
         up.comment(self.db, 'WFM', author='dustymabe', karma=1)
         up = self.db.query(Build).filter_by(nvr=nvr).one().update
 
-        up.comment(self.db, 'LGTM', author='bowlofeggs', karma=1)
+        with fml_testing.mock_sends(api.Message):
+            up.comment(self.db, 'LGTM', author='bowlofeggs', karma=1)
         up = self.db.query(Build).filter_by(nvr=nvr).one().update
 
         self.assertEqual(up.karma, 2)
@@ -3339,7 +3341,8 @@ def test_pending_urgent_update_on_stable_karma_reached_autopush_enabled(self, *a
         up.comment(self.db, 'WFM', author='dustymabe', karma=1)
         up = self.db.query(Build).filter_by(nvr=nvr).one().update
 
-        up.comment(self.db, 'LGTM', author='bowlofeggs', karma=1)
+        with fml_testing.mock_sends(api.Message):
+            up.comment(self.db, 'LGTM', author='bowlofeggs', karma=1)
         up = self.db.query(Build).filter_by(nvr=nvr).one().update
 
         self.assertEqual(up.karma, 2)
@@ -4668,7 +4671,8 @@ def test_autopush_critical_update_with_no_negative_karma(self, *args):
         up.comment(self.db, 'LGTM', author='ralph', karma=1)
         up = self.db.query(Update).filter_by(alias=resp.json['alias']).one()
 
-        up.comment(self.db, 'LGTM', author='bowlofeggs', karma=1)
+        with fml_testing.mock_sends(api.Message):
+            up.comment(self.db, 'LGTM', author='bowlofeggs', karma=1)
         up = self.db.query(Update).filter_by(alias=resp.json['alias']).one()
         self.assertEqual(up.karma, 2)
 
@@ -4865,7 +4869,8 @@ def test_autopush_non_critical_update_with_no_negative_karma(self, *args):
         up.comment(self.db, 'LGTM Now', author='ralph', karma=1)
         up = self.db.query(Update).filter_by(alias=resp.json['alias']).one()
 
-        up.comment(self.db, 'WFM', author='puiterwijk', karma=1)
+        with fml_testing.mock_sends(api.Message):
+            up.comment(self.db, 'WFM', author='puiterwijk', karma=1)
         up = self.db.query(Update).filter_by(alias=resp.json['alias']).one()
 
         # No negative karma: Update gets automatically marked as stable
@@ -5362,7 +5367,8 @@ def test_submit_older_build_to_stable(self, *args):
                         stable_karma=3, unstable_karma=-3)
         update.comment(self.db, "foo1", 1, 'foo1')
         update.comment(self.db, "foo2", 1, 'foo2')
-        update.comment(self.db, "foo3", 1, 'foo3')
+        with fml_testing.mock_sends(api.Message, api.Message):
+            update.comment(self.db, "foo3", 1, 'foo3')
         self.db.add(update)
         # Let's clear any messages that might get sent
         self.db.info['messages'] = []
diff --git a/bodhi/tests/server/test_models.py b/bodhi/tests/server/test_models.py
--- a/bodhi/tests/server/test_models.py
+++ b/bodhi/tests/server/test_models.py
@@ -27,6 +27,7 @@
 from urllib.error import URLError
 
 from fedora_messaging.testing import mock_sends
+from fedora_messaging.api import Message
 from pyramid.testing import DummyRequest
 import pytest
 from sqlalchemy.exc import IntegrityError
@@ -2153,7 +2154,8 @@ def test_autokarma_update_reaching_stable_karma(self):
         update.status = UpdateStatus.testing
         update.stable_karma = 1
         # Now let's add some karma to get it to the required threshold
-        update.comment(self.db, 'testing', author='hunter2', karma=1)
+        with mock_sends(Message):
+            update.comment(self.db, 'testing', author='hunter2', karma=1)
 
         # meets_testing_requirement() should return True since the karma threshold has been reached
         assert update.meets_testing_requirements
@@ -2190,12 +2192,13 @@ def test_critpath_karma_2_met(self):
         update.critpath = True
         update.stable_karma = 1
         with mock.patch('bodhi.server.models.handle_update'):
-            update.comment(self.db, 'testing', author='enemy', karma=-1)
-            update.comment(self.db, 'testing', author='bro', karma=1)
-            # Despite meeting the stable_karma, the function should still not mark this as meeting
-            # testing requirements because critpath packages have a higher requirement for minimum
-            # karma. So let's get it a second one.
-            update.comment(self.db, 'testing', author='ham', karma=1)
+            with mock_sends(Message, Message, Message, Message):
+                update.comment(self.db, 'testing', author='enemy', karma=-1)
+                update.comment(self.db, 'testing', author='bro', karma=1)
+                # Despite meeting the stable_karma, the function should still not
+                # mark this as meeting testing requirements because critpath packages
+                # have a higher requirement for minimum karma. So let's get it a second one.
+                update.comment(self.db, 'testing', author='ham', karma=1)
 
         assert update.meets_testing_requirements