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.
| pulkit |
| hg-reviewers |
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.
| Lint Skipped |
| Unit Tests Skipped |
| contrib/perf.py | ||
|---|---|---|
| 2302 | Same here: I think this needs to be made compatible with both versions (before and after this patch) | |
| mercurial/localrepo.py | ||
| 2077 | Hmm, it's much less clear now that this updates the cache. At the very least, it deserves a comment. Is the updatecache() call from __getitem__() necessary for your later patches? Sorry, I didn't quite follow. | |
| mercurial/streamclone.py | ||
| 15–16 | test-check-pyflakes.t says that this is now unused | |
| contrib/perf.py | ||
|---|---|---|
| 2302 | I'll wait for confirmation; see the other patch. | |
| mercurial/localrepo.py | ||
| 2077 | Yes, the updating will change in a later, as yet to be submitted patch. But why should it be clear here that there is a cache that is kept up-to-date on access to the branchmap? That's a responsibility of the cache, not of whatever accesses a branchmap. The cache is an implementation detail of branchmap, and should really not bleed out into code that merely consumes the map. | |
| mercurial/localrepo.py | ||
|---|---|---|
| 2077 | Fair enough, it doesn't have to be clear that it updates the cache, but it has to be clear that it does *something*. It currently looks like a statement that can be removed, but I assume it cannot. | |
In D5638#86131, @pulkit wrote:This looks good to me. @martinvonz do you have any concerns?
Yes, looks good. Sorry, I thought this had already been queued.
| Path | Packages | |||
|---|---|---|---|---|
| M | contrib/perf.py (7 lines) | |||
| M | mercurial/branchmap.py (136 lines) | |||
| M | mercurial/localrepo.py (11 lines) | |||
| M | mercurial/statichttprepo.py (3 lines) | |||
| M | mercurial/streamclone.py (3 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| Martijn Pieters | Jan 21 2019, 12:37 PM |
| # This create and ordering used for branchmap purpose. | # This create and ordering used for branchmap purpose. | ||||
| # the ordering may be partial | # the ordering may be partial | ||||
| subsettable = {None: 'visible', | subsettable = {None: 'visible', | ||||
| 'visible-hidden': 'visible', | 'visible-hidden': 'visible', | ||||
| 'visible': 'served', | 'visible': 'served', | ||||
| 'served': 'immutable', | 'served': 'immutable', | ||||
| 'immutable': 'base'} | 'immutable': 'base'} | ||||
| def updatecache(repo): | |||||
| class BranchMapCache(object): | |||||
| """Cache mapping""" | |||||
| def __init__(self): | |||||
| self._per_filter = {} | |||||
| 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""" | """Update the cache for the given filtered view on a repository""" | ||||
| # This can trigger updates for the caches for subsets of the filtered | # 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 | # view, e.g. when there is no cache for this filtered view or the cache | ||||
| # is stale. | # is stale. | ||||
| cl = repo.changelog | cl = repo.changelog | ||||
| filtername = repo.filtername | filtername = repo.filtername | ||||
| bcache = repo._branchcaches.get(filtername) | bcache = self._per_filter.get(filtername) | ||||
| if bcache is None or not bcache.validfor(repo): | if bcache is None or not bcache.validfor(repo): | ||||
| # cache object missing or cache object stale? Read from disk | # cache object missing or cache object stale? Read from disk | ||||
| bcache = branchcache.fromfile(repo) | bcache = branchcache.fromfile(repo) | ||||
| revs = [] | revs = [] | ||||
| if bcache is None: | if bcache is None: | ||||
| # no (fresh) cache available anymore, perhaps we can re-use | # no (fresh) cache available anymore, perhaps we can re-use | ||||
| # the cache for a subset, then extend that to add info on missing | # the cache for a subset, then extend that to add info on missing | ||||
| # revisions. | # revisions. | ||||
| subsetname = subsettable.get(filtername) | subsetname = subsettable.get(filtername) | ||||
| if subsetname is not None: | if subsetname is not None: | ||||
| subset = repo.filtered(subsetname) | subset = repo.filtered(subsetname) | ||||
| bcache = subset.branchmap().copy() | bcache = self[subset].copy() | ||||
| extrarevs = subset.changelog.filteredrevs - cl.filteredrevs | extrarevs = subset.changelog.filteredrevs - cl.filteredrevs | ||||
| revs.extend(r for r in extrarevs if r <= bcache.tiprev) | revs.extend(r for r in extrarevs if r <= bcache.tiprev) | ||||
| else: | else: | ||||
| # nothing to fall back on, start empty. | # nothing to fall back on, start empty. | ||||
| bcache = branchcache() | bcache = branchcache() | ||||
| revs.extend(cl.revs(start=bcache.tiprev + 1)) | revs.extend(cl.revs(start=bcache.tiprev + 1)) | ||||
| if revs: | if revs: | ||||
| bcache.update(repo, revs) | bcache.update(repo, revs) | ||||
| assert bcache.validfor(repo), filtername | assert bcache.validfor(repo), filtername | ||||
| repo._branchcaches[repo.filtername] = bcache | self._per_filter[repo.filtername] = bcache | ||||
| def replacecache(repo, bm): | def replace(self, repo, remotebranchmap): | ||||
| """Replace the branchmap cache for a repo with a branch mapping. | """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 | cl = repo.changelog | ||||
| clrev = cl.rev | clrev = cl.rev | ||||
| clbranchinfo = cl.branchinfo | clbranchinfo = cl.branchinfo | ||||
| rbheads = [] | rbheads = [] | ||||
| closed = [] | closed = [] | ||||
| for bheads in bm.itervalues(): | for bheads in remotebranchmap.itervalues(): | ||||
| rbheads.extend(bheads) | rbheads += bheads | ||||
| for h in bheads: | for h in bheads: | ||||
| r = clrev(h) | r = clrev(h) | ||||
| b, c = clbranchinfo(r) | b, c = clbranchinfo(r) | ||||
| if c: | if c: | ||||
| closed.append(h) | closed.append(h) | ||||
| if rbheads: | if rbheads: | ||||
| rtiprev = max((int(clrev(node)) | rtiprev = max((int(clrev(node)) for node in rbheads)) | ||||
| for node in rbheads)) | cache = branchcache( | ||||
| cache = branchcache(bm, | remotebranchmap, repo[rtiprev].node(), rtiprev, | ||||
| repo[rtiprev].node(), | |||||
| rtiprev, | |||||
| closednodes=closed) | closednodes=closed) | ||||
| # Try to stick it as low as possible | # Try to stick it as low as possible | ||||
| # filter above served are unlikely to be fetch from a clone | # filter above served are unlikely to be fetch from a clone | ||||
| for candidate in ('base', 'immutable', 'served'): | for candidate in ('base', 'immutable', 'served'): | ||||
| rview = repo.filtered(candidate) | rview = repo.filtered(candidate) | ||||
| if cache.validfor(rview): | if cache.validfor(rview): | ||||
| repo._branchcaches[candidate] = cache | self._per_filter[candidate] = cache | ||||
| cache.write(rview) | cache.write(rview) | ||||
| break | return | ||||
| def clear(self): | |||||
| self._per_filter.clear() | |||||
| class branchcache(dict): | class branchcache(dict): | ||||
| """A dict like object that hold branches heads cache. | """A dict like object that hold branches heads cache. | ||||
| This cache is used to avoid costly computations to determine all the | This cache is used to avoid costly computations to determine all the | ||||
| branch heads of a repo. | branch heads of a repo. | ||||
| The cache is serialized on disk in the following format: | The cache is serialized on disk in the following format: | ||||
| # statichttprepo.py - simple http repository class for mercurial | # statichttprepo.py - simple http repository class for mercurial | ||||
| # | # | ||||
| # This provides read-only repo access to repositories exported via static http | # This provides read-only repo access to repositories exported via static http | ||||
| # | # | ||||
| # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | # Copyright 2005-2007 Matt Mackall <mpm@selenic.com> | ||||
| # | # | ||||
| # This software may be used and distributed according to the terms of the | # This software may be used and distributed according to the terms of the | ||||
| # GNU General Public License version 2 or any later version. | # GNU General Public License version 2 or any later version. | ||||
| from __future__ import absolute_import | from __future__ import absolute_import | ||||
| import errno | import errno | ||||
| from .i18n import _ | from .i18n import _ | ||||
| from . import ( | from . import ( | ||||
| branchmap, | |||||
| changelog, | changelog, | ||||
| error, | error, | ||||
| localrepo, | localrepo, | ||||
| manifest, | manifest, | ||||
| namespaces, | namespaces, | ||||
| pathutil, | pathutil, | ||||
| url, | url, | ||||
| util, | util, | ||||
| self.requirements = requirements | self.requirements = requirements | ||||
| rootmanifest = manifest.manifestrevlog(self.svfs) | rootmanifest = manifest.manifestrevlog(self.svfs) | ||||
| self.manifestlog = manifest.manifestlog(self.svfs, self, rootmanifest, | self.manifestlog = manifest.manifestlog(self.svfs, self, rootmanifest, | ||||
| self.narrowmatch()) | self.narrowmatch()) | ||||
| self.changelog = changelog.changelog(self.svfs) | self.changelog = changelog.changelog(self.svfs) | ||||
| self._tags = None | self._tags = None | ||||
| self.nodetagscache = None | self.nodetagscache = None | ||||
| self._branchcaches = {} | self._branchcaches = branchmap.BranchMapCache() | ||||
| self._revbranchcache = None | self._revbranchcache = None | ||||
| self.encodepats = None | self.encodepats = None | ||||
| self.decodepats = None | self.decodepats = None | ||||
| self._transref = None | self._transref = None | ||||
| def _restrictcapabilities(self, caps): | def _restrictcapabilities(self, caps): | ||||
| caps = super(statichttprepository, self)._restrictcapabilities(caps) | caps = super(statichttprepository, self)._restrictcapabilities(caps) | ||||
| return caps.difference(["pushkey"]) | return caps.difference(["pushkey"]) | ||||
Same here: I think this needs to be made compatible with both versions (before and after this patch)