changeset 44369:e41a164db7a9

nodemap: track the maximum revision tracked in the nodemap We need a simple way to detect when the on disk data contains less revision than the index we read from disk. The docket file is meant for this, we just had to start tracking that data. We should also try to detect strip operation, but we will deal with this in later changesets. Right now we are focusing on defining the API for index supporting persistent nodemap. Differential Revision: https://phab.mercurial-scm.org/D7888
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 15 Jan 2020 15:50:14 +0100
parents 6614b301ea58
children 8374b69aef75
files mercurial/debugcommands.py mercurial/pure/parsers.py mercurial/revlog.py mercurial/revlogutils/nodemap.py tests/test-persistent-nodemap.t
diffstat 5 files changed, 20 insertions(+), 10 deletions(-) [+]
line wrap: on
line diff
--- a/mercurial/debugcommands.py	Wed Jan 15 15:50:04 2020 +0100
+++ b/mercurial/debugcommands.py	Wed Jan 15 15:50:14 2020 +0100
@@ -2137,6 +2137,7 @@
         if nm_data is not None:
             docket, data = nm_data
             ui.write((b"uid: %s\n") % docket.uid)
+            ui.write((b"tip-rev: %d\n") % docket.tip_rev)
 
 
 @command(
--- a/mercurial/pure/parsers.py	Wed Jan 15 15:50:04 2020 +0100
+++ b/mercurial/pure/parsers.py	Wed Jan 15 15:50:14 2020 +0100
@@ -170,15 +170,15 @@
         self._nm_root = self._nm_max_idx = self._nm_rev = None
         return data
 
-    def update_nodemap_data(self, nm_data):
-        """provide full blokc of persisted binary data for a nodemap
+    def update_nodemap_data(self, docket, nm_data):
+        """provide full block of persisted binary data for a nodemap
 
         The data are expected to come from disk. See `nodemap_data_all` for a
         produceur of such data."""
         if nm_data is not None:
             self._nm_root, self._nm_max_idx = nodemaputil.parse_data(nm_data)
             if self._nm_root:
-                self._nm_rev = len(self) - 1
+                self._nm_rev = docket.tip_rev
             else:
                 self._nm_root = self._nm_max_idx = self._nm_rev = None
 
--- a/mercurial/revlog.py	Wed Jan 15 15:50:04 2020 +0100
+++ b/mercurial/revlog.py	Wed Jan 15 15:50:14 2020 +0100
@@ -639,7 +639,7 @@
             if use_nodemap:
                 nodemap_data = nodemaputil.persisted_data(self)
                 if nodemap_data is not None:
-                    index.update_nodemap_data(nodemap_data[1])
+                    index.update_nodemap_data(*nodemap_data)
         except (ValueError, IndexError):
             raise error.RevlogError(
                 _(b"index %s is corrupted") % self.indexfile
--- a/mercurial/revlogutils/nodemap.py	Wed Jan 15 15:50:04 2020 +0100
+++ b/mercurial/revlogutils/nodemap.py	Wed Jan 15 15:50:14 2020 +0100
@@ -36,9 +36,11 @@
     if version != ONDISK_VERSION:
         return None
     offset += S_VERSION.size
-    (uid_size,) = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
+    headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
+    uid_size, tip_rev = headers
     offset += S_HEADER.size
     docket = NodeMapDocket(pdata[offset : offset + uid_size])
+    docket.tip_rev = tip_rev
 
     filename = _rawdata_filepath(revlog, docket)
     return docket, revlog.opener.tryread(filename)
@@ -94,6 +96,7 @@
         # store vfs
         with revlog.opener(datafile, b'w') as fd:
             fd.write(data)
+    target_docket.tip_rev = revlog.tiprev()
     # EXP-TODO: if this is a cache, this should use a cache vfs, not a
     # store vfs
     with revlog.opener(revlog.nodemap_file, b'w', atomictemp=True) as fp:
@@ -142,7 +145,7 @@
 ONDISK_VERSION = 0
 
 S_VERSION = struct.Struct(">B")
-S_HEADER = struct.Struct(">B")
+S_HEADER = struct.Struct(">BQ")
 
 ID_SIZE = 8
 
@@ -164,15 +167,19 @@
         if uid is None:
             uid = _make_uid()
         self.uid = uid
+        self.tip_rev = None
 
     def copy(self):
-        return NodeMapDocket(uid=self.uid)
+        new = NodeMapDocket(uid=self.uid)
+        new.tip_rev = self.tip_rev
+        return new
 
     def serialize(self):
         """return serialized bytes for a docket using the passed uid"""
         data = []
         data.append(S_VERSION.pack(ONDISK_VERSION))
-        data.append(S_HEADER.pack(len(self.uid)))
+        headers = (len(self.uid), self.tip_rev)
+        data.append(S_HEADER.pack(*headers))
         data.append(self.uid)
         return b''.join(data)
 
--- a/tests/test-persistent-nodemap.t	Wed Jan 15 15:50:04 2020 +0100
+++ b/tests/test-persistent-nodemap.t	Wed Jan 15 15:50:14 2020 +0100
@@ -14,8 +14,9 @@
   $ hg debugbuilddag .+5000
   $ hg debugnodemap --metadata
   uid: ???????????????? (glob)
+  tip-rev: 5000
   $ f --size .hg/store/00changelog.n
-  .hg/store/00changelog.n: size=18
+  .hg/store/00changelog.n: size=26
   $ f --sha256 .hg/store/00changelog-*.nd
   .hg/store/00changelog-????????????????.nd: sha256=b961925120e1c9bc345c199b2cc442abc477029fdece37ef9d99cbe59c0558b7 (glob)
   $ hg debugnodemap --dump-new | f --sha256 --size
@@ -51,8 +52,9 @@
   $ hg ci -m 'foo'
   $ hg debugnodemap --metadata
   uid: ???????????????? (glob)
+  tip-rev: 5001
   $ f --size .hg/store/00changelog.n
-  .hg/store/00changelog.n: size=18
+  .hg/store/00changelog.n: size=26
 
 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)