Mercurial > hg
changeset 41615:328ca3b9e545
branchmap: encapsulate cache updating in the map itself
Rather than have a repository update the cache, move handling of cache updates
into the branchmap module, in the form of a custom mapping class.
This makes later performance improvements easier to handle too.
Differential Revision: https://phab.mercurial-scm.org/D5638
author | Martijn Pieters <mj@octobus.net> |
---|---|
date | Mon, 21 Jan 2019 17:37:33 +0000 |
parents | fbd4ce55bcbd |
children | 2c13e91ede6e |
files | contrib/perf.py mercurial/branchmap.py mercurial/localrepo.py mercurial/statichttprepo.py mercurial/streamclone.py |
diffstat | 5 files changed, 89 insertions(+), 71 deletions(-) [+] |
line wrap: on
line diff
--- a/contrib/perf.py Thu Feb 07 21:16:25 2019 -0800 +++ b/contrib/perf.py Mon Jan 21 17:37:33 2019 +0000 @@ -2376,13 +2376,18 @@ view = repo else: view = repo.filtered(filtername) + if util.safehasattr(view._branchcaches, '_per_filter'): + filtered = view._branchcaches._per_filter + else: + # older versions + filtered = view._branchcaches def d(): if clear_revbranch: repo.revbranchcache()._clear() if full: view._branchcaches.clear() else: - view._branchcaches.pop(filtername, None) + filtered.pop(filtername, None) view.branchmap() return d # add filter in smaller subset to bigger subset
--- a/mercurial/branchmap.py Thu Feb 07 21:16:25 2019 -0800 +++ b/mercurial/branchmap.py Mon Jan 21 17:37:33 2019 +0000 @@ -43,75 +43,89 @@ 'served': 'immutable', 'immutable': 'base'} -def updatecache(repo): - """Update the cache for the given filtered view on a repository""" - # This can trigger updates for the caches for subsets of the filtered - # view, e.g. when there is no cache for this filtered view or the cache - # is stale. + +class BranchMapCache(object): + """Cache mapping""" + def __init__(self): + self._per_filter = {} - cl = repo.changelog - filtername = repo.filtername - bcache = repo._branchcaches.get(filtername) - if bcache is None or not bcache.validfor(repo): - # cache object missing or cache object stale? Read from disk - bcache = branchcache.fromfile(repo) + def __getitem__(self, repo): + self.updatecache(repo) + return self._per_filter[repo.filtername] + + def updatecache(self, repo): + """Update the cache for the given filtered view on a repository""" + # This can trigger updates for the caches for subsets of the filtered + # view, e.g. when there is no cache for this filtered view or the cache + # is stale. - revs = [] - if bcache is None: - # no (fresh) cache available anymore, perhaps we can re-use - # the cache for a subset, then extend that to add info on missing - # revisions. - subsetname = subsettable.get(filtername) - if subsetname is not None: - subset = repo.filtered(subsetname) - bcache = subset.branchmap().copy() - extrarevs = subset.changelog.filteredrevs - cl.filteredrevs - revs.extend(r for r in extrarevs if r <= bcache.tiprev) - else: - # nothing to fall back on, start empty. - bcache = branchcache() + cl = repo.changelog + filtername = repo.filtername + bcache = self._per_filter.get(filtername) + if bcache is None or not bcache.validfor(repo): + # cache object missing or cache object stale? Read from disk + bcache = branchcache.fromfile(repo) - revs.extend(cl.revs(start=bcache.tiprev + 1)) - if revs: - bcache.update(repo, revs) + revs = [] + if bcache is None: + # no (fresh) cache available anymore, perhaps we can re-use + # the cache for a subset, then extend that to add info on missing + # revisions. + subsetname = subsettable.get(filtername) + if subsetname is not None: + subset = repo.filtered(subsetname) + bcache = self[subset].copy() + extrarevs = subset.changelog.filteredrevs - cl.filteredrevs + revs.extend(r for r in extrarevs if r <= bcache.tiprev) + else: + # nothing to fall back on, start empty. + bcache = branchcache() - assert bcache.validfor(repo), filtername - repo._branchcaches[repo.filtername] = bcache + revs.extend(cl.revs(start=bcache.tiprev + 1)) + if revs: + bcache.update(repo, revs) -def replacecache(repo, bm): - """Replace the branchmap cache for a repo with a branch mapping. + assert bcache.validfor(repo), filtername + self._per_filter[repo.filtername] = bcache + + def replace(self, repo, remotebranchmap): + """Replace the branchmap cache for a repo with a branch mapping. + + This is likely only called during clone with a branch map from a + remote. - This is likely only called during clone with a branch map from a remote. - """ - cl = repo.changelog - clrev = cl.rev - clbranchinfo = cl.branchinfo - rbheads = [] - closed = [] - for bheads in bm.itervalues(): - rbheads.extend(bheads) - for h in bheads: - r = clrev(h) - b, c = clbranchinfo(r) - if c: - closed.append(h) + """ + cl = repo.changelog + clrev = cl.rev + clbranchinfo = cl.branchinfo + rbheads = [] + closed = [] + for bheads in remotebranchmap.itervalues(): + rbheads += bheads + for h in bheads: + r = clrev(h) + b, c = clbranchinfo(r) + if c: + closed.append(h) - if rbheads: - rtiprev = max((int(clrev(node)) - for node in rbheads)) - cache = branchcache(bm, - repo[rtiprev].node(), - rtiprev, - closednodes=closed) + if rbheads: + rtiprev = max((int(clrev(node)) for node in rbheads)) + cache = branchcache( + remotebranchmap, repo[rtiprev].node(), rtiprev, + closednodes=closed) - # Try to stick it as low as possible - # filter above served are unlikely to be fetch from a clone - for candidate in ('base', 'immutable', 'served'): - rview = repo.filtered(candidate) - if cache.validfor(rview): - repo._branchcaches[candidate] = cache - cache.write(rview) - break + # Try to stick it as low as possible + # filter above served are unlikely to be fetch from a clone + for candidate in ('base', 'immutable', 'served'): + rview = repo.filtered(candidate) + if cache.validfor(rview): + self._per_filter[candidate] = cache + cache.write(rview) + return + + def clear(self): + self._per_filter.clear() + class branchcache(dict): """A dict like object that hold branches heads cache.
--- a/mercurial/localrepo.py Thu Feb 07 21:16:25 2019 -0800 +++ b/mercurial/localrepo.py Mon Jan 21 17:37:33 2019 +0000 @@ -992,7 +992,7 @@ self._dirstatevalidatewarned = False - self._branchcaches = {} + self._branchcaches = branchmap.BranchMapCache() self._revbranchcache = None self._filterpats = {} self._datafilters = {} @@ -1520,8 +1520,7 @@ def branchmap(self): '''returns a dictionary {branch: [branchheads]} with branchheads ordered by increasing revision number''' - branchmap.updatecache(self) - return self._branchcaches[self.filtername] + return self._branchcaches[self] @unfilteredmethod def revbranchcache(self): @@ -2073,9 +2072,9 @@ return if tr is None or tr.changes['origrepolen'] < len(self): - # updating the unfiltered branchmap should refresh all the others, + # accessing the 'ser ved' branchmap should refresh all the others, self.ui.debug('updating the branch cache\n') - branchmap.updatecache(self.filtered('served')) + self.filtered('served').branchmap() if full: rbc = self.revbranchcache() @@ -2093,7 +2092,7 @@ # can't use delattr on proxy del self.__dict__[r'_tagscache'] - self.unfiltered()._branchcaches.clear() + self._branchcaches.clear() self.invalidatevolatilesets() self._sparsesignaturecache.clear()
--- a/mercurial/statichttprepo.py Thu Feb 07 21:16:25 2019 -0800 +++ b/mercurial/statichttprepo.py Mon Jan 21 17:37:33 2019 +0000 @@ -13,6 +13,7 @@ from .i18n import _ from . import ( + branchmap, changelog, error, localrepo, @@ -193,7 +194,7 @@ self.changelog = changelog.changelog(self.svfs) self._tags = None self.nodetagscache = None - self._branchcaches = {} + self._branchcaches = branchmap.BranchMapCache() self._revbranchcache = None self.encodepats = None self.decodepats = None
--- a/mercurial/streamclone.py Thu Feb 07 21:16:25 2019 -0800 +++ b/mercurial/streamclone.py Mon Jan 21 17:37:33 2019 +0000 @@ -13,7 +13,6 @@ from .i18n import _ from . import ( - branchmap, cacheutil, error, narrowspec, @@ -174,7 +173,7 @@ repo._writerequirements() if rbranchmap: - branchmap.replacecache(repo, rbranchmap) + repo._branchcaches.replace(repo, rbranchmap) repo.invalidate()