rev-branch-cache: stop truncating cache file
Truncating the file prevent the safe use of mmap. So instead of overwrite the
existing data. If more than 20% of the file is to be overwritten, we rewrite the
whole file instead.
Such whole rewrite is done by replacing the old one with a new one, so mmap of
the old file would be affected.
This prepare a more aggressive use of mmap in later patches.
--- a/mercurial/branching/rev_cache.py Tue Sep 24 00:16:04 2024 +0200
+++ b/mercurial/branching/rev_cache.py Tue Sep 24 00:16:23 2024 +0200
@@ -4,6 +4,7 @@
# GNU General Public License version 2 or any later version.
from __future__ import annotations
+import os
import struct
from ..node import (
@@ -39,6 +40,10 @@
_rbccloseflag = 0x80000000
+# with atomic replacement.
+REWRITE_RATIO = 0.2
+
+
class rbcrevs:
"""a byte string consisting of an immutable prefix followed by a mutable suffix"""
@@ -345,19 +350,44 @@
def _writerevs(self, repo, start):
"""write the new revs to revbranchcache"""
revs = min(len(repo.changelog), len(self._rbcrevs) // _rbcrecsize)
+
+ end = revs * _rbcrecsize
if self._force_overwrite:
start = 0
- with repo.cachevfs.open(_rbcrevs, b'ab') as f:
+
+ with repo.cachevfs.open(_rbcrevs, b'a+b') as f:
+ pass # this make sure the file exist…
+ with repo.cachevfs.open(_rbcrevs, b'r+b') as f:
+ f.seek(0, os.SEEK_END)
current_size = f.tell()
if current_size < start:
start = 0
if current_size != start:
- msg = b"truncating cache/%s to %d\n"
- msg %= (_rbcrevs, start)
- repo.ui.debug(msg)
+ threshold = current_size * REWRITE_RATIO
+ if (max(end, current_size) - start) < threshold:
+ # end affected, let overwrite the bad value
+ dbg = b"overwriting %d bytes from %d in cache/%s"
+ dbg %= (current_size - start, start, _rbcrevs)
+ if end < current_size:
+ extra = b" leaving (%d trailing bytes)"
+ extra %= current_size - end
+ dbg += extra
+ dbg += b'\n'
+ repo.ui.debug(dbg)
+ else:
+ start = 0
+ dbg = b"resetting content of cache/%s\n" % _rbcrevs
+ repo.ui.debug(dbg)
+ if start > 0:
f.seek(start)
- f.truncate()
- end = revs * _rbcrecsize
- f.write(self._rbcrevs.slice(start, end))
+ f.write(self._rbcrevs.slice(start, end))
+ else:
+ f.close()
+ with repo.cachevfs.open(
+ _rbcrevs,
+ b'wb',
+ atomictemp=True,
+ ) as rev_file:
+ rev_file.write(self._rbcrevs.slice(start, end))
self._rbcrevslen = revs
self._force_overwrite = False
--- a/tests/test-acl.t Tue Sep 24 00:16:04 2024 +0200
+++ b/tests/test-acl.t Tue Sep 24 00:16:23 2024 +0200
@@ -202,7 +202,7 @@
bundle2-input-part: "phase-heads" supported
bundle2-input-part: total payload size * (glob)
bundle2-input-bundle: 5 parts total
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
updating the branch cache
added 3 changesets with 3 changes to 3 files
bundle2-output-bundle: "HG20", 1 parts total
@@ -280,7 +280,7 @@
bundle2-input-part: "phase-heads" supported
bundle2-input-part: total payload size * (glob)
bundle2-input-bundle: 5 parts total
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
updating the branch cache
added 3 changesets with 3 changes to 3 files
bundle2-output-bundle: "HG20", 1 parts total
@@ -355,7 +355,7 @@
bundle2-input-bundle: 5 parts total
transaction abort!
rollback completed
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
abort: acl: user "fred" not allowed on "foo/file.txt" (changeset "ef1ea85a6374")
no rollback information available
0:6675d58eff77
@@ -879,7 +879,7 @@
bundle2-input-bundle: 7 parts total
transaction abort!
rollback completed
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
abort: acl: user "fred" denied on bookmark "moving-bookmark" (changeset "ef1ea85a6374b77d6da9dcda9541f498f2d17df7")
no rollback information available
0:6675d58eff77
@@ -1048,7 +1048,7 @@
bundle2-input-bundle: 5 parts total
transaction abort!
rollback completed
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
abort: acl: user "wilma" not allowed on "quux/file.py" (changeset "911600dab2ae")
no rollback information available
0:6675d58eff77
@@ -1380,7 +1380,7 @@
bundle2-input-part: "phase-heads" supported
bundle2-input-part: total payload size * (glob)
bundle2-input-bundle: 5 parts total
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
updating the branch cache
added 3 changesets with 3 changes to 3 files
bundle2-output-bundle: "HG20", 1 parts total
@@ -1464,7 +1464,7 @@
bundle2-input-bundle: 5 parts total
transaction abort!
rollback completed
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
no rollback information available
0:6675d58eff77
@@ -1632,7 +1632,7 @@
bundle2-input-bundle: 5 parts total
transaction abort!
rollback completed
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
abort: acl: user "fred" denied on "foo/Bar/file.txt" (changeset "f9cafe1212c8")
no rollback information available
0:6675d58eff77
--- a/tests/test-branches.t Tue Sep 24 00:16:04 2024 +0200
+++ b/tests/test-branches.t Tue Sep 24 00:16:23 2024 +0200
@@ -835,9 +835,9 @@
$ echo >> .hg/cache/rbc-revs-v1
$ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
5
- truncating cache/rbc-revs-v1 to 160
+ overwriting 2 bytes from 160 in cache/rbc-revs-v1 leaving (2 trailing bytes)
$ f --size .hg/cache/rbc-revs*
- .hg/cache/rbc-revs-v1: size=160
+ .hg/cache/rbc-revs-v1: size=162
recovery from invalid cache file with partial last record
$ mv .hg/cache/rbc-revs-v1 .
@@ -846,7 +846,7 @@
.hg/cache/rbc-revs-v1: size=119
$ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
5
- truncating cache/rbc-revs-v1 to 112
+ resetting content of cache/rbc-revs-v1
$ f --size .hg/cache/rbc-revs*
.hg/cache/rbc-revs-v1: size=160
@@ -869,10 +869,10 @@
$ hg log -r 'branch(.)' -T '{rev} ' --debug
history modification detected - truncating revision branch cache to revision * (glob)
history modification detected - truncating revision branch cache to revision 1
- 3 4 8 9 10 11 12 13 truncating cache/rbc-revs-v1 to 8
+ 3 4 8 9 10 11 12 13 resetting content of cache/rbc-revs-v1
$ rm -f .hg/cache/branch* && hg head a -T '{rev}\n' --debug
5
- truncating cache/rbc-revs-v1 to 104
+ resetting content of cache/rbc-revs-v1
$ f --size --hexdump --bytes=16 .hg/cache/rbc-revs*
.hg/cache/rbc-revs-v1: size=160
0000: 19 70 9c 5a 00 00 00 00 dd 6b 44 0d 00 00 00 01 |.p.Z.....kD.....|
@@ -893,7 +893,7 @@
.hg/cache/rbc-names-v1: size=111
$ grep "i-will-regret-this" .hg/cache/rbc-names-* > /dev/null
$ f --size .hg/cache/rbc-revs-*
- .hg/cache/rbc-revs-v1: size=160
+ .hg/cache/rbc-revs-v1: size=168
cache is updated/truncated when stripping - it is thus very hard to get in a
situation where the cache is out of sync and the hash check detects it
--- a/tests/test-rebase-conflicts.t Tue Sep 24 00:16:04 2024 +0200
+++ b/tests/test-rebase-conflicts.t Tue Sep 24 00:16:23 2024 +0200
@@ -319,14 +319,14 @@
bundle2-input-part: "phase-heads" supported
bundle2-input-part: total payload size 24
bundle2-input-bundle: 3 parts total
- truncating cache/rbc-revs-v1 to 0
+ resetting content of cache/rbc-revs-v1
added 2 changesets with 2 changes to 1 files
updating the branch cache
invalid branch cache (served): tip differs
history modification detected - truncating revision branch cache to revision 1
invalid branch cache (served.hidden): tip differs
rebase completed
- truncating cache/rbc-revs-v1 to 8
+ resetting content of cache/rbc-revs-v1
Test minimization of merge conflicts
$ hg up -q null
--- a/tests/test-strip.t Tue Sep 24 00:16:04 2024 +0200
+++ b/tests/test-strip.t Tue Sep 24 00:16:23 2024 +0200
@@ -913,7 +913,7 @@
saved backup bundle to $TESTTMP/issue4736/.hg/strip-backup/6625a5168474-345bb43d-backup.hg
updating the branch cache
invalid branch cache (served): tip differs
- truncating cache/rbc-revs-v1 to 0
+ resetting content of cache/rbc-revs-v1
$ hg log -G
o changeset: 2:5c51d8d6557d
| tag: tip