hgext3rd/evolve/obscache.py
branchstable
changeset 2594 a3fbe5293bf6
parent 2554 08bd8ab55cc9
parent 2570 86959f2c625d
child 2749 e1b7ea48e243
--- a/hgext3rd/evolve/obscache.py	Wed May 31 22:15:34 2017 +0200
+++ b/hgext3rd/evolve/obscache.py	Fri Jun 16 11:32:21 2017 +0200
@@ -364,18 +364,31 @@
         super(obscache, self).__init__()
         self._ondiskkey = None
         self._vfs = repo.vfs
-        self._data = bytearray()
+        self._setdata(bytearray())
+
+    @util.propertycache
+    def get(self):
+        """final signature: obscache.get(rev)
+
+        return True if "rev" is used as "precursors for any obsmarkers
 
-    def get(self, rev):
-        """return True if "rev" is used as "precursors for any obsmarkers
+        IMPORTANT: make sure the cache has been updated to match the repository
+        content before using it
 
-        Make sure the cache has been updated to match the repository content before using it"""
-        return self._data[rev]
+        We use a property cache to skip the attribute resolution overhead in
+        hot loops."""
+        return self._data.__getitem__
+
+    def _setdata(self, data):
+        """set a new bytearray data, invalidating the 'get' shortcut if needed"""
+        self._data = data
+        if 'get' in vars(self):
+            del self.get
 
     def clear(self, reset=False):
         """invalidate the cache content"""
         super(obscache, self).clear(reset=reset)
-        self._data = bytearray()
+        self._setdata(bytearray())
 
     def _updatefrom(self, repo, revs, obsmarkers):
         if revs:
@@ -404,16 +417,18 @@
         For now we stick to the simpler approach of paying the
         performance cost on new changesets.
         """
-        node = repo.changelog.node
-        succs = repo.obsstore.successors
-        for r in revs:
-            if node(r) in succs:
-                val = 1
-            else:
-                val = 0
-            self._data.append(val)
-        cl = repo.changelog
-        assert len(self._data) == len(cl), (len(self._data), len(cl))
+        new_entries = bytearray(len(revs))
+        if not self._data:
+            self._setdata(new_entries)
+        else:
+            self._data.extend(new_entries)
+        data = self._data
+        if repo.obsstore:
+            node = repo.changelog.node
+            succs = repo.obsstore.successors
+            for r in revs:
+                if node(r) in succs:
+                    data[r] = 1
 
     def _updatemarkers(self, repo, obsmarkers):
         """update the cache with new markers"""
@@ -443,11 +458,11 @@
         data = repo.vfs.tryread(self._filepath)
         if not data:
             self._cachekey = self.emptykey
-            self._data = bytearray()
+            self._setdata(bytearray())
         else:
             headersize = struct.calcsize(self._headerformat)
             self._cachekey = struct.unpack(self._headerformat, data[:headersize])
-            self._data = bytearray(data[headersize:])
+            self._setdata(bytearray(data[headersize:]))
         self._ondiskkey = self._cachekey
 
 def _computeobsoleteset(orig, repo):
@@ -507,23 +522,31 @@
                 self.obsstore.obscache.clear()
             super(obscacherepo, self).destroyed()
 
-        def transaction(self, *args, **kwargs):
-            tr = super(obscacherepo, self).transaction(*args, **kwargs)
-            reporef = weakref.ref(self)
-
-            def _warmcache(tr):
-                repo = reporef()
-                if repo is None:
-                    return
-                repo = repo.unfiltered()
-                # As pointed in 'obscache.update', we could have the changelog
-                # and the obsstore in charge of updating the cache when new
-                # items goes it. The tranaction logic would then only be
-                # involved for the 'pending' and final writing on disk.
+        if util.safehasattr(repo, 'updatecaches'):
+            @localrepo.unfilteredmethod
+            def updatecaches(self, tr=None):
+                super(obscacherepo, self).updatecaches(tr)
                 self.obsstore.obscache.update(repo)
                 self.obsstore.obscache.save(repo)
 
-            tr.addpostclose('warmcache-obscache', _warmcache)
-            return tr
+        else:
+            def transaction(self, *args, **kwargs):
+                tr = super(obscacherepo, self).transaction(*args, **kwargs)
+                reporef = weakref.ref(self)
+
+                def _warmcache(tr):
+                    repo = reporef()
+                    if repo is None:
+                        return
+                    repo = repo.unfiltered()
+                    # As pointed in 'obscache.update', we could have the changelog
+                    # and the obsstore in charge of updating the cache when new
+                    # items goes it. The tranaction logic would then only be
+                    # involved for the 'pending' and final writing on disk.
+                    self.obsstore.obscache.update(repo)
+                    self.obsstore.obscache.save(repo)
+
+                tr.addpostclose('warmcache-obscache', _warmcache)
+                return tr
 
     repo.__class__ = obscacherepo