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)