Patchwork [3,of,5,filtering,part,2] clfilter: add a cache on repo for set of revision to filter for a given set

login
register
mail settings
Submitter Pierre-Yves David
Date Dec. 4, 2012, 12:26 a.m.
Message ID <6c38bdbce9bcbd96fd23.1354580816@yamac.lan>
Download mbox | patch
Permalink /patch/8/
State Superseded, archived
Headers show

Comments

Pierre-Yves David - Dec. 4, 2012, 12:26 a.m.
# HG changeset patch
# User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
# Date 1354389187 -3600
# Node ID 6c38bdbce9bcbd96fd23476561f1443c17c18d6f
# Parent  e1e4bc0047f35958b6dbdb8dca10e81b60bbb223
clfilter: add a cache on repo for set of revision to filter for a given set.

Recomputing the filtered revisions at every access to changelog is far too
expensive. This changeset introduce a cache for this information. This cache is
hold by the repository (unfiltered repository) and invalidated when necessary.
This cache is not a protected attribute (leading _) because some logic that
invalidate it is not held by the local repo itself.
Pierre-Yves David - Dec. 7, 2012, 2:23 p.m.
On Tue, Dec 04, 2012 at 01:26:56AM +0100, Pierre-Yves David wrote:
> # HG changeset patch
> # User Pierre-Yves David <pierre-yves.david at ens-lyon.org>
> # Date 1354389187 -3600
> # Node ID 6c38bdbce9bcbd96fd23476561f1443c17c18d6f
> # Parent  e1e4bc0047f35958b6dbdb8dca10e81b60bbb223
> clfilter: add a cache on repo for set of revision to filter for a given set.
> 
> Recomputing the filtered revisions at every access to changelog is far too
> expensive. This changeset introduce a cache for this information. This cache is
> hold by the repository (unfiltered repository) and invalidated when necessary.
> This cache is not a protected attribute (leading _) because some logic that
> invalidate it is not held by the local repo itself.
> 
> diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
> --- a/mercurial/localrepo.py
> +++ b/mercurial/localrepo.py
> @@ -61,11 +61,14 @@ def unfilteredmethod(orig):
>  # function to compute filtered set
>  computefiltered = {}
>  
>  def _filteredrevs(repo, filtername):
>      """returns set of filtered revision for this filter name"""
> -    return computefiltered[filtername](repo.unfiltered())
> +    if filtername not in repo.revsfiltercache:
> +        func = computefiltered[filtername]
> +        repo.revsfiltercache[filtername] = func(repo.unfiltered())
> +    return repo.revsfiltercache[filtername]
>  
>  class repoproxy(object):
>      """Changelog filterered localrepo proxy object
>  
>      This object act is a proxy for attribute operation. setattr is done on the
> @@ -301,10 +304,19 @@ class localrepository(object):
>          # (used by the filecache decorator)
>          #
>          # Maps a property name to its util.filecacheentry
>          self._filecache = {}
>  
> +        # hold sets of revision t
> +        # should be cleared when
> +        # - new changesets,
> +        # - phase change,
> +        # - new obsolescence mark
> +        # - working directory par
> +        # - bookmark changes
> +        self.revsfiltercache = {}
> +

Comment have be misteriously truncated: you can read the full version here:

    http://hg-lab.logilab.org/wip/hg/rev/fa0778d07ece#l1.19

Patch

diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py
--- a/mercurial/localrepo.py
+++ b/mercurial/localrepo.py
@@ -61,11 +61,14 @@  def unfilteredmethod(orig):
 # function to compute filtered set
 computefiltered = {}
 
 def _filteredrevs(repo, filtername):
     """returns set of filtered revision for this filter name"""
-    return computefiltered[filtername](repo.unfiltered())
+    if filtername not in repo.revsfiltercache:
+        func = computefiltered[filtername]
+        repo.revsfiltercache[filtername] = func(repo.unfiltered())
+    return repo.revsfiltercache[filtername]
 
 class repoproxy(object):
     """Changelog filterered localrepo proxy object
 
     This object act is a proxy for attribute operation. setattr is done on the
@@ -301,10 +304,19 @@  class localrepository(object):
         # (used by the filecache decorator)
         #
         # Maps a property name to its util.filecacheentry
         self._filecache = {}
 
+        # hold sets of revision t
+        # should be cleared when
+        # - new changesets,
+        # - phase change,
+        # - new obsolescence mark
+        # - working directory par
+        # - bookmark changes
+        self.revsfiltercache = {}
+
     def close(self):
         pass
 
     def _restrictcapabilities(self, caps):
         return caps
@@ -1155,10 +1167,11 @@  class localrepository(object):
             del self.__dict__['_tagscache']
 
         self.unfiltered()._branchcache = None # in UTF-8
         self.unfiltered()._branchcachetip = None
         obsolete.clearobscaches(self)
+        self.revsfiltercache.clear()
 
     def invalidatedirstate(self):
         '''Invalidates the dirstate, causing the next call to dirstate
         to check if it was modified since the last time it was read,
         rereading it if it has.
@@ -1920,10 +1933,11 @@  class localrepository(object):
                         tr = self.transaction(trname)
                     for key in sorted(remoteobs, reverse=True):
                         if key.startswith('dump'):
                             data = base85.b85decode(remoteobs[key])
                             self.obsstore.mergemarkers(tr, data)
+                    self.revsfiltercache.clear()
             if tr is not None:
                 tr.close()
         finally:
             if tr is not None:
                 tr.release()
@@ -2529,10 +2543,11 @@  class localrepository(object):
 
             self.ui.status(_("added %d changesets"
                              " with %d changes to %d files%s\n")
                              % (changesets, revisions, files, htext))
             obsolete.clearobscaches(self)
+            self.revsfiltercache.clear()
 
             if changesets > 0:
                 p = lambda: cl.writepending() and self.root or ""
                 self.hook('pretxnchangegroup', throw=True,
                           node=hex(cl.node(clstart)), source=srctype,
diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py
--- a/mercurial/obsolete.py
+++ b/mercurial/obsolete.py
@@ -520,8 +520,9 @@  def createmarkers(repo, relations, flag=
             nprec = prec.node()
             nsucs = tuple(s.node() for s in sucs)
             if nprec in nsucs:
                 raise util.Abort("changeset %s cannot obsolete itself" % prec)
             repo.obsstore.create(tr, nprec, nsucs, flag, metadata)
+            repo.revsfiltercache.clear()
         tr.close()
     finally:
         tr.release()
diff --git a/mercurial/phases.py b/mercurial/phases.py
--- a/mercurial/phases.py
+++ b/mercurial/phases.py
@@ -247,10 +247,11 @@  class phasecache(object):
                 delroots.extend(olds - roots)
             # declare deleted root in the target phase
             if targetphase != 0:
                 self.retractboundary(repo, targetphase, delroots)
         obsolete.clearobscaches(repo)
+        repo.revsfiltercache.clear()
 
     def retractboundary(self, repo, targetphase, nodes):
         # Be careful to preserve shallow-copied values: do not update
         # phaseroots values, replace them.
 
@@ -265,10 +266,11 @@  class phasecache(object):
             currentroots.update(newroots)
             ctxs = repo.set('roots(%ln::)', currentroots)
             currentroots.intersection_update(ctx.node() for ctx in ctxs)
             self._updateroots(targetphase, currentroots)
         obsolete.clearobscaches(repo)
+        repo.revsfiltercache.clear()
 
 def advanceboundary(repo, targetphase, nodes):
     """Add nodes to a phase changing other nodes phases if necessary.
 
     This function move boundary *forward* this means that all nodes