changeset 47875:0fb328bb2459

debugrebuildfncache: add a cheaper option to rebuild the fncache On my repository, debugrebuildfncache takes 5-10min with the lock. With the flag added in this commit, it takes 10s. The tradeoff is that it only recovers from certain kinds of corruptions. It is intended to to recover faster from fncaches broken by a revlog split during a transaction that ends up being rolled back. Differential Revision: https://phab.mercurial-scm.org/D11265
author Valentin Gatien-Baron <vgatien-baron@janestreet.com>
date Fri, 06 Aug 2021 16:27:17 -0400
parents 308e843f24b1
children 517a2c1cb788
files mercurial/debugcommands.py mercurial/repair.py tests/test-completion.t tests/test-transaction-rollback-on-revlog-split.t
diffstat 4 files changed, 51 insertions(+), 23 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/debugcommands.py	Fri Aug 06 16:17:17 2021 -0400
+++ b/mercurial/debugcommands.py	Fri Aug 06 16:27:17 2021 -0400
@@ -2987,10 +2987,22 @@
         dirstate.rebuild(ctx.node(), ctx.manifest(), changedfiles)
 
 
-@command(b'debugrebuildfncache', [], b'')
-def debugrebuildfncache(ui, repo):
+@command(
+    b'debugrebuildfncache',
+    [
+        (
+            b'',
+            b'only-data',
+            False,
+            _(b'only look for wrong .d files (much faster)'),
+        )
+    ],
+    b'',
+)
+def debugrebuildfncache(ui, repo, **opts):
     """rebuild the fncache file"""
-    repair.rebuildfncache(ui, repo)
+    opts = pycompat.byteskwargs(opts)
+    repair.rebuildfncache(ui, repo, opts.get(b"only_data"))
 
 
 @command(
--- a/mercurial/repair.py	Fri Aug 06 16:17:17 2021 -0400
+++ b/mercurial/repair.py	Fri Aug 06 16:27:17 2021 -0400
@@ -441,7 +441,7 @@
                 yield repo.manifestlog.getstorage(dir)
 
 
-def rebuildfncache(ui, repo):
+def rebuildfncache(ui, repo, only_data=False):
     """Rebuilds the fncache file from repo history.
 
     Missing entries will be added. Extra entries will be removed.
@@ -465,28 +465,40 @@
         newentries = set()
         seenfiles = set()
 
-        progress = ui.makeprogress(
-            _(b'rebuilding'), unit=_(b'changesets'), total=len(repo)
-        )
-        for rev in repo:
-            progress.update(rev)
+        if only_data:
+            # Trust the listing of .i from the fncache, but not the .d. This is
+            # much faster, because we only need to stat every possible .d files,
+            # instead of reading the full changelog
+            for f in fnc:
+                if f[:5] == b'data/' and f[-2:] == b'.i':
+                    seenfiles.add(f[5:-2])
+                    newentries.add(f)
+                    dataf = f[:-2] + b'.d'
+                    if repo.store._exists(dataf):
+                        newentries.add(dataf)
+        else:
+            progress = ui.makeprogress(
+                _(b'rebuilding'), unit=_(b'changesets'), total=len(repo)
+            )
+            for rev in repo:
+                progress.update(rev)
 
-            ctx = repo[rev]
-            for f in ctx.files():
-                # This is to minimize I/O.
-                if f in seenfiles:
-                    continue
-                seenfiles.add(f)
+                ctx = repo[rev]
+                for f in ctx.files():
+                    # This is to minimize I/O.
+                    if f in seenfiles:
+                        continue
+                    seenfiles.add(f)
 
-                i = b'data/%s.i' % f
-                d = b'data/%s.d' % f
+                    i = b'data/%s.i' % f
+                    d = b'data/%s.d' % f
 
-                if repo.store._exists(i):
-                    newentries.add(i)
-                if repo.store._exists(d):
-                    newentries.add(d)
+                    if repo.store._exists(i):
+                        newentries.add(i)
+                    if repo.store._exists(d):
+                        newentries.add(d)
 
-        progress.complete()
+            progress.complete()
 
         if requirements.TREEMANIFEST_REQUIREMENT in repo.requirements:
             # This logic is safe if treemanifest isn't enabled, but also
--- a/tests/test-completion.t	Fri Aug 06 16:17:17 2021 -0400
+++ b/tests/test-completion.t	Fri Aug 06 16:27:17 2021 -0400
@@ -316,7 +316,7 @@
   debugpushkey: 
   debugpvec: 
   debugrebuilddirstate: rev, minimal
-  debugrebuildfncache: 
+  debugrebuildfncache: only-data
   debugrename: rev
   debugrequires: 
   debugrevlog: changelog, manifest, dir, dump
--- a/tests/test-transaction-rollback-on-revlog-split.t	Fri Aug 06 16:17:17 2021 -0400
+++ b/tests/test-transaction-rollback-on-revlog-split.t	Fri Aug 06 16:27:17 2021 -0400
@@ -86,6 +86,10 @@
    warning: revlog 'data/file.d' not in fncache!
   1 warnings encountered!
   hint: run "hg debugrebuildfncache" to recover from corrupt fncache
+  $ hg debugrebuildfncache --only-data
+  adding data/file.d
+  1 items added, 0 removed from fncache
+  $ hg verify -q
   $ cd ..