comparison mercurial/revlogutils/nodemap.py @ 44515:6c906eaedd0d

nodemap: track the tip_node for validation Differential Revision: https://phab.mercurial-scm.org/D8184
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Thu, 27 Feb 2020 16:32:43 +0100
parents 3265c92f7d13
children 64e2f603de9d
comparison
equal deleted inserted replaced
44514:15a033cabc19 44515:6c906eaedd0d
36 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size]) 36 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
37 if version != ONDISK_VERSION: 37 if version != ONDISK_VERSION:
38 return None 38 return None
39 offset += S_VERSION.size 39 offset += S_VERSION.size
40 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size]) 40 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
41 uid_size, tip_rev, data_length, data_unused = headers 41 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
42 offset += S_HEADER.size 42 offset += S_HEADER.size
43 docket = NodeMapDocket(pdata[offset : offset + uid_size]) 43 docket = NodeMapDocket(pdata[offset : offset + uid_size])
44 offset += uid_size
44 docket.tip_rev = tip_rev 45 docket.tip_rev = tip_rev
46 docket.tip_node = pdata[offset : offset + tip_node_size]
45 docket.data_length = data_length 47 docket.data_length = data_length
46 docket.data_unused = data_unused 48 docket.data_unused = data_unused
47 49
48 filename = _rawdata_filepath(revlog, docket) 50 filename = _rawdata_filepath(revlog, docket)
49 use_mmap = revlog.opener.options.get("exp-persistent-nodemap.mmap") 51 use_mmap = revlog.opener.options.get("exp-persistent-nodemap.mmap")
162 else: 164 else:
163 fd.flush() 165 fd.flush()
164 new_data = util.buffer(util.mmapread(fd, len(data))) 166 new_data = util.buffer(util.mmapread(fd, len(data)))
165 target_docket.data_length = len(data) 167 target_docket.data_length = len(data)
166 target_docket.tip_rev = revlog.tiprev() 168 target_docket.tip_rev = revlog.tiprev()
169 target_docket.tip_node = revlog.node(target_docket.tip_rev)
167 # EXP-TODO: if this is a cache, this should use a cache vfs, not a 170 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
168 # store vfs 171 # store vfs
169 with revlog.opener(revlog.nodemap_file, b'w', atomictemp=True) as fp: 172 with revlog.opener(revlog.nodemap_file, b'w', atomictemp=True) as fp:
170 fp.write(target_docket.serialize()) 173 fp.write(target_docket.serialize())
171 revlog._nodemap_docket = target_docket 174 revlog._nodemap_docket = target_docket
212 # nodemap gains the necessary features to be used in production. 215 # nodemap gains the necessary features to be used in production.
213 216
214 # version 0 is experimental, no BC garantee, do no use outside of tests. 217 # version 0 is experimental, no BC garantee, do no use outside of tests.
215 ONDISK_VERSION = 0 218 ONDISK_VERSION = 0
216 S_VERSION = struct.Struct(">B") 219 S_VERSION = struct.Struct(">B")
217 S_HEADER = struct.Struct(">BQQQ") 220 S_HEADER = struct.Struct(">BQQQQ")
218 221
219 ID_SIZE = 8 222 ID_SIZE = 8
220 223
221 224
222 def _make_uid(): 225 def _make_uid():
240 # - When a new data file is created, a new identifier is generated. 243 # - When a new data file is created, a new identifier is generated.
241 self.uid = uid 244 self.uid = uid
242 # the tipmost revision stored in the data file. This revision and all 245 # the tipmost revision stored in the data file. This revision and all
243 # revision before it are expected to be encoded in the data file. 246 # revision before it are expected to be encoded in the data file.
244 self.tip_rev = None 247 self.tip_rev = None
248 # the node of that tipmost revision, if it mismatch the current index
249 # data the docket is not valid for the current index and should be
250 # discarded.
251 #
252 # note: this method is not perfect as some destructive operation could
253 # preserve the same tip_rev + tip_node while altering lower revision.
254 # However this multiple other caches have the same vulnerability (eg:
255 # brancmap cache).
256 self.tip_node = None
245 # the size (in bytes) of the persisted data to encode the nodemap valid 257 # the size (in bytes) of the persisted data to encode the nodemap valid
246 # for `tip_rev`. 258 # for `tip_rev`.
247 # - data file shorter than this are corrupted, 259 # - data file shorter than this are corrupted,
248 # - any extra data should be ignored. 260 # - any extra data should be ignored.
249 self.data_length = None 261 self.data_length = None
252 self.data_unused = 0 264 self.data_unused = 0
253 265
254 def copy(self): 266 def copy(self):
255 new = NodeMapDocket(uid=self.uid) 267 new = NodeMapDocket(uid=self.uid)
256 new.tip_rev = self.tip_rev 268 new.tip_rev = self.tip_rev
269 new.tip_node = self.tip_node
257 new.data_length = self.data_length 270 new.data_length = self.data_length
258 new.data_unused = self.data_unused 271 new.data_unused = self.data_unused
259 return new 272 return new
260 273
261 def __cmp__(self, other): 274 def __cmp__(self, other):
279 headers = ( 292 headers = (
280 len(self.uid), 293 len(self.uid),
281 self.tip_rev, 294 self.tip_rev,
282 self.data_length, 295 self.data_length,
283 self.data_unused, 296 self.data_unused,
297 len(self.tip_node),
284 ) 298 )
285 data.append(S_HEADER.pack(*headers)) 299 data.append(S_HEADER.pack(*headers))
286 data.append(self.uid) 300 data.append(self.uid)
301 data.append(self.tip_node)
287 return b''.join(data) 302 return b''.join(data)
288 303
289 304
290 def _rawdata_filepath(revlog, docket): 305 def _rawdata_filepath(revlog, docket):
291 """The (vfs relative) nodemap's rawdata file for a given uid""" 306 """The (vfs relative) nodemap's rawdata file for a given uid"""