[PATCH 2 of 3] repoview: cache hidden changesets

David Soria Parra davidsp at fb.com
Thu Aug 7 13:08:46 CDT 2014


# HG changeset patch
# User David Soria Parra <davidsp at fb.com>
# Date 1407356774 25200
#      Wed Aug 06 13:26:14 2014 -0700
# Node ID 3da6820d38112cddfc21e2b99ab02957f93d80cf
# Parent  4ac33b2438c3d50d5f1f21423f0a51ed32d12994
repoview: cache hidden changesets

Add a cache for hidden changesets. We cache all hidden changesets computed by
_gethiddenblockers and check if a bookmark, tag or wd parent moved to a hidden
changesets in which case we recompute parts. This significantly speeds up
status, log, etc:

without caching:
$ time hg status
hg status  0.72s user 0.20s system 100% cpu 0.917 total

with caching
$ time hg status
hg status  0.49s user 0.15s system 100% cpu 0.645 total

We invalidate the cache if the append only obsstore size changed from what we
have observed before. In that case we recompute everything again.

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -1013,6 +1013,7 @@
     def invalidatevolatilesets(self):
         self.filteredrevcache.clear()
         obsolete.clearobscaches(self)
+        repoview.invalidatecache(self)
 
     def invalidatedirstate(self):
         '''Invalidates the dirstate, causing the next call to dirstate
diff --git a/mercurial/repoview.py b/mercurial/repoview.py
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -10,6 +10,7 @@
 import phases
 import util
 import obsolete
+import struct
 import tags as tagsmod
 
 
@@ -56,17 +57,61 @@
         blockers.extend([cl.rev(t[0]) for t in tags.values()])
     return frozenset(blockers)
 
+cachefile = 'cache/hidden'
+cachemeta = 'cache/hidden.len'
+def invalidatecache(repo):
+    for filename in [cachefile, cachemeta]:
+        if repo.vfs.exists(filename):
+            repo.vfs.unlink(filename)
+
 def computehidden(repo):
     """compute the set of hidden revision to filter
 
     During most operation hidden should be filtered."""
     assert not repo.changelog.filteredrevs
+
+    def iscachevalid(repo, hideable):
+        """The cache is invalid if the append-only obsstore has more markers
+        then we observed before."""
+        if repo.vfs.exists(cachefile):
+            oldlen = repo.vfs.tryread(cachemeta)
+            if oldlen and int(oldlen) == len(hideable):
+                return True
+        return False
+
+    hidden = frozenset()
     hideable = hideablerevs(repo)
     if hideable:
         cl = repo.changelog
-        blocked = cl.ancestors(_gethiddenblockers(repo), inclusive=True)
-        return frozenset(r for r in hideable if r not in blocked)
-    return frozenset()
+        if iscachevalid(repo, hideable):
+            data = repo.vfs.read(cachefile)
+            hidden = frozenset(struct.unpack('%sI' % (len(data) / 4), data))
+        else:
+            # recompute cache
+            blocked = cl.ancestors(_gethiddenblockers(repo), inclusive=True)
+            hidden = frozenset(r for r in hideable if r not in blocked)
+
+            # write cache to file
+            try:
+                data = struct.pack('%sI' % len(hidden), *hidden)
+                fh = repo.vfs(cachefile, 'w+b', atomictemp=True)
+                fh.write(data)
+                fh.close()
+
+                # our observed size of the obsstore. used for cache invalidation.
+                fh = repo.vfs(cachemeta, 'w+b', atomictemp=True)
+                fh.write('%d' % len(hideable))
+                fh.close()
+            except IOError:
+                ui.debug(_('error writing hidden changesets cache'))
+
+        # check if we have wd parents, bookmarks or tags pointing to hidden
+        # changesets and remove those.
+        dynamic = hidden & _getdynamicblockers(repo)
+        if dynamic:
+            blocked = cl.ancestors(dynamic, inclusive=True)
+            hidden &= frozenset(r for r in hideable if r not in blocked)
+    return hidden
 
 def computeunserved(repo):
     """compute the set of revision that should be filtered when used a server


More information about the Mercurial-devel mailing list