Patchwork [3,of,4,V2] repoview: add caching bits

login
register
mail settings
Submitter David Soria Parra
Date Aug. 13, 2014, 9:44 p.m.
Message ID <b42aa11b82553e53ec29.1407966240@dev544.prn1.facebook.com>
Download mbox | patch
Permalink /patch/5377/
State Accepted
Headers show

Comments

David Soria Parra - Aug. 13, 2014, 9:44 p.m.
# HG changeset patch
# User David Soria Parra <davidsp@fb.com>
# Date 1407887334 25200
#      Tue Aug 12 16:48:54 2014 -0700
# Node ID b42aa11b82553e53ec29e145be46facc5fe09394
# Parent  fc977262986e188702091f7d98f1ccd076e36316
repoview: add caching bits

Add a caching infrastructure to cache hidden changesets. The cache tries to read
the cache lazily and falls back to recomputing if no wlock can be obtain.
To validate the cache we store a sha of the obstore content and repo heads in
the beginning of the cache which we check every request.

Patch

diff --git a/mercurial/repoview.py b/mercurial/repoview.py
--- a/mercurial/repoview.py
+++ b/mercurial/repoview.py
@@ -7,11 +7,14 @@ 
 # GNU General Public License version 2 or any later version.
 
 import copy
+import error
+import hashlib
 import phases
 import util
 import obsolete
+import struct
 import tags as tagsmod
-
+from mercurial.i18n import _
 
 def hideablerevs(repo):
     """Revisions candidates to be hidden
@@ -55,6 +58,70 @@ 
         blockers.update([cl.rev(t[0]) for t in tags.values()])
     return blockers
 
+cacheversion = 1
+cachefile = 'cache/hidden'
+
+def cachehash(repo, hideable):
+    """return sha1 hash of repository data to identify a valid cache.
+
+    We calculate a sha1 of repo heads and the content of the obsstore and write
+    it to the cache. Upon reading we can easily validate by checking the hash
+    against the stored one and discard the cache in case the hashes don't match.
+    """
+    h = hashlib.sha1()
+    h.update(''.join(repo.heads()))
+    h.update(str(hash(frozenset(hideable))))
+    return h.digest()
+
+def trywritehiddencache(repo, hideable, hidden):
+    """write cache of hidden changesets to disk
+
+    Will not write the cache if a wlock cannot be obtained lazily.
+    The cache consists of a head of 22byte:
+       2 byte    version number of the cache
+      20 byte    sha1 to validate the cache
+     n*4 byte    hidden revs
+    """
+    wlock = fh = None
+    try:
+        wlock = repo.wlock(wait=False)
+        # write cache to file
+        newhash = cachehash(repo, hideable)
+        sortedset = sorted(hidden)
+        data = struct.pack('>%iI' % len(sortedset), *sortedset)
+        fh = repo.vfs.open(cachefile, 'w+b', atomictemp=True)
+        fh.write(struct.pack(">H", cacheversion))
+        fh.write(newhash)
+        fh.write(data)
+    except (IOError, OSError):
+        ui.debug('error writing hidden changesets cache')
+    except error.LockHeld:
+        ui.debug('cannot obtain lock to write hidden changesets cache')
+    finally:
+        if fh:
+            fh.close()
+        if wlock:
+            wlock.release()
+
+def tryreadcache(repo, hideable):
+    """read a cache if the cache exists and is valid, otherwise returns None."""
+    hidden = fh = None
+    try:
+        if repo.vfs.exists(cachefile):
+            fh = repo.vfs.open(cachefile, 'rb')
+            version, = struct.unpack(">H", fh.read(2))
+            oldhash = fh.read(20)
+            newhash = cachehash(repo, hideable)
+            if (cacheversion, oldhash) == (version, newhash):
+                # cache is valid, so we can start reading the hidden revs
+                data = fh.read()
+                count = len(data) / 4
+                hidden = frozenset(struct.unpack('>%iI' % count, data))
+        return hidden
+    finally:
+        if fh:
+            fh.close()
+
 def computehidden(repo):
     """compute the set of hidden revision to filter