changeset 51537:4a8bb136ee77

branchcache: allow to detect "pure topological case" for branchmap We don't rum this detection every time we run the branchcache, that would be costly. However we now do it when running `hg debugupdatecache`. This will help existing repository to benefit from the fastpath when possible.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 07 Mar 2024 10:57:16 +0100
parents 718f28ea3af4
children e4eeb9fedfe3
files mercurial/branchmap.py mercurial/interfaces/repository.py mercurial/localrepo.py
diffstat 3 files changed, 32 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/branchmap.py	Thu Mar 07 04:15:23 2024 +0100
+++ b/mercurial/branchmap.py	Thu Mar 07 10:57:16 2024 +0100
@@ -69,7 +69,7 @@
         )
         return bcache
 
-    def update_disk(self, repo):
+    def update_disk(self, repo, detect_pure_topo=False):
         """ensure and up-to-date cache is (or will be) written on disk
 
         The cache for this repository view is updated  if needed and written on
@@ -87,6 +87,8 @@
             bcache._filtername,
             repo.filtername,
         )
+        if detect_pure_topo:
+            bcache._detect_pure_topo(repo)
         tr = repo.currenttransaction()
         if getattr(tr, 'finalized', True):
             bcache.sync_disk(repo)
@@ -488,6 +490,9 @@
     def _ensure_populated(self, repo):
         """make sure any lazily loaded values are fully populated"""
 
+    def _detect_pure_topo(self, repo) -> None:
+        pass
+
     def validfor(self, repo):
         """check that cache contents are valid for (a subset of) this repo
 
@@ -1055,6 +1060,19 @@
             self._entries[self._pure_topo_branch] = heads
             self._needs_populate = False
 
+    def _detect_pure_topo(self, repo) -> None:
+        if self._pure_topo_branch is not None:
+            # we are pure topological already
+            return
+        to_node = repo.changelog.node
+        topo_heads = [to_node(r) for r in self._get_topo_heads(repo)]
+        if any(n in self._closednodes for n in topo_heads):
+            return
+        for branch, heads in self._entries.items():
+            if heads == topo_heads:
+                self._pure_topo_branch = branch
+                break
+
 
 class remotebranchcache(_BaseBranchCache):
     """Branchmap info for a remote connection, should not write locally"""
--- a/mercurial/interfaces/repository.py	Thu Mar 07 04:15:23 2024 +0100
+++ b/mercurial/interfaces/repository.py	Thu Mar 07 10:57:16 2024 +0100
@@ -54,6 +54,8 @@
 CACHE_BRANCHMAP_SERVED = b"branchmap-served"
 # Warm internal changelog cache (eg: persistent nodemap)
 CACHE_CHANGELOG_CACHE = b"changelog-cache"
+# check of a branchmap can use the "pure topo" mode
+CACHE_BRANCHMAP_DETECT_PURE_TOPO = b"branchmap-detect-pure-topo"
 # Warm full manifest cache
 CACHE_FULL_MANIFEST = b"full-manifest"
 # Warm file-node-tags cache
@@ -78,6 +80,7 @@
 CACHES_ALL = {
     CACHE_BRANCHMAP_SERVED,
     CACHE_BRANCHMAP_ALL,
+    CACHE_BRANCHMAP_DETECT_PURE_TOPO,
     CACHE_CHANGELOG_CACHE,
     CACHE_FILE_NODE_TAGS,
     CACHE_FULL_MANIFEST,
--- a/mercurial/localrepo.py	Thu Mar 07 04:15:23 2024 +0100
+++ b/mercurial/localrepo.py	Thu Mar 07 10:57:16 2024 +0100
@@ -2924,8 +2924,13 @@
         if repository.CACHE_BRANCHMAP_SERVED in caches:
             if tr is None or tr.changes[b'origrepolen'] < len(self):
                 self.ui.debug(b'updating the branch cache\n')
-                self._branchcaches.update_disk(self.filtered(b'served'))
-                self._branchcaches.update_disk(self.filtered(b'served.hidden'))
+                dpt = repository.CACHE_BRANCHMAP_DETECT_PURE_TOPO in caches
+                served = self.filtered(b'served')
+                self._branchcaches.update_disk(served, detect_pure_topo=dpt)
+                served_hidden = self.filtered(b'served.hidden')
+                self._branchcaches.update_disk(
+                    served_hidden, detect_pure_topo=dpt
+                )
 
         if repository.CACHE_CHANGELOG_CACHE in caches:
             self.changelog.update_caches(transaction=tr)
@@ -2968,9 +2973,11 @@
             # even if they haven't explicitly been requested yet (if they've
             # never been used by hg, they won't ever have been written, even if
             # they're a subset of another kind of cache that *has* been used).
+            dpt = repository.CACHE_BRANCHMAP_DETECT_PURE_TOPO in caches
+
             for filt in repoview.filtertable.keys():
                 filtered = self.filtered(filt)
-                self._branchcaches.update_disk(filtered)
+                self._branchcaches.update_disk(filtered, detect_pure_topo=dpt)
 
         # flush all possibly delayed write.
         self._branchcaches.write_dirty(self)