# HG changeset patch # User Joerg Sonnenberger # Date 2024-07-08 20:46:04 # Node ID b8647465b59ae1e9a877b64abfadb614f24011fd # Parent 8e3f6b5bf7205b6b5ea0d4582c00c775b4d8a87d exchange: improve computation of relevant markers for large repos Compute the candidate nodes with relevant markers directly from keys of the predecessors/successors/children dictionaries of obsstore. This is faster than iterating over all nodes directly. This test could be further improved for repositories with relative few markers compared to the repository size, but this is no longer hot already. With the current loop structure, the obshashrange use works as well as before as it passes lists with a single node. Adjust the interface by allowing revision lists as well as node lists. This helps cases that computes ancestors as it reduces the materialisation cost. Use this in _pushdiscoveryobsmarker and _getbundleobsmarkerpart. Improve the latter further by directly using ancestors(). diff --git a/mercurial/bundle2.py b/mercurial/bundle2.py --- a/mercurial/bundle2.py +++ b/mercurial/bundle2.py @@ -1788,7 +1788,7 @@ def _addpartsfromopts(ui, repo, bundler, addpartrevbranchcache(repo, bundler, outgoing) if opts.get(b'obsolescence', False): - obsmarkers = repo.obsstore.relevantmarkers(outgoing.missing) + obsmarkers = repo.obsstore.relevantmarkers(nodes=outgoing.missing) buildobsmarkerspart( bundler, obsmarkers, diff --git a/mercurial/exchange.py b/mercurial/exchange.py --- a/mercurial/exchange.py +++ b/mercurial/exchange.py @@ -703,8 +703,8 @@ def _pushdiscoveryobsmarkers(pushop): repo = pushop.repo # very naive computation, that can be quite expensive on big repo. # However: evolution is currently slow on them anyway. - nodes = (c.node() for c in repo.set(b'::%ln', pushop.futureheads)) - pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(nodes) + repo.revs(b'::%ln', pushop.futureheads) + pushop.outobsmarkers = pushop.repo.obsstore.relevantmarkers(revs=revs) @pushdiscovery(b'bookmarks') @@ -2603,10 +2603,15 @@ def _getbundleobsmarkerpart( ): """add an obsolescence markers part to the requested bundle""" if kwargs.get('obsmarkers', False): + unfi_cl = repo.unfiltered().changelog if heads is None: - heads = repo.heads() - subset = [c.node() for c in repo.set(b'::%ln', heads)] - markers = repo.obsstore.relevantmarkers(subset) + headrevs = repo.changelog.headrevs() + else: + get_rev = unfi_cl.index.get_rev + headrevs = [get_rev(node) for node in heads] + headrevs = [rev for rev in headrevs if rev is not None] + revs = unfi_cl.ancestors(headrevs, inclusive=True) + markers = repo.obsstore.relevantmarkers(revs=revs) markers = obsutil.sortedmarkers(markers) bundle2.buildobsmarkerspart(bundler, markers) diff --git a/mercurial/obsolete.py b/mercurial/obsolete.py --- a/mercurial/obsolete.py +++ b/mercurial/obsolete.py @@ -771,10 +771,11 @@ class obsstore: _addchildren(self.children, markers) _checkinvalidmarkers(self.repo, markers) - def relevantmarkers(self, nodes): - """return a set of all obsolescence markers relevant to a set of nodes. + def relevantmarkers(self, nodes=None, revs=None): + """return a set of all obsolescence markers relevant to a set of + nodes or revisions. - "relevant" to a set of nodes mean: + "relevant" to a set of nodes or revisions mean: - marker that use this changeset as successor - prune marker of direct children on this changeset @@ -782,13 +783,33 @@ class obsstore: markers It is a set so you cannot rely on order.""" + if nodes is None: + nodes = set() + if revs is None: + revs = set() - pendingnodes = set(nodes) - seenmarkers = set() - seennodes = set(pendingnodes) + tonode = self.repo.unfiltered().changelog.node + pendingnodes = set() precursorsmarkers = self.predecessors succsmarkers = self.successors children = self.children + for node in nodes: + if ( + node in precursorsmarkers + or node in succsmarkers + or node in children + ): + pendingnodes.add(node) + for rev in revs: + node = tonode(rev) + if ( + node in precursorsmarkers + or node in succsmarkers + or node in children + ): + pendingnodes.add(node) + seenmarkers = set() + seennodes = pendingnodes.copy() while pendingnodes: direct = set() for current in pendingnodes: diff --git a/mercurial/obsutil.py b/mercurial/obsutil.py --- a/mercurial/obsutil.py +++ b/mercurial/obsutil.py @@ -108,7 +108,7 @@ def getmarkers(repo, nodes=None, exclusi elif exclusive: rawmarkers = exclusivemarkers(repo, nodes) else: - rawmarkers = repo.obsstore.relevantmarkers(nodes) + rawmarkers = repo.obsstore.relevantmarkers(nodes=nodes) for markerdata in rawmarkers: yield marker(repo, markerdata)