Merge "Fix regression in change tracking"
diff --git a/zuul/connection/__init__.py b/zuul/connection/__init__.py
index f2ea47a..402528f 100644
--- a/zuul/connection/__init__.py
+++ b/zuul/connection/__init__.py
@@ -43,6 +43,14 @@
         self.connection_name = connection_name
         self.connection_config = connection_config
 
+        # Keep track of the sources, triggers and reporters using this
+        # connection
+        self.attached_to = {
+            'source': [],
+            'trigger': [],
+            'reporter': [],
+        }
+
     def onLoad(self):
         pass
 
@@ -51,3 +59,6 @@
 
     def registerScheduler(self, sched):
         self.sched = sched
+
+    def registerUse(self, what, instance):
+        self.attached_to[what].append(instance)
diff --git a/zuul/connection/gerrit.py b/zuul/connection/gerrit.py
index c408d7d..f8e5add 100644
--- a/zuul/connection/gerrit.py
+++ b/zuul/connection/gerrit.py
@@ -47,7 +47,6 @@
     def _handleEvent(self):
         ts, data = self.connection.getEvent()
         if self._stopped:
-            self.connection.eventDone()
             return
         # Gerrit can produce inconsistent data immediately after an
         # event, So ensure that we do not deliver the event to Zuul
@@ -101,11 +100,23 @@
 
         if (event.change_number and
             self.connection.sched.getProject(event.project_name)):
-            # Mark the change as needing a refresh in the cache
-            event._needs_refresh = True
-
+            # Call _getChange for the side effect of updating the
+            # cache.  Note that this modifies Change objects outside
+            # the main thread.
+            # NOTE(jhesketh): Ideally we'd just remove the change from the
+            # cache to denote that it needs updating. However the change
+            # object is already used by Item's and hence BuildSet's etc. and
+            # we need to update those objects by reference so that they have
+            # the correct/new information and also avoid hitting gerrit
+            # multiple times.
+            if self.connection.attached_to['source']:
+                self.connection.attached_to['source'][0]._getChange(
+                    event.change_number, event.patch_number, refresh=True)
+                # We only need to do this once since the connection maintains
+                # the cache (which is shared between all the sources)
+                # NOTE(jhesketh): We may couple sources and connections again
+                # at which point this becomes more sensible.
         self.connection.sched.addEvent(event)
-        self.connection.eventDone()
 
     def run(self):
         while True:
@@ -115,6 +126,8 @@
                 self._handleEvent()
             except:
                 self.log.exception("Exception moving Gerrit event:")
+            finally:
+                self.connection.eventDone()
 
 
 class GerritWatcher(threading.Thread):
diff --git a/zuul/model.py b/zuul/model.py
index 54f776c..c555561 100644
--- a/zuul/model.py
+++ b/zuul/model.py
@@ -1005,9 +1005,6 @@
         # an admin command, etc):
         self.forced_pipeline = None
 
-        # Internal mechanism to track if the change needs a refresh from cache
-        self._needs_refresh = False
-
     def __repr__(self):
         ret = '<TriggerEvent %s %s' % (self.type, self.project_name)
 
diff --git a/zuul/scheduler.py b/zuul/scheduler.py
index 151aae1..05eb6fe 100644
--- a/zuul/scheduler.py
+++ b/zuul/scheduler.py
@@ -307,6 +307,9 @@
                 driver_config, self, connection
         )
 
+        if connection:
+            connection.registerUse(dtype, driver_instance)
+
         return driver_instance
 
     def _getSourceDriver(self, connection_name):
diff --git a/zuul/source/gerrit.py b/zuul/source/gerrit.py
index ea05a4e..f35ab73 100644
--- a/zuul/source/gerrit.py
+++ b/zuul/source/gerrit.py
@@ -129,9 +129,6 @@
     def getChange(self, event, project):
         if event.change_number:
             refresh = False
-            if event._needs_refresh:
-                refresh = True
-                event._needs_refresh = False
             change = self._getChange(event.change_number, event.patch_number,
                                      refresh=refresh)
         elif event.ref: