##// END OF EJS Templates
cli: fix spelling in `debugnodemap` error messages
Arseniy Alekseyev -
r51403:4fe46f96 default
parent child Browse files
Show More
@@ -1,668 +1,668 b''
1 1 # nodemap.py - nodemap related code and utilities
2 2 #
3 3 # Copyright 2019 Pierre-Yves David <pierre-yves.david@octobus.net>
4 4 # Copyright 2019 George Racinet <georges.racinet@octobus.net>
5 5 #
6 6 # This software may be used and distributed according to the terms of the
7 7 # GNU General Public License version 2 or any later version.
8 8
9 9
10 10 import re
11 11 import struct
12 12
13 13 from ..node import hex
14 14
15 15 from .. import (
16 16 error,
17 17 requirements,
18 18 util,
19 19 )
20 20 from . import docket as docket_mod
21 21
22 22
23 23 class NodeMap(dict):
24 24 def __missing__(self, x):
25 25 raise error.RevlogError(b'unknown node: %s' % x)
26 26
27 27
28 28 def test_race_hook_1():
29 29 """hook point for test
30 30
31 31 This let tests to have things happens between the docket reading and the
32 32 data reading"""
33 33 pass
34 34
35 35
36 36 def post_stream_cleanup(repo):
37 37 """The stream clone might needs to remove some file if persisten nodemap
38 38 was dropped while stream cloning
39 39 """
40 40 if requirements.REVLOGV1_REQUIREMENT not in repo.requirements:
41 41 return
42 42 if requirements.NODEMAP_REQUIREMENT in repo.requirements:
43 43 return
44 44 unfi = repo.unfiltered()
45 45 delete_nodemap(None, unfi, unfi.changelog)
46 46 delete_nodemap(None, repo, unfi.manifestlog._rootstore._revlog)
47 47
48 48
49 49 def persisted_data(revlog):
50 50 """read the nodemap for a revlog from disk"""
51 51 if revlog._nodemap_file is None:
52 52 return None
53 53 pdata = revlog.opener.tryread(revlog._nodemap_file)
54 54 if not pdata:
55 55 return None
56 56 offset = 0
57 57 (version,) = S_VERSION.unpack(pdata[offset : offset + S_VERSION.size])
58 58 if version != ONDISK_VERSION:
59 59 return None
60 60 offset += S_VERSION.size
61 61 headers = S_HEADER.unpack(pdata[offset : offset + S_HEADER.size])
62 62 uid_size, tip_rev, data_length, data_unused, tip_node_size = headers
63 63 offset += S_HEADER.size
64 64 docket = NodeMapDocket(pdata[offset : offset + uid_size])
65 65 offset += uid_size
66 66 docket.tip_rev = tip_rev
67 67 docket.tip_node = pdata[offset : offset + tip_node_size]
68 68 docket.data_length = data_length
69 69 docket.data_unused = data_unused
70 70
71 71 filename = _rawdata_filepath(revlog, docket)
72 72 use_mmap = revlog.opener.options.get(b"persistent-nodemap.mmap")
73 73
74 74 test_race_hook_1()
75 75 try:
76 76 with revlog.opener(filename) as fd:
77 77 if use_mmap:
78 78 try:
79 79 data = util.buffer(util.mmapread(fd, data_length))
80 80 except ValueError:
81 81 # raised when the read file is too small
82 82 data = b''
83 83 else:
84 84 data = fd.read(data_length)
85 85 except FileNotFoundError:
86 86 return None
87 87 if len(data) < data_length:
88 88 return None
89 89 return docket, data
90 90
91 91
92 92 def setup_persistent_nodemap(tr, revlog):
93 93 """Install whatever is needed transaction side to persist a nodemap on disk
94 94
95 95 (only actually persist the nodemap if this is relevant for this revlog)
96 96 """
97 97 if revlog._inline:
98 98 return # inlined revlog are too small for this to be relevant
99 99 if revlog._nodemap_file is None:
100 100 return # we do not use persistent_nodemap on this revlog
101 101
102 102 # we need to happen after the changelog finalization, in that use "cl-"
103 103 callback_id = b"nm-revlog-persistent-nodemap-%s" % revlog._nodemap_file
104 104 if tr.hasfinalize(callback_id):
105 105 return # no need to register again
106 106 tr.addpending(
107 107 callback_id, lambda tr: persist_nodemap(tr, revlog, pending=True)
108 108 )
109 109 tr.addfinalize(callback_id, lambda tr: persist_nodemap(tr, revlog))
110 110
111 111
112 112 class _NoTransaction:
113 113 """transaction like object to update the nodemap outside a transaction"""
114 114
115 115 def __init__(self):
116 116 self._postclose = {}
117 117
118 118 def addpostclose(self, callback_id, callback_func):
119 119 self._postclose[callback_id] = callback_func
120 120
121 121 def registertmp(self, *args, **kwargs):
122 122 pass
123 123
124 124 def addbackup(self, *args, **kwargs):
125 125 pass
126 126
127 127 def add(self, *args, **kwargs):
128 128 pass
129 129
130 130 def addabort(self, *args, **kwargs):
131 131 pass
132 132
133 133 def _report(self, *args):
134 134 pass
135 135
136 136
137 137 def update_persistent_nodemap(revlog):
138 138 """update the persistent nodemap right now
139 139
140 140 To be used for updating the nodemap on disk outside of a normal transaction
141 141 setup (eg, `debugupdatecache`).
142 142 """
143 143 if revlog._inline:
144 144 return # inlined revlog are too small for this to be relevant
145 145 if revlog._nodemap_file is None:
146 146 return # we do not use persistent_nodemap on this revlog
147 147
148 148 notr = _NoTransaction()
149 149 persist_nodemap(notr, revlog)
150 150 for k in sorted(notr._postclose):
151 151 notr._postclose[k](None)
152 152
153 153
154 154 def delete_nodemap(tr, repo, revlog):
155 155 """Delete nodemap data on disk for a given revlog"""
156 156 prefix = revlog.radix
157 157 pattern = re.compile(br"(^|/)%s(-[0-9a-f]+\.nd|\.n(\.a)?)$" % prefix)
158 158 dirpath = revlog.opener.dirname(revlog._indexfile)
159 159 for f in revlog.opener.listdir(dirpath):
160 160 if pattern.match(f):
161 161 repo.svfs.tryunlink(f)
162 162
163 163
164 164 def persist_nodemap(tr, revlog, pending=False, force=False):
165 165 """Write nodemap data on disk for a given revlog"""
166 166 if getattr(revlog, 'filteredrevs', ()):
167 167 raise error.ProgrammingError(
168 168 "cannot persist nodemap of a filtered changelog"
169 169 )
170 170 if revlog._nodemap_file is None:
171 171 if force:
172 172 revlog._nodemap_file = get_nodemap_file(revlog)
173 173 else:
174 174 msg = "calling persist nodemap on a revlog without the feature enabled"
175 175 raise error.ProgrammingError(msg)
176 176
177 177 can_incremental = util.safehasattr(revlog.index, "nodemap_data_incremental")
178 178 ondisk_docket = revlog._nodemap_docket
179 179 feed_data = util.safehasattr(revlog.index, "update_nodemap_data")
180 180 use_mmap = revlog.opener.options.get(b"persistent-nodemap.mmap")
181 181
182 182 data = None
183 183 # first attemp an incremental update of the data
184 184 if can_incremental and ondisk_docket is not None:
185 185 target_docket = revlog._nodemap_docket.copy()
186 186 (
187 187 src_docket,
188 188 data_changed_count,
189 189 data,
190 190 ) = revlog.index.nodemap_data_incremental()
191 191 new_length = target_docket.data_length + len(data)
192 192 new_unused = target_docket.data_unused + data_changed_count
193 193 if src_docket != target_docket:
194 194 data = None
195 195 elif new_length <= (new_unused * 10): # under 10% of unused data
196 196 data = None
197 197 else:
198 198 datafile = _rawdata_filepath(revlog, target_docket)
199 199 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
200 200 # store vfs
201 201 tr.add(datafile, target_docket.data_length)
202 202 with revlog.opener(datafile, b'r+') as fd:
203 203 fd.seek(target_docket.data_length)
204 204 fd.write(data)
205 205 if feed_data:
206 206 if use_mmap:
207 207 fd.seek(0)
208 208 new_data = fd.read(new_length)
209 209 else:
210 210 fd.flush()
211 211 new_data = util.buffer(util.mmapread(fd, new_length))
212 212 target_docket.data_length = new_length
213 213 target_docket.data_unused = new_unused
214 214
215 215 if data is None:
216 216 # otherwise fallback to a full new export
217 217 target_docket = NodeMapDocket()
218 218 datafile = _rawdata_filepath(revlog, target_docket)
219 219 if util.safehasattr(revlog.index, "nodemap_data_all"):
220 220 data = revlog.index.nodemap_data_all()
221 221 else:
222 222 data = persistent_data(revlog.index)
223 223 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
224 224 # store vfs
225 225
226 226 tryunlink = revlog.opener.tryunlink
227 227
228 228 def abortck(tr):
229 229 tryunlink(datafile)
230 230
231 231 callback_id = b"delete-%s" % datafile
232 232
233 233 # some flavor of the transaction abort does not cleanup new file, it
234 234 # simply empty them.
235 235 tr.addabort(callback_id, abortck)
236 236 with revlog.opener(datafile, b'w+') as fd:
237 237 fd.write(data)
238 238 if feed_data:
239 239 if use_mmap:
240 240 new_data = data
241 241 else:
242 242 fd.flush()
243 243 new_data = util.buffer(util.mmapread(fd, len(data)))
244 244 target_docket.data_length = len(data)
245 245 target_docket.tip_rev = revlog.tiprev()
246 246 target_docket.tip_node = revlog.node(target_docket.tip_rev)
247 247 # EXP-TODO: if this is a cache, this should use a cache vfs, not a
248 248 # store vfs
249 249 file_path = revlog._nodemap_file
250 250 if pending:
251 251 file_path += b'.a'
252 252 tr.registertmp(file_path)
253 253 else:
254 254 tr.addbackup(file_path)
255 255
256 256 with revlog.opener(file_path, b'w', atomictemp=True) as fp:
257 257 fp.write(target_docket.serialize())
258 258 revlog._nodemap_docket = target_docket
259 259 if feed_data:
260 260 revlog.index.update_nodemap_data(target_docket, new_data)
261 261
262 262 # search for old index file in all cases, some older process might have
263 263 # left one behind.
264 264 olds = _other_rawdata_filepath(revlog, target_docket)
265 265 if olds:
266 266 realvfs = getattr(revlog, '_realopener', revlog.opener)
267 267
268 268 def cleanup(tr):
269 269 for oldfile in olds:
270 270 realvfs.tryunlink(oldfile)
271 271
272 272 callback_id = b"revlog-cleanup-nodemap-%s" % revlog._nodemap_file
273 273 tr.addpostclose(callback_id, cleanup)
274 274
275 275
276 276 ### Nodemap docket file
277 277 #
278 278 # The nodemap data are stored on disk using 2 files:
279 279 #
280 280 # * a raw data files containing a persistent nodemap
281 281 # (see `Nodemap Trie` section)
282 282 #
283 283 # * a small "docket" file containing medatadata
284 284 #
285 285 # While the nodemap data can be multiple tens of megabytes, the "docket" is
286 286 # small, it is easy to update it automatically or to duplicated its content
287 287 # during a transaction.
288 288 #
289 289 # Multiple raw data can exist at the same time (The currently valid one and a
290 290 # new one beind used by an in progress transaction). To accomodate this, the
291 291 # filename hosting the raw data has a variable parts. The exact filename is
292 292 # specified inside the "docket" file.
293 293 #
294 294 # The docket file contains information to find, qualify and validate the raw
295 295 # data. Its content is currently very light, but it will expand as the on disk
296 296 # nodemap gains the necessary features to be used in production.
297 297
298 298 ONDISK_VERSION = 1
299 299 S_VERSION = struct.Struct(">B")
300 300 S_HEADER = struct.Struct(">BQQQQ")
301 301
302 302
303 303 class NodeMapDocket:
304 304 """metadata associated with persistent nodemap data
305 305
306 306 The persistent data may come from disk or be on their way to disk.
307 307 """
308 308
309 309 def __init__(self, uid=None):
310 310 if uid is None:
311 311 uid = docket_mod.make_uid()
312 312 # a unique identifier for the data file:
313 313 # - When new data are appended, it is preserved.
314 314 # - When a new data file is created, a new identifier is generated.
315 315 self.uid = uid
316 316 # the tipmost revision stored in the data file. This revision and all
317 317 # revision before it are expected to be encoded in the data file.
318 318 self.tip_rev = None
319 319 # the node of that tipmost revision, if it mismatch the current index
320 320 # data the docket is not valid for the current index and should be
321 321 # discarded.
322 322 #
323 323 # note: this method is not perfect as some destructive operation could
324 324 # preserve the same tip_rev + tip_node while altering lower revision.
325 325 # However this multiple other caches have the same vulnerability (eg:
326 326 # brancmap cache).
327 327 self.tip_node = None
328 328 # the size (in bytes) of the persisted data to encode the nodemap valid
329 329 # for `tip_rev`.
330 330 # - data file shorter than this are corrupted,
331 331 # - any extra data should be ignored.
332 332 self.data_length = None
333 333 # the amount (in bytes) of "dead" data, still in the data file but no
334 334 # longer used for the nodemap.
335 335 self.data_unused = 0
336 336
337 337 def copy(self):
338 338 new = NodeMapDocket(uid=self.uid)
339 339 new.tip_rev = self.tip_rev
340 340 new.tip_node = self.tip_node
341 341 new.data_length = self.data_length
342 342 new.data_unused = self.data_unused
343 343 return new
344 344
345 345 def __cmp__(self, other):
346 346 if self.uid < other.uid:
347 347 return -1
348 348 if self.uid > other.uid:
349 349 return 1
350 350 elif self.data_length < other.data_length:
351 351 return -1
352 352 elif self.data_length > other.data_length:
353 353 return 1
354 354 return 0
355 355
356 356 def __eq__(self, other):
357 357 return self.uid == other.uid and self.data_length == other.data_length
358 358
359 359 def serialize(self):
360 360 """return serialized bytes for a docket using the passed uid"""
361 361 data = []
362 362 data.append(S_VERSION.pack(ONDISK_VERSION))
363 363 headers = (
364 364 len(self.uid),
365 365 self.tip_rev,
366 366 self.data_length,
367 367 self.data_unused,
368 368 len(self.tip_node),
369 369 )
370 370 data.append(S_HEADER.pack(*headers))
371 371 data.append(self.uid)
372 372 data.append(self.tip_node)
373 373 return b''.join(data)
374 374
375 375
376 376 def _rawdata_filepath(revlog, docket):
377 377 """The (vfs relative) nodemap's rawdata file for a given uid"""
378 378 prefix = revlog.radix
379 379 return b"%s-%s.nd" % (prefix, docket.uid)
380 380
381 381
382 382 def _other_rawdata_filepath(revlog, docket):
383 383 prefix = revlog.radix
384 384 pattern = re.compile(br"(^|/)%s-[0-9a-f]+\.nd$" % prefix)
385 385 new_file_path = _rawdata_filepath(revlog, docket)
386 386 new_file_name = revlog.opener.basename(new_file_path)
387 387 dirpath = revlog.opener.dirname(new_file_path)
388 388 others = []
389 389 for f in revlog.opener.listdir(dirpath):
390 390 if pattern.match(f) and f != new_file_name:
391 391 others.append(f)
392 392 return others
393 393
394 394
395 395 ### Nodemap Trie
396 396 #
397 397 # This is a simple reference implementation to compute and persist a nodemap
398 398 # trie. This reference implementation is write only. The python version of this
399 399 # is not expected to be actually used, since it wont provide performance
400 400 # improvement over existing non-persistent C implementation.
401 401 #
402 402 # The nodemap is persisted as Trie using 4bits-address/16-entries block. each
403 403 # revision can be adressed using its node shortest prefix.
404 404 #
405 405 # The trie is stored as a sequence of block. Each block contains 16 entries
406 406 # (signed 64bit integer, big endian). Each entry can be one of the following:
407 407 #
408 408 # * value >= 0 -> index of sub-block
409 409 # * value == -1 -> no value
410 410 # * value < -1 -> encoded revision: rev = -(value+2)
411 411 #
412 412 # See REV_OFFSET and _transform_rev below.
413 413 #
414 414 # The implementation focus on simplicity, not on performance. A Rust
415 415 # implementation should provide a efficient version of the same binary
416 416 # persistence. This reference python implementation is never meant to be
417 417 # extensively use in production.
418 418
419 419
420 420 def persistent_data(index):
421 421 """return the persistent binary form for a nodemap for a given index"""
422 422 trie = _build_trie(index)
423 423 return _persist_trie(trie)
424 424
425 425
426 426 def update_persistent_data(index, root, max_idx, last_rev):
427 427 """return the incremental update for persistent nodemap from a given index"""
428 428 changed_block, trie = _update_trie(index, root, last_rev)
429 429 return (
430 430 changed_block * S_BLOCK.size,
431 431 _persist_trie(trie, existing_idx=max_idx),
432 432 )
433 433
434 434
435 435 S_BLOCK = struct.Struct(">" + ("l" * 16))
436 436
437 437 NO_ENTRY = -1
438 438 # rev 0 need to be -2 because 0 is used by block, -1 is a special value.
439 439 REV_OFFSET = 2
440 440
441 441
442 442 def _transform_rev(rev):
443 443 """Return the number used to represent the rev in the tree.
444 444
445 445 (or retrieve a rev number from such representation)
446 446
447 447 Note that this is an involution, a function equal to its inverse (i.e.
448 448 which gives the identity when applied to itself).
449 449 """
450 450 return -(rev + REV_OFFSET)
451 451
452 452
453 453 def _to_int(hex_digit):
454 454 """turn an hexadecimal digit into a proper integer"""
455 455 return int(hex_digit, 16)
456 456
457 457
458 458 class Block(dict):
459 459 """represent a block of the Trie
460 460
461 461 contains up to 16 entry indexed from 0 to 15"""
462 462
463 463 def __init__(self):
464 464 super(Block, self).__init__()
465 465 # If this block exist on disk, here is its ID
466 466 self.ondisk_id = None
467 467
468 468 def __iter__(self):
469 469 return iter(self.get(i) for i in range(16))
470 470
471 471
472 472 def _build_trie(index):
473 473 """build a nodemap trie
474 474
475 475 The nodemap stores revision number for each unique prefix.
476 476
477 477 Each block is a dictionary with keys in `[0, 15]`. Values are either
478 478 another block or a revision number.
479 479 """
480 480 root = Block()
481 481 for rev in range(len(index)):
482 482 current_hex = hex(index[rev][7])
483 483 _insert_into_block(index, 0, root, rev, current_hex)
484 484 return root
485 485
486 486
487 487 def _update_trie(index, root, last_rev):
488 488 """consume"""
489 489 changed = 0
490 490 for rev in range(last_rev + 1, len(index)):
491 491 current_hex = hex(index[rev][7])
492 492 changed += _insert_into_block(index, 0, root, rev, current_hex)
493 493 return changed, root
494 494
495 495
496 496 def _insert_into_block(index, level, block, current_rev, current_hex):
497 497 """insert a new revision in a block
498 498
499 499 index: the index we are adding revision for
500 500 level: the depth of the current block in the trie
501 501 block: the block currently being considered
502 502 current_rev: the revision number we are adding
503 503 current_hex: the hexadecimal representation of the of that revision
504 504 """
505 505 changed = 1
506 506 if block.ondisk_id is not None:
507 507 block.ondisk_id = None
508 508 hex_digit = _to_int(current_hex[level : level + 1])
509 509 entry = block.get(hex_digit)
510 510 if entry is None:
511 511 # no entry, simply store the revision number
512 512 block[hex_digit] = current_rev
513 513 elif isinstance(entry, dict):
514 514 # need to recurse to an underlying block
515 515 changed += _insert_into_block(
516 516 index, level + 1, entry, current_rev, current_hex
517 517 )
518 518 else:
519 519 # collision with a previously unique prefix, inserting new
520 520 # vertices to fit both entry.
521 521 other_hex = hex(index[entry][7])
522 522 other_rev = entry
523 523 new = Block()
524 524 block[hex_digit] = new
525 525 _insert_into_block(index, level + 1, new, other_rev, other_hex)
526 526 _insert_into_block(index, level + 1, new, current_rev, current_hex)
527 527 return changed
528 528
529 529
530 530 def _persist_trie(root, existing_idx=None):
531 531 """turn a nodemap trie into persistent binary data
532 532
533 533 See `_build_trie` for nodemap trie structure"""
534 534 block_map = {}
535 535 if existing_idx is not None:
536 536 base_idx = existing_idx + 1
537 537 else:
538 538 base_idx = 0
539 539 chunks = []
540 540 for tn in _walk_trie(root):
541 541 if tn.ondisk_id is not None:
542 542 block_map[id(tn)] = tn.ondisk_id
543 543 else:
544 544 block_map[id(tn)] = len(chunks) + base_idx
545 545 chunks.append(_persist_block(tn, block_map))
546 546 return b''.join(chunks)
547 547
548 548
549 549 def _walk_trie(block):
550 550 """yield all the block in a trie
551 551
552 552 Children blocks are always yield before their parent block.
553 553 """
554 554 for (__, item) in sorted(block.items()):
555 555 if isinstance(item, dict):
556 556 for sub_block in _walk_trie(item):
557 557 yield sub_block
558 558 yield block
559 559
560 560
561 561 def _persist_block(block_node, block_map):
562 562 """produce persistent binary data for a single block
563 563
564 564 Children block are assumed to be already persisted and present in
565 565 block_map.
566 566 """
567 567 data = tuple(_to_value(v, block_map) for v in block_node)
568 568 return S_BLOCK.pack(*data)
569 569
570 570
571 571 def _to_value(item, block_map):
572 572 """persist any value as an integer"""
573 573 if item is None:
574 574 return NO_ENTRY
575 575 elif isinstance(item, dict):
576 576 return block_map[id(item)]
577 577 else:
578 578 return _transform_rev(item)
579 579
580 580
581 581 def parse_data(data):
582 582 """parse parse nodemap data into a nodemap Trie"""
583 583 if (len(data) % S_BLOCK.size) != 0:
584 584 msg = b"nodemap data size is not a multiple of block size (%d): %d"
585 585 raise error.Abort(msg % (S_BLOCK.size, len(data)))
586 586 if not data:
587 587 return Block(), None
588 588 block_map = {}
589 589 new_blocks = []
590 590 for i in range(0, len(data), S_BLOCK.size):
591 591 block = Block()
592 592 block.ondisk_id = len(block_map)
593 593 block_map[block.ondisk_id] = block
594 594 block_data = data[i : i + S_BLOCK.size]
595 595 values = S_BLOCK.unpack(block_data)
596 596 new_blocks.append((block, values))
597 597 for b, values in new_blocks:
598 598 for idx, v in enumerate(values):
599 599 if v == NO_ENTRY:
600 600 continue
601 601 elif v >= 0:
602 602 b[idx] = block_map[v]
603 603 else:
604 604 b[idx] = _transform_rev(v)
605 605 return block, i // S_BLOCK.size
606 606
607 607
608 608 # debug utility
609 609
610 610
611 611 def check_data(ui, index, data):
612 612 """verify that the provided nodemap data are valid for the given idex"""
613 613 ret = 0
614 ui.status((b"revision in index: %d\n") % len(index))
614 ui.status((b"revisions in index: %d\n") % len(index))
615 615 root, __ = parse_data(data)
616 616 all_revs = set(_all_revisions(root))
617 ui.status((b"revision in nodemap: %d\n") % len(all_revs))
617 ui.status((b"revisions in nodemap: %d\n") % len(all_revs))
618 618 for r in range(len(index)):
619 619 if r not in all_revs:
620 620 msg = b" revision missing from nodemap: %d\n" % r
621 621 ui.write_err(msg)
622 622 ret = 1
623 623 else:
624 624 all_revs.remove(r)
625 625 nm_rev = _find_node(root, hex(index[r][7]))
626 626 if nm_rev is None:
627 627 msg = b" revision node does not match any entries: %d\n" % r
628 628 ui.write_err(msg)
629 629 ret = 1
630 630 elif nm_rev != r:
631 631 msg = (
632 632 b" revision node does not match the expected revision: "
633 633 b"%d != %d\n" % (r, nm_rev)
634 634 )
635 635 ui.write_err(msg)
636 636 ret = 1
637 637
638 638 if all_revs:
639 639 for r in sorted(all_revs):
640 msg = b" extra revision in nodemap: %d\n" % r
640 msg = b" extra revisions in nodemap: %d\n" % r
641 641 ui.write_err(msg)
642 642 ret = 1
643 643 return ret
644 644
645 645
646 646 def _all_revisions(root):
647 647 """return all revisions stored in a Trie"""
648 648 for block in _walk_trie(root):
649 649 for v in block:
650 650 if v is None or isinstance(v, Block):
651 651 continue
652 652 yield v
653 653
654 654
655 655 def _find_node(block, node):
656 656 """find the revision associated with a given node"""
657 657 entry = block.get(_to_int(node[0:1]))
658 658 if isinstance(entry, dict):
659 659 return _find_node(entry, node[1:])
660 660 return entry
661 661
662 662
663 663 def get_nodemap_file(revlog):
664 664 if revlog._trypending:
665 665 pending_path = revlog.radix + b".n.a"
666 666 if revlog.opener.exists(pending_path):
667 667 return pending_path
668 668 return revlog.radix + b".n"
@@ -1,1258 +1,1258 b''
1 1 ===================================
2 2 Test the persistent on-disk nodemap
3 3 ===================================
4 4
5 5
6 6 $ cat << EOF >> $HGRCPATH
7 7 > [format]
8 8 > use-share-safe=yes
9 9 > [extensions]
10 10 > share=
11 11 > EOF
12 12
13 13 #if no-rust
14 14
15 15 $ cat << EOF >> $HGRCPATH
16 16 > [format]
17 17 > use-persistent-nodemap=yes
18 18 > [devel]
19 19 > persistent-nodemap=yes
20 20 > EOF
21 21
22 22 #endif
23 23
24 24 $ hg init test-repo --config storage.revlog.persistent-nodemap.slow-path=allow
25 25 $ cd test-repo
26 26
27 27 Check handling of the default slow-path value
28 28
29 29 #if no-pure no-rust
30 30
31 31 $ hg id
32 32 abort: accessing `persistent-nodemap` repository without associated fast implementation.
33 33 (check `hg help config.format.use-persistent-nodemap` for details)
34 34 [255]
35 35
36 36 Unlock further check (we are here to test the feature)
37 37
38 38 $ cat << EOF >> $HGRCPATH
39 39 > [storage]
40 40 > # to avoid spamming the test
41 41 > revlog.persistent-nodemap.slow-path=allow
42 42 > EOF
43 43
44 44 #endif
45 45
46 46 #if rust
47 47
48 48 Regression test for a previous bug in Rust/C FFI for the `Revlog_CAPI` capsule:
49 49 in places where `mercurial/cext/revlog.c` function signatures use `Py_ssize_t`
50 50 (64 bits on Linux x86_64), corresponding declarations in `rust/hg-cpython/src/cindex.rs`
51 51 incorrectly used `libc::c_int` (32 bits).
52 52 As a result, -1 passed from Rust for the null revision became 4294967295 in C.
53 53
54 54 $ hg log -r 00000000
55 55 changeset: -1:000000000000
56 56 tag: tip
57 57 user:
58 58 date: Thu Jan 01 00:00:00 1970 +0000
59 59
60 60
61 61 #endif
62 62
63 63
64 64 $ hg debugformat
65 65 format-variant repo
66 66 fncache: yes
67 67 dirstate-v2: no
68 68 tracked-hint: no
69 69 dotencode: yes
70 70 generaldelta: yes
71 71 share-safe: yes
72 72 sparserevlog: yes
73 73 persistent-nodemap: yes
74 74 copies-sdc: no
75 75 revlog-v2: no
76 76 changelog-v2: no
77 77 plain-cl-delta: yes
78 78 compression: zlib (no-zstd !)
79 79 compression: zstd (zstd !)
80 80 compression-level: default
81 81 $ hg debugbuilddag .+5000 --new-file
82 82
83 83 $ hg debugnodemap --metadata
84 84 uid: ???????? (glob)
85 85 tip-rev: 5000
86 86 tip-node: 6b02b8c7b96654c25e86ba69eda198d7e6ad8b3c
87 87 data-length: 121088
88 88 data-unused: 0
89 89 data-unused: 0.000%
90 90 $ f --size .hg/store/00changelog.n
91 91 .hg/store/00changelog.n: size=62
92 92
93 93 Simple lookup works
94 94
95 95 $ ANYNODE=`hg log --template '{node|short}\n' --rev tip`
96 96 $ hg log -r "$ANYNODE" --template '{rev}\n'
97 97 5000
98 98
99 99
100 100 #if rust
101 101
102 102 $ f --sha256 .hg/store/00changelog-*.nd
103 103 .hg/store/00changelog-????????.nd: sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd (glob)
104 104
105 105 $ f --sha256 .hg/store/00manifest-*.nd
106 106 .hg/store/00manifest-????????.nd: sha256=97117b1c064ea2f86664a124589e47db0e254e8d34739b5c5cc5bf31c9da2b51 (glob)
107 107 $ hg debugnodemap --dump-new | f --sha256 --size
108 108 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
109 109 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
110 110 size=121088, sha256=2e029d3200bd1a986b32784fc2ef1a3bd60dc331f025718bcf5ff44d93f026fd
111 111 0000: 00 00 00 91 00 00 00 20 00 00 00 bb 00 00 00 e7 |....... ........|
112 112 0010: 00 00 00 66 00 00 00 a1 00 00 01 13 00 00 01 22 |...f..........."|
113 113 0020: 00 00 00 23 00 00 00 fc 00 00 00 ba 00 00 00 5e |...#...........^|
114 114 0030: 00 00 00 df 00 00 01 4e 00 00 01 65 00 00 00 ab |.......N...e....|
115 115 0040: 00 00 00 a9 00 00 00 95 00 00 00 73 00 00 00 38 |...........s...8|
116 116 0050: 00 00 00 cc 00 00 00 92 00 00 00 90 00 00 00 69 |...............i|
117 117 0060: 00 00 00 ec 00 00 00 8d 00 00 01 4f 00 00 00 12 |...........O....|
118 118 0070: 00 00 02 0c 00 00 00 77 00 00 00 9c 00 00 00 8f |.......w........|
119 119 0080: 00 00 00 d5 00 00 00 6b 00 00 00 48 00 00 00 b3 |.......k...H....|
120 120 0090: 00 00 00 e5 00 00 00 b5 00 00 00 8e 00 00 00 ad |................|
121 121 00a0: 00 00 00 7b 00 00 00 7c 00 00 00 0b 00 00 00 2b |...{...|.......+|
122 122 00b0: 00 00 00 c6 00 00 00 1e 00 00 01 08 00 00 00 11 |................|
123 123 00c0: 00 00 01 30 00 00 00 26 00 00 01 9c 00 00 00 35 |...0...&.......5|
124 124 00d0: 00 00 00 b8 00 00 01 31 00 00 00 2c 00 00 00 55 |.......1...,...U|
125 125 00e0: 00 00 00 8a 00 00 00 9a 00 00 00 0c 00 00 01 1e |................|
126 126 00f0: 00 00 00 a4 00 00 00 83 00 00 00 c9 00 00 00 8c |................|
127 127
128 128
129 129 #else
130 130
131 131 $ f --sha256 .hg/store/00changelog-*.nd
132 132 .hg/store/00changelog-????????.nd: sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79 (glob)
133 133 $ hg debugnodemap --dump-new | f --sha256 --size
134 134 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
135 135 $ hg debugnodemap --dump-disk | f --sha256 --bytes=256 --hexdump --size
136 136 size=121088, sha256=f544f5462ff46097432caf6d764091f6d8c46d6121be315ead8576d548c9dd79
137 137 0000: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
138 138 0010: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
139 139 0020: ff ff ff ff ff ff f5 06 ff ff ff ff ff ff f3 e7 |................|
140 140 0030: ff ff ef ca ff ff ff ff ff ff ff ff ff ff ff ff |................|
141 141 0040: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
142 142 0050: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ed 08 |................|
143 143 0060: ff ff ed 66 ff ff ff ff ff ff ff ff ff ff ff ff |...f............|
144 144 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
145 145 0080: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
146 146 0090: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f6 ed |................|
147 147 00a0: ff ff ff ff ff ff fe 61 ff ff ff ff ff ff ff ff |.......a........|
148 148 00b0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
149 149 00c0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
150 150 00d0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff |................|
151 151 00e0: ff ff ff ff ff ff ff ff ff ff ff ff ff ff f1 02 |................|
152 152 00f0: ff ff ff ff ff ff ed 1b ff ff ff ff ff ff ff ff |................|
153 153
154 154 #endif
155 155
156 156 $ hg debugnodemap --check
157 revision in index: 5001
158 revision in nodemap: 5001
157 revisions in index: 5001
158 revisions in nodemap: 5001
159 159
160 160 add a new commit
161 161
162 162 $ hg up
163 163 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
164 164 $ echo foo > foo
165 165 $ hg add foo
166 166
167 167
168 168 Check slow-path config value handling
169 169 -------------------------------------
170 170
171 171 #if no-pure no-rust
172 172
173 173 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
174 174 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
175 175 falling back to default value: abort
176 176 abort: accessing `persistent-nodemap` repository without associated fast implementation.
177 177 (check `hg help config.format.use-persistent-nodemap` for details)
178 178 [255]
179 179
180 180 $ hg log -r . --config "storage.revlog.persistent-nodemap.slow-path=warn"
181 181 warning: accessing `persistent-nodemap` repository without associated fast implementation.
182 182 (check `hg help config.format.use-persistent-nodemap` for details)
183 183 changeset: 5000:6b02b8c7b966
184 184 tag: tip
185 185 user: debugbuilddag
186 186 date: Thu Jan 01 01:23:20 1970 +0000
187 187 summary: r5000
188 188
189 189 $ hg ci -m 'foo' --config "storage.revlog.persistent-nodemap.slow-path=abort"
190 190 abort: accessing `persistent-nodemap` repository without associated fast implementation.
191 191 (check `hg help config.format.use-persistent-nodemap` for details)
192 192 [255]
193 193
194 194 #else
195 195
196 196 $ hg id --config "storage.revlog.persistent-nodemap.slow-path=invalid-value"
197 197 unknown value for config "storage.revlog.persistent-nodemap.slow-path": "invalid-value"
198 198 falling back to default value: abort
199 199 6b02b8c7b966+ tip
200 200
201 201 #endif
202 202
203 203 $ hg ci -m 'foo'
204 204
205 205 #if no-pure no-rust
206 206 $ hg debugnodemap --metadata
207 207 uid: ???????? (glob)
208 208 tip-rev: 5001
209 209 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
210 210 data-length: 121088
211 211 data-unused: 0
212 212 data-unused: 0.000%
213 213 #else
214 214 $ hg debugnodemap --metadata
215 215 uid: ???????? (glob)
216 216 tip-rev: 5001
217 217 tip-node: 16395c3cf7e231394735e6b1717823ada303fb0c
218 218 data-length: 121344
219 219 data-unused: 256
220 220 data-unused: 0.211%
221 221 #endif
222 222
223 223 $ f --size .hg/store/00changelog.n
224 224 .hg/store/00changelog.n: size=62
225 225
226 226 (The pure code use the debug code that perform incremental update, the C code reencode from scratch)
227 227
228 228 #if pure
229 229 $ f --sha256 .hg/store/00changelog-*.nd --size
230 230 .hg/store/00changelog-????????.nd: size=121344, sha256=cce54c5da5bde3ad72a4938673ed4064c86231b9c64376b082b163fdb20f8f66 (glob)
231 231 #endif
232 232
233 233 #if rust
234 234 $ f --sha256 .hg/store/00changelog-*.nd --size
235 235 .hg/store/00changelog-????????.nd: size=121344, sha256=952b042fcf614ceb37b542b1b723e04f18f83efe99bee4e0f5ccd232ef470e58 (glob)
236 236 #endif
237 237
238 238 #if no-pure no-rust
239 239 $ f --sha256 .hg/store/00changelog-*.nd --size
240 240 .hg/store/00changelog-????????.nd: size=121088, sha256=df7c06a035b96cb28c7287d349d603baef43240be7736fe34eea419a49702e17 (glob)
241 241 #endif
242 242
243 243 $ hg debugnodemap --check
244 revision in index: 5002
245 revision in nodemap: 5002
244 revisions in index: 5002
245 revisions in nodemap: 5002
246 246
247 247 Test code path without mmap
248 248 ---------------------------
249 249
250 250 $ echo bar > bar
251 251 $ hg add bar
252 252 $ hg ci -m 'bar' --config storage.revlog.persistent-nodemap.mmap=no
253 253
254 254 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=yes
255 revision in index: 5003
256 revision in nodemap: 5003
255 revisions in index: 5003
256 revisions in nodemap: 5003
257 257 $ hg debugnodemap --check --config storage.revlog.persistent-nodemap.mmap=no
258 revision in index: 5003
259 revision in nodemap: 5003
258 revisions in index: 5003
259 revisions in nodemap: 5003
260 260
261 261
262 262 #if pure
263 263 $ hg debugnodemap --metadata
264 264 uid: ???????? (glob)
265 265 tip-rev: 5002
266 266 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
267 267 data-length: 121600
268 268 data-unused: 512
269 269 data-unused: 0.421%
270 270 $ f --sha256 .hg/store/00changelog-*.nd --size
271 271 .hg/store/00changelog-????????.nd: size=121600, sha256=def52503d049ccb823974af313a98a935319ba61f40f3aa06a8be4d35c215054 (glob)
272 272 #endif
273 273 #if rust
274 274 $ hg debugnodemap --metadata
275 275 uid: ???????? (glob)
276 276 tip-rev: 5002
277 277 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
278 278 data-length: 121600
279 279 data-unused: 512
280 280 data-unused: 0.421%
281 281 $ f --sha256 .hg/store/00changelog-*.nd --size
282 282 .hg/store/00changelog-????????.nd: size=121600, sha256=dacf5b5f1d4585fee7527d0e67cad5b1ba0930e6a0928f650f779aefb04ce3fb (glob)
283 283 #endif
284 284 #if no-pure no-rust
285 285 $ hg debugnodemap --metadata
286 286 uid: ???????? (glob)
287 287 tip-rev: 5002
288 288 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
289 289 data-length: 121088
290 290 data-unused: 0
291 291 data-unused: 0.000%
292 292 $ f --sha256 .hg/store/00changelog-*.nd --size
293 293 .hg/store/00changelog-????????.nd: size=121088, sha256=59fcede3e3cc587755916ceed29e3c33748cd1aa7d2f91828ac83e7979d935e8 (glob)
294 294 #endif
295 295
296 296 Test force warming the cache
297 297
298 298 $ rm .hg/store/00changelog.n
299 299 $ hg debugnodemap --metadata
300 300 $ hg debugupdatecache
301 301 #if pure
302 302 $ hg debugnodemap --metadata
303 303 uid: ???????? (glob)
304 304 tip-rev: 5002
305 305 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
306 306 data-length: 121088
307 307 data-unused: 0
308 308 data-unused: 0.000%
309 309 #else
310 310 $ hg debugnodemap --metadata
311 311 uid: ???????? (glob)
312 312 tip-rev: 5002
313 313 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
314 314 data-length: 121088
315 315 data-unused: 0
316 316 data-unused: 0.000%
317 317 #endif
318 318
319 319 Check out of sync nodemap
320 320 =========================
321 321
322 322 First copy old data on the side.
323 323
324 324 $ mkdir ../tmp-copies
325 325 $ cp .hg/store/00changelog-????????.nd .hg/store/00changelog.n ../tmp-copies
326 326
327 327 Nodemap lagging behind
328 328 ----------------------
329 329
330 330 make a new commit
331 331
332 332 $ echo bar2 > bar
333 333 $ hg ci -m 'bar2'
334 334 $ NODE=`hg log -r tip -T '{node}\n'`
335 335 $ hg log -r "$NODE" -T '{rev}\n'
336 336 5003
337 337
338 338 If the nodemap is lagging behind, it can catch up fine
339 339
340 340 $ hg debugnodemap --metadata
341 341 uid: ???????? (glob)
342 342 tip-rev: 5003
343 343 tip-node: c9329770f979ade2d16912267c38ba5f82fd37b3
344 344 data-length: 121344 (pure !)
345 345 data-length: 121344 (rust !)
346 346 data-length: 121152 (no-rust no-pure !)
347 347 data-unused: 192 (pure !)
348 348 data-unused: 192 (rust !)
349 349 data-unused: 0 (no-rust no-pure !)
350 350 data-unused: 0.158% (pure !)
351 351 data-unused: 0.158% (rust !)
352 352 data-unused: 0.000% (no-rust no-pure !)
353 353 $ cp -f ../tmp-copies/* .hg/store/
354 354 $ hg debugnodemap --metadata
355 355 uid: ???????? (glob)
356 356 tip-rev: 5002
357 357 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
358 358 data-length: 121088
359 359 data-unused: 0
360 360 data-unused: 0.000%
361 361 $ hg log -r "$NODE" -T '{rev}\n'
362 362 5003
363 363
364 364 changelog altered
365 365 -----------------
366 366
367 367 If the nodemap is not gated behind a requirements, an unaware client can alter
368 368 the repository so the revlog used to generate the nodemap is not longer
369 369 compatible with the persistent nodemap. We need to detect that.
370 370
371 371 $ hg up "$NODE~5"
372 372 0 files updated, 0 files merged, 4 files removed, 0 files unresolved
373 373 $ echo bar > babar
374 374 $ hg add babar
375 375 $ hg ci -m 'babar'
376 376 created new head
377 377 $ OTHERNODE=`hg log -r tip -T '{node}\n'`
378 378 $ hg log -r "$OTHERNODE" -T '{rev}\n'
379 379 5004
380 380
381 381 $ hg --config extensions.strip= strip --rev "$NODE~1" --no-backup
382 382
383 383 the nodemap should detect the changelog have been tampered with and recover.
384 384
385 385 $ hg debugnodemap --metadata
386 386 uid: ???????? (glob)
387 387 tip-rev: 5002
388 388 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
389 389 data-length: 121536 (pure !)
390 390 data-length: 121088 (rust !)
391 391 data-length: 121088 (no-pure no-rust !)
392 392 data-unused: 448 (pure !)
393 393 data-unused: 0 (rust !)
394 394 data-unused: 0 (no-pure no-rust !)
395 395 data-unused: 0.000% (rust !)
396 396 data-unused: 0.369% (pure !)
397 397 data-unused: 0.000% (no-pure no-rust !)
398 398
399 399 $ cp -f ../tmp-copies/* .hg/store/
400 400 $ hg debugnodemap --metadata
401 401 uid: ???????? (glob)
402 402 tip-rev: 5002
403 403 tip-node: 880b18d239dfa9f632413a2071bfdbcc4806a4fd
404 404 data-length: 121088
405 405 data-unused: 0
406 406 data-unused: 0.000%
407 407 $ hg log -r "$OTHERNODE" -T '{rev}\n'
408 408 5002
409 409
410 410 missing data file
411 411 -----------------
412 412
413 413 $ UUID=`hg debugnodemap --metadata| grep 'uid:' | \
414 414 > sed 's/uid: //'`
415 415 $ FILE=.hg/store/00changelog-"${UUID}".nd
416 416 $ mv $FILE ../tmp-data-file
417 417 $ cp .hg/store/00changelog.n ../tmp-docket
418 418
419 419 mercurial don't crash
420 420
421 421 $ hg log -r .
422 422 changeset: 5002:b355ef8adce0
423 423 tag: tip
424 424 parent: 4998:d918ad6d18d3
425 425 user: test
426 426 date: Thu Jan 01 00:00:00 1970 +0000
427 427 summary: babar
428 428
429 429 $ hg debugnodemap --metadata
430 430
431 431 $ hg debugupdatecache
432 432 $ hg debugnodemap --metadata
433 433 uid: * (glob)
434 434 tip-rev: 5002
435 435 tip-node: b355ef8adce0949b8bdf6afc72ca853740d65944
436 436 data-length: 121088
437 437 data-unused: 0
438 438 data-unused: 0.000%
439 439 $ mv ../tmp-data-file $FILE
440 440 $ mv ../tmp-docket .hg/store/00changelog.n
441 441
442 442 Check transaction related property
443 443 ==================================
444 444
445 445 An up to date nodemap should be available to shell hooks,
446 446
447 447 $ echo dsljfl > a
448 448 $ hg add a
449 449 $ hg ci -m a
450 450 $ hg debugnodemap --metadata
451 451 uid: ???????? (glob)
452 452 tip-rev: 5003
453 453 tip-node: a52c5079765b5865d97b993b303a18740113bbb2
454 454 data-length: 121088
455 455 data-unused: 0
456 456 data-unused: 0.000%
457 457 $ echo babar2 > babar
458 458 $ hg ci -m 'babar2' --config "hooks.pretxnclose.nodemap-test=hg debugnodemap --metadata"
459 459 uid: ???????? (glob)
460 460 tip-rev: 5004
461 461 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
462 462 data-length: 121280 (pure !)
463 463 data-length: 121280 (rust !)
464 464 data-length: 121088 (no-pure no-rust !)
465 465 data-unused: 192 (pure !)
466 466 data-unused: 192 (rust !)
467 467 data-unused: 0 (no-pure no-rust !)
468 468 data-unused: 0.158% (pure !)
469 469 data-unused: 0.158% (rust !)
470 470 data-unused: 0.000% (no-pure no-rust !)
471 471 $ hg debugnodemap --metadata
472 472 uid: ???????? (glob)
473 473 tip-rev: 5004
474 474 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
475 475 data-length: 121280 (pure !)
476 476 data-length: 121280 (rust !)
477 477 data-length: 121088 (no-pure no-rust !)
478 478 data-unused: 192 (pure !)
479 479 data-unused: 192 (rust !)
480 480 data-unused: 0 (no-pure no-rust !)
481 481 data-unused: 0.158% (pure !)
482 482 data-unused: 0.158% (rust !)
483 483 data-unused: 0.000% (no-pure no-rust !)
484 484
485 485 Another process does not see the pending nodemap content during run.
486 486
487 487 $ echo qpoasp > a
488 488 $ hg ci -m a2 \
489 489 > --config "hooks.pretxnclose=sh \"$RUNTESTDIR/testlib/wait-on-file\" 20 sync-repo-read sync-txn-pending" \
490 490 > --config "hooks.txnclose=touch sync-txn-close" > output.txt 2>&1 &
491 491
492 492 (read the repository while the commit transaction is pending)
493 493
494 494 $ sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-pending && \
495 495 > hg debugnodemap --metadata && \
496 496 > sh "$RUNTESTDIR/testlib/wait-on-file" 20 sync-txn-close sync-repo-read
497 497 uid: ???????? (glob)
498 498 tip-rev: 5004
499 499 tip-node: 2f5fb1c06a16834c5679d672e90da7c5f3b1a984
500 500 data-length: 121280 (pure !)
501 501 data-length: 121280 (rust !)
502 502 data-length: 121088 (no-pure no-rust !)
503 503 data-unused: 192 (pure !)
504 504 data-unused: 192 (rust !)
505 505 data-unused: 0 (no-pure no-rust !)
506 506 data-unused: 0.158% (pure !)
507 507 data-unused: 0.158% (rust !)
508 508 data-unused: 0.000% (no-pure no-rust !)
509 509 $ hg debugnodemap --metadata
510 510 uid: ???????? (glob)
511 511 tip-rev: 5005
512 512 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
513 513 data-length: 121536 (pure !)
514 514 data-length: 121536 (rust !)
515 515 data-length: 121088 (no-pure no-rust !)
516 516 data-unused: 448 (pure !)
517 517 data-unused: 448 (rust !)
518 518 data-unused: 0 (no-pure no-rust !)
519 519 data-unused: 0.369% (pure !)
520 520 data-unused: 0.369% (rust !)
521 521 data-unused: 0.000% (no-pure no-rust !)
522 522
523 523 $ cat output.txt
524 524
525 525 Check that a failing transaction will properly revert the data
526 526
527 527 $ echo plakfe > a
528 528 $ f --size --sha256 .hg/store/00changelog-*.nd
529 529 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
530 530 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
531 531 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
532 532 $ hg ci -m a3 --config "extensions.abort=$RUNTESTDIR/testlib/crash_transaction_late.py"
533 533 transaction abort!
534 534 rollback completed
535 535 abort: This is a late abort
536 536 [255]
537 537 $ hg debugnodemap --metadata
538 538 uid: ???????? (glob)
539 539 tip-rev: 5005
540 540 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
541 541 data-length: 121536 (pure !)
542 542 data-length: 121536 (rust !)
543 543 data-length: 121088 (no-pure no-rust !)
544 544 data-unused: 448 (pure !)
545 545 data-unused: 448 (rust !)
546 546 data-unused: 0 (no-pure no-rust !)
547 547 data-unused: 0.369% (pure !)
548 548 data-unused: 0.369% (rust !)
549 549 data-unused: 0.000% (no-pure no-rust !)
550 550 $ f --size --sha256 .hg/store/00changelog-*.nd
551 551 .hg/store/00changelog-????????.nd: size=121536, sha256=bb414468d225cf52d69132e1237afba34d4346ee2eb81b505027e6197b107f03 (glob) (pure !)
552 552 .hg/store/00changelog-????????.nd: size=121536, sha256=909ac727bc4d1c0fda5f7bff3c620c98bd4a2967c143405a1503439e33b377da (glob) (rust !)
553 553 .hg/store/00changelog-????????.nd: size=121088, sha256=342d36d30d86dde67d3cb6c002606c4a75bcad665595d941493845066d9c8ee0 (glob) (no-pure no-rust !)
554 554
555 555 Check that removing content does not confuse the nodemap
556 556 --------------------------------------------------------
557 557
558 558 removing data with rollback
559 559
560 560 $ echo aso > a
561 561 $ hg ci -m a4
562 562 $ hg rollback
563 563 repository tip rolled back to revision 5005 (undo commit)
564 564 working directory now based on revision 5005
565 565 $ hg id -r .
566 566 90d5d3ba2fc4 tip
567 567
568 568 removing data with strip
569 569
570 570 $ echo aso > a
571 571 $ hg ci -m a4
572 572 $ hg --config extensions.strip= strip -r . --no-backup
573 573 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
574 574 $ hg id -r . --traceback
575 575 90d5d3ba2fc4 tip
576 576
577 577 (be a good citizen and regenerate the nodemap)
578 578 $ hg debugupdatecaches
579 579 $ hg debugnodemap --metadata
580 580 uid: * (glob)
581 581 tip-rev: 5005
582 582 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
583 583 data-length: 121088
584 584 data-unused: 0
585 585 data-unused: 0.000%
586 586
587 587 Check race condition when multiple process write new data to the repository
588 588 ---------------------------------------------------------------------------
589 589
590 590 In this test, we check that two writers touching the repositories will not
591 591 overwrite each other data. This test is prompted by the existent of issue6554.
592 592 Where a writer ended up using and outdated docket to update the repository. See
593 593 the dedicated extension for details on the race windows and read/write schedule
594 594 necessary to end up in this situation: testlib/persistent-nodemap-race-ext.py
595 595
596 596 The issue was initially observed on a server with a high push trafic, but it
597 597 can be reproduced using a share and two commiting process which seems simpler.
598 598
599 599 The test is Rust only as the other implementation does not use the same
600 600 read/write patterns.
601 601
602 602 $ cd ..
603 603
604 604 #if rust
605 605
606 606 $ cp -R test-repo race-repo
607 607 $ hg share race-repo ./other-wc --config format.use-share-safe=yes
608 608 updating working directory
609 609 5001 files updated, 0 files merged, 0 files removed, 0 files unresolved
610 610 $ hg debugformat -R ./race-repo | egrep 'share-safe|persistent-nodemap'
611 611 share-safe: yes
612 612 persistent-nodemap: yes
613 613 $ hg debugformat -R ./other-wc/ | egrep 'share-safe|persistent-nodemap'
614 614 share-safe: yes
615 615 persistent-nodemap: yes
616 616 $ hg -R ./other-wc update 'min(head())'
617 617 3 files updated, 0 files merged, 2 files removed, 0 files unresolved
618 618 $ hg -R ./race-repo debugnodemap --metadata
619 619 uid: 43c37dde
620 620 tip-rev: 5005
621 621 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
622 622 data-length: 121088
623 623 data-unused: 0
624 624 data-unused: 0.000%
625 625 $ hg -R ./race-repo log -G -r 'head()'
626 626 @ changeset: 5005:90d5d3ba2fc4
627 627 | tag: tip
628 628 ~ user: test
629 629 date: Thu Jan 01 00:00:00 1970 +0000
630 630 summary: a2
631 631
632 632 o changeset: 5001:16395c3cf7e2
633 633 | user: test
634 634 ~ date: Thu Jan 01 00:00:00 1970 +0000
635 635 summary: foo
636 636
637 637 $ hg -R ./other-wc log -G -r 'head()'
638 638 o changeset: 5005:90d5d3ba2fc4
639 639 | tag: tip
640 640 ~ user: test
641 641 date: Thu Jan 01 00:00:00 1970 +0000
642 642 summary: a2
643 643
644 644 @ changeset: 5001:16395c3cf7e2
645 645 | user: test
646 646 ~ date: Thu Jan 01 00:00:00 1970 +0000
647 647 summary: foo
648 648
649 649 $ echo left-side-race > race-repo/left-side-race
650 650 $ hg -R ./race-repo/ add race-repo/left-side-race
651 651
652 652 $ echo right-side-race > ./other-wc/right-side-race
653 653 $ hg -R ./other-wc/ add ./other-wc/right-side-race
654 654
655 655 $ mkdir sync-files
656 656 $ mkdir outputs
657 657 $ (
658 658 > hg -R ./race-repo/ commit -m left-side-commit \
659 659 > --config "extensions.race=${RUNTESTDIR}/testlib/persistent-nodemap-race-ext.py" \
660 660 > --config 'devel.nodemap-race.role=left';
661 661 > touch sync-files/left-done
662 662 > ) > outputs/left.txt 2>&1 &
663 663 $ (
664 664 > hg -R ./other-wc/ commit -m right-side-commit \
665 665 > --config "extensions.race=${RUNTESTDIR}/testlib/persistent-nodemap-race-ext.py" \
666 666 > --config 'devel.nodemap-race.role=right';
667 667 > touch sync-files/right-done
668 668 > ) > outputs/right.txt 2>&1 &
669 669 $ (
670 670 > hg -R ./race-repo/ check-nodemap-race \
671 671 > --config "extensions.race=${RUNTESTDIR}/testlib/persistent-nodemap-race-ext.py" \
672 672 > --config 'devel.nodemap-race.role=reader';
673 673 > touch sync-files/reader-done
674 674 > ) > outputs/reader.txt 2>&1 &
675 675 $ sh "$RUNTESTDIR"/testlib/wait-on-file 10 sync-files/left-done
676 676 $ cat outputs/left.txt
677 677 docket-details:
678 678 uid: 43c37dde
679 679 actual-tip: 5005
680 680 tip-rev: 5005
681 681 data-length: 121088
682 682 nodemap-race: left side locked and ready to commit
683 683 docket-details:
684 684 uid: 43c37dde
685 685 actual-tip: 5005
686 686 tip-rev: 5005
687 687 data-length: 121088
688 688 finalized changelog write
689 689 persisting changelog nodemap
690 690 new data start at 121088
691 691 persisted changelog nodemap
692 692 docket-details:
693 693 uid: 43c37dde
694 694 actual-tip: 5006
695 695 tip-rev: 5006
696 696 data-length: 121280
697 697 $ sh "$RUNTESTDIR"/testlib/wait-on-file 10 sync-files/right-done
698 698 $ cat outputs/right.txt
699 699 nodemap-race: right side start of the locking sequence
700 700 nodemap-race: right side reading changelog
701 701 nodemap-race: right side reading of changelog is done
702 702 docket-details:
703 703 uid: 43c37dde
704 704 actual-tip: 5006
705 705 tip-rev: 5005
706 706 data-length: 121088
707 707 nodemap-race: right side ready to wait for the lock
708 708 nodemap-race: right side locked and ready to commit
709 709 docket-details:
710 710 uid: 43c37dde
711 711 actual-tip: 5006
712 712 tip-rev: 5006
713 713 data-length: 121280
714 714 right ready to write, waiting for reader
715 715 right proceeding with writing its changelog index and nodemap
716 716 finalized changelog write
717 717 persisting changelog nodemap
718 718 new data start at 121280
719 719 persisted changelog nodemap
720 720 docket-details:
721 721 uid: 43c37dde
722 722 actual-tip: 5007
723 723 tip-rev: 5007
724 724 data-length: 121536
725 725 $ sh "$RUNTESTDIR"/testlib/wait-on-file 10 sync-files/reader-done
726 726 $ cat outputs/reader.txt
727 727 reader: reading changelog
728 728 reader ready to read the changelog, waiting for right
729 729 reader: nodemap docket read
730 730 record-data-length: 121280
731 731 actual-data-length: 121280
732 732 file-actual-length: 121536
733 733 reader: changelog read
734 734 docket-details:
735 735 uid: 43c37dde
736 736 actual-tip: 5006
737 737 tip-rev: 5006
738 738 data-length: 121280
739 739 tip-rev: 5006
740 740 tip-node: 492901161367
741 741 node-rev: 5006
742 742
743 743 $ hg -R ./race-repo log -G -r 'head()'
744 744 o changeset: 5007:ac4a2abde241
745 745 | tag: tip
746 746 ~ parent: 5001:16395c3cf7e2
747 747 user: test
748 748 date: Thu Jan 01 00:00:00 1970 +0000
749 749 summary: right-side-commit
750 750
751 751 @ changeset: 5006:492901161367
752 752 | user: test
753 753 ~ date: Thu Jan 01 00:00:00 1970 +0000
754 754 summary: left-side-commit
755 755
756 756 $ hg -R ./other-wc log -G -r 'head()'
757 757 @ changeset: 5007:ac4a2abde241
758 758 | tag: tip
759 759 ~ parent: 5001:16395c3cf7e2
760 760 user: test
761 761 date: Thu Jan 01 00:00:00 1970 +0000
762 762 summary: right-side-commit
763 763
764 764 o changeset: 5006:492901161367
765 765 | user: test
766 766 ~ date: Thu Jan 01 00:00:00 1970 +0000
767 767 summary: left-side-commit
768 768
769 769 #endif
770 770
771 771 Test upgrade / downgrade
772 772 ========================
773 773
774 774 $ cd ./test-repo/
775 775
776 776 downgrading
777 777
778 778 $ cat << EOF >> .hg/hgrc
779 779 > [format]
780 780 > use-persistent-nodemap=no
781 781 > EOF
782 782 $ hg debugformat -v
783 783 format-variant repo config default
784 784 fncache: yes yes yes
785 785 dirstate-v2: no no no
786 786 tracked-hint: no no no
787 787 dotencode: yes yes yes
788 788 generaldelta: yes yes yes
789 789 share-safe: yes yes yes
790 790 sparserevlog: yes yes yes
791 791 persistent-nodemap: yes no no
792 792 copies-sdc: no no no
793 793 revlog-v2: no no no
794 794 changelog-v2: no no no
795 795 plain-cl-delta: yes yes yes
796 796 compression: zlib zlib zlib (no-zstd !)
797 797 compression: zstd zstd zstd (zstd !)
798 798 compression-level: default default default
799 799 $ hg debugupgraderepo --run --no-backup --quiet
800 800 upgrade will perform the following actions:
801 801
802 802 requirements
803 803 preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
804 804 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
805 805 preserved: dotencode, use-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
806 806 removed: persistent-nodemap
807 807
808 808 processed revlogs:
809 809 - all-filelogs
810 810 - changelog
811 811 - manifest
812 812
813 813 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
814 814 [1]
815 815 $ hg debugnodemap --metadata
816 816
817 817
818 818 upgrading
819 819
820 820 $ cat << EOF >> .hg/hgrc
821 821 > [format]
822 822 > use-persistent-nodemap=yes
823 823 > EOF
824 824 $ hg debugformat -v
825 825 format-variant repo config default
826 826 fncache: yes yes yes
827 827 dirstate-v2: no no no
828 828 tracked-hint: no no no
829 829 dotencode: yes yes yes
830 830 generaldelta: yes yes yes
831 831 share-safe: yes yes yes
832 832 sparserevlog: yes yes yes
833 833 persistent-nodemap: no yes no
834 834 copies-sdc: no no no
835 835 revlog-v2: no no no
836 836 changelog-v2: no no no
837 837 plain-cl-delta: yes yes yes
838 838 compression: zlib zlib zlib (no-zstd !)
839 839 compression: zstd zstd zstd (zstd !)
840 840 compression-level: default default default
841 841 $ hg debugupgraderepo --run --no-backup --quiet
842 842 upgrade will perform the following actions:
843 843
844 844 requirements
845 845 preserved: dotencode, fncache, generaldelta, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
846 846 preserved: dotencode, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
847 847 preserved: dotencode, use-dirstate-v2, fncache, generaldelta, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
848 848 added: persistent-nodemap
849 849
850 850 processed revlogs:
851 851 - all-filelogs
852 852 - changelog
853 853 - manifest
854 854
855 855 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
856 856 00changelog-*.nd (glob)
857 857 00changelog.n
858 858 00manifest-*.nd (glob)
859 859 00manifest.n
860 860
861 861 $ hg debugnodemap --metadata
862 862 uid: * (glob)
863 863 tip-rev: 5005
864 864 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
865 865 data-length: 121088
866 866 data-unused: 0
867 867 data-unused: 0.000%
868 868
869 869 Running unrelated upgrade
870 870
871 871 $ hg debugupgraderepo --run --no-backup --quiet --optimize re-delta-all
872 872 upgrade will perform the following actions:
873 873
874 874 requirements
875 875 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlogv1, share-safe, sparserevlog, store (no-zstd no-dirstate-v2 !)
876 876 preserved: dotencode, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd no-dirstate-v2 !)
877 877 preserved: dotencode, use-dirstate-v2, fncache, generaldelta, persistent-nodemap, revlog-compression-zstd, revlogv1, share-safe, sparserevlog, store (zstd dirstate-v2 !)
878 878
879 879 optimisations: re-delta-all
880 880
881 881 processed revlogs:
882 882 - all-filelogs
883 883 - changelog
884 884 - manifest
885 885
886 886 $ ls -1 .hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
887 887 00changelog-*.nd (glob)
888 888 00changelog.n
889 889 00manifest-*.nd (glob)
890 890 00manifest.n
891 891
892 892 $ hg debugnodemap --metadata
893 893 uid: * (glob)
894 894 tip-rev: 5005
895 895 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
896 896 data-length: 121088
897 897 data-unused: 0
898 898 data-unused: 0.000%
899 899
900 900 Persistent nodemap and local/streaming clone
901 901 ============================================
902 902
903 903 $ cd ..
904 904
905 905 standard clone
906 906 --------------
907 907
908 908 The persistent nodemap should exist after a streaming clone
909 909
910 910 $ hg clone --pull --quiet -U test-repo standard-clone
911 911 $ ls -1 standard-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
912 912 00changelog-*.nd (glob)
913 913 00changelog.n
914 914 00manifest-*.nd (glob)
915 915 00manifest.n
916 916 $ hg -R standard-clone debugnodemap --metadata
917 917 uid: * (glob)
918 918 tip-rev: 5005
919 919 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
920 920 data-length: 121088
921 921 data-unused: 0
922 922 data-unused: 0.000%
923 923
924 924
925 925 local clone
926 926 ------------
927 927
928 928 The persistent nodemap should exist after a streaming clone
929 929
930 930 $ hg clone -U test-repo local-clone
931 931 $ ls -1 local-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
932 932 00changelog-*.nd (glob)
933 933 00changelog.n
934 934 00manifest-*.nd (glob)
935 935 00manifest.n
936 936 $ hg -R local-clone debugnodemap --metadata
937 937 uid: * (glob)
938 938 tip-rev: 5005
939 939 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
940 940 data-length: 121088
941 941 data-unused: 0
942 942 data-unused: 0.000%
943 943
944 944 Test various corruption case
945 945 ============================
946 946
947 947 Missing datafile
948 948 ----------------
949 949
950 950 Test behavior with a missing datafile
951 951
952 952 $ hg clone --quiet --pull test-repo corruption-test-repo
953 953 $ ls -1 corruption-test-repo/.hg/store/00changelog*
954 954 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
955 955 corruption-test-repo/.hg/store/00changelog.d
956 956 corruption-test-repo/.hg/store/00changelog.i
957 957 corruption-test-repo/.hg/store/00changelog.n
958 958 $ rm corruption-test-repo/.hg/store/00changelog*.nd
959 959 $ hg log -R corruption-test-repo -r .
960 960 changeset: 5005:90d5d3ba2fc4
961 961 tag: tip
962 962 user: test
963 963 date: Thu Jan 01 00:00:00 1970 +0000
964 964 summary: a2
965 965
966 966 $ ls -1 corruption-test-repo/.hg/store/00changelog*
967 967 corruption-test-repo/.hg/store/00changelog.d
968 968 corruption-test-repo/.hg/store/00changelog.i
969 969 corruption-test-repo/.hg/store/00changelog.n
970 970
971 971 Truncated data file
972 972 -------------------
973 973
974 974 Test behavior with a too short datafile
975 975
976 976 rebuild the missing data
977 977 $ hg -R corruption-test-repo debugupdatecache
978 978 $ ls -1 corruption-test-repo/.hg/store/00changelog*
979 979 corruption-test-repo/.hg/store/00changelog-*.nd (glob)
980 980 corruption-test-repo/.hg/store/00changelog.d
981 981 corruption-test-repo/.hg/store/00changelog.i
982 982 corruption-test-repo/.hg/store/00changelog.n
983 983
984 984 truncate the file
985 985
986 986 $ datafilepath=`ls corruption-test-repo/.hg/store/00changelog*.nd`
987 987 $ f -s $datafilepath
988 988 corruption-test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
989 989 $ dd if=$datafilepath bs=1000 count=10 of=$datafilepath-tmp
990 990 10+0 records in
991 991 10+0 records out
992 992 * bytes * (glob)
993 993 $ mv $datafilepath-tmp $datafilepath
994 994 $ f -s $datafilepath
995 995 corruption-test-repo/.hg/store/00changelog-*.nd: size=10000 (glob)
996 996
997 997 Check that Mercurial reaction to this event
998 998
999 999 $ hg -R corruption-test-repo log -r . --traceback
1000 1000 changeset: 5005:90d5d3ba2fc4
1001 1001 tag: tip
1002 1002 user: test
1003 1003 date: Thu Jan 01 00:00:00 1970 +0000
1004 1004 summary: a2
1005 1005
1006 1006
1007 1007
1008 1008 stream clone
1009 1009 ============
1010 1010
1011 1011 The persistent nodemap should exist after a streaming clone
1012 1012
1013 1013 Simple case
1014 1014 -----------
1015 1015
1016 1016 No race condition
1017 1017
1018 1018 $ hg clone -U --stream ssh://user@dummy/test-repo stream-clone --debug | egrep '00(changelog|manifest)'
1019 1019 adding [s] 00manifest.n (62 bytes)
1020 1020 adding [s] 00manifest-*.nd (118 KB) (glob)
1021 1021 adding [s] 00manifest.d (491 KB) (zstd no-bigendian !)
1022 1022 adding [s] 00manifest.d (452 KB) (no-zstd !)
1023 1023 adding [s] 00manifest.d (492 KB) (zstd bigendian !)
1024 1024 adding [s] 00manifest.i (313 KB)
1025 1025 adding [s] 00changelog.n (62 bytes)
1026 1026 adding [s] 00changelog-*.nd (118 KB) (glob)
1027 1027 adding [s] 00changelog.d (360 KB) (no-zstd !)
1028 1028 adding [s] 00changelog.d (368 KB) (zstd !)
1029 1029 adding [s] 00changelog.i (313 KB)
1030 1030 $ ls -1 stream-clone/.hg/store/ | egrep '00(changelog|manifest)(\.n|-.*\.nd)'
1031 1031 00changelog-*.nd (glob)
1032 1032 00changelog.n
1033 1033 00manifest-*.nd (glob)
1034 1034 00manifest.n
1035 1035 $ hg -R stream-clone debugnodemap --metadata
1036 1036 uid: * (glob)
1037 1037 tip-rev: 5005
1038 1038 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1039 1039 data-length: 121088
1040 1040 data-unused: 0
1041 1041 data-unused: 0.000%
1042 1042
1043 1043 new data appened
1044 1044 -----------------
1045 1045
1046 1046 Other commit happening on the server during the stream clone
1047 1047
1048 1048 setup the step-by-step stream cloning
1049 1049
1050 1050 $ HG_TEST_STREAM_WALKED_FILE_1="$TESTTMP/sync_file_walked_1"
1051 1051 $ export HG_TEST_STREAM_WALKED_FILE_1
1052 1052 $ HG_TEST_STREAM_WALKED_FILE_2="$TESTTMP/sync_file_walked_2"
1053 1053 $ export HG_TEST_STREAM_WALKED_FILE_2
1054 1054 $ HG_TEST_STREAM_WALKED_FILE_3="$TESTTMP/sync_file_walked_3"
1055 1055 $ export HG_TEST_STREAM_WALKED_FILE_3
1056 1056 $ cat << EOF >> test-repo/.hg/hgrc
1057 1057 > [extensions]
1058 1058 > steps=$RUNTESTDIR/testlib/ext-stream-clone-steps.py
1059 1059 > EOF
1060 1060
1061 1061 Check and record file state beforehand
1062 1062
1063 1063 $ f --size test-repo/.hg/store/00changelog*
1064 1064 test-repo/.hg/store/00changelog-*.nd: size=121088 (glob)
1065 1065 test-repo/.hg/store/00changelog.d: size=376891 (zstd no-bigendian !)
1066 1066 test-repo/.hg/store/00changelog.d: size=376889 (zstd bigendian !)
1067 1067 test-repo/.hg/store/00changelog.d: size=368890 (no-zstd !)
1068 1068 test-repo/.hg/store/00changelog.i: size=320384
1069 1069 test-repo/.hg/store/00changelog.n: size=62
1070 1070 $ hg -R test-repo debugnodemap --metadata | tee server-metadata.txt
1071 1071 uid: * (glob)
1072 1072 tip-rev: 5005
1073 1073 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1074 1074 data-length: 121088
1075 1075 data-unused: 0
1076 1076 data-unused: 0.000%
1077 1077
1078 1078 Prepare a commit
1079 1079
1080 1080 $ echo foo >> test-repo/foo
1081 1081 $ hg -R test-repo/ add test-repo/foo
1082 1082
1083 1083 Do a mix of clone and commit at the same time so that the file listed on disk differ at actual transfer time.
1084 1084
1085 1085 $ (hg clone -U --stream ssh://user@dummy/test-repo stream-clone-race-1 --debug 2>> clone-output | egrep '00(changelog|manifest)' >> clone-output; touch $HG_TEST_STREAM_WALKED_FILE_3) &
1086 1086 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
1087 1087 $ hg -R test-repo/ commit -m foo
1088 1088 $ touch $HG_TEST_STREAM_WALKED_FILE_2
1089 1089 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
1090 1090 $ cat clone-output
1091 1091 adding [s] 00manifest.n (62 bytes)
1092 1092 adding [s] 00manifest-*.nd (118 KB) (glob)
1093 1093 adding [s] 00manifest.d (491 KB) (zstd no-bigendian !)
1094 1094 adding [s] 00manifest.d (452 KB) (no-zstd !)
1095 1095 adding [s] 00manifest.d (492 KB) (zstd bigendian !)
1096 1096 adding [s] 00manifest.i (313 KB)
1097 1097 adding [s] 00changelog.n (62 bytes)
1098 1098 adding [s] 00changelog-*.nd (118 KB) (glob)
1099 1099 adding [s] 00changelog.d (368 KB) (zstd !)
1100 1100 adding [s] 00changelog.d (360 KB) (no-zstd !)
1101 1101 adding [s] 00changelog.i (313 KB)
1102 1102
1103 1103 Check the result state
1104 1104
1105 1105 $ f --size stream-clone-race-1/.hg/store/00changelog*
1106 1106 stream-clone-race-1/.hg/store/00changelog-*.nd: size=121088 (glob)
1107 1107 stream-clone-race-1/.hg/store/00changelog.d: size=368890 (no-zstd !)
1108 1108 stream-clone-race-1/.hg/store/00changelog.d: size=376891 (zstd no-bigendian !)
1109 1109 stream-clone-race-1/.hg/store/00changelog.d: size=376889 (zstd bigendian !)
1110 1110 stream-clone-race-1/.hg/store/00changelog.i: size=320384
1111 1111 stream-clone-race-1/.hg/store/00changelog.n: size=62
1112 1112
1113 1113 $ hg -R stream-clone-race-1 debugnodemap --metadata | tee client-metadata.txt
1114 1114 uid: * (glob)
1115 1115 tip-rev: 5005
1116 1116 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1117 1117 data-length: 121088
1118 1118 data-unused: 0
1119 1119 data-unused: 0.000%
1120 1120
1121 1121 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1122 1122 (ie: the following diff should be empty)
1123 1123
1124 1124 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1125 1125
1126 1126 #if no-rust no-pure
1127 1127 $ diff -u server-metadata.txt client-metadata.txt
1128 1128 --- server-metadata.txt * (glob)
1129 1129 +++ client-metadata.txt * (glob)
1130 1130 @@ -1,4 +1,4 @@
1131 1131 -uid: * (glob)
1132 1132 +uid: * (glob)
1133 1133 tip-rev: 5005
1134 1134 tip-node: 90d5d3ba2fc47db50f712570487cb261a68c8ffe
1135 1135 data-length: 121088
1136 1136 [1]
1137 1137 #else
1138 1138 $ diff -u server-metadata.txt client-metadata.txt
1139 1139 #endif
1140 1140
1141 1141
1142 1142 Clean up after the test.
1143 1143
1144 1144 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_1"
1145 1145 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_2"
1146 1146 $ rm -f "$HG_TEST_STREAM_WALKED_FILE_3"
1147 1147
1148 1148 full regeneration
1149 1149 -----------------
1150 1150
1151 1151 A full nodemap is generated
1152 1152
1153 1153 (ideally this test would append enough data to make sure the nodemap data file
1154 1154 get changed, however to make thing simpler we will force the regeneration for
1155 1155 this test.
1156 1156
1157 1157 Check the initial state
1158 1158
1159 1159 $ f --size test-repo/.hg/store/00changelog*
1160 1160 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1161 1161 test-repo/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1162 1162 test-repo/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1163 1163 test-repo/.hg/store/00changelog.d: size=376950 (zstd no-bigendian !)
1164 1164 test-repo/.hg/store/00changelog.d: size=376948 (zstd bigendian !)
1165 1165 test-repo/.hg/store/00changelog.d: size=368949 (no-zstd !)
1166 1166 test-repo/.hg/store/00changelog.i: size=320448
1167 1167 test-repo/.hg/store/00changelog.n: size=62
1168 1168 $ hg -R test-repo debugnodemap --metadata | tee server-metadata-2.txt
1169 1169 uid: * (glob)
1170 1170 tip-rev: 5006
1171 1171 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1172 1172 data-length: 121344 (rust !)
1173 1173 data-length: 121344 (pure !)
1174 1174 data-length: 121152 (no-rust no-pure !)
1175 1175 data-unused: 192 (rust !)
1176 1176 data-unused: 192 (pure !)
1177 1177 data-unused: 0 (no-rust no-pure !)
1178 1178 data-unused: 0.158% (rust !)
1179 1179 data-unused: 0.158% (pure !)
1180 1180 data-unused: 0.000% (no-rust no-pure !)
1181 1181
1182 1182 Performe the mix of clone and full refresh of the nodemap, so that the files
1183 1183 (and filenames) are different between listing time and actual transfer time.
1184 1184
1185 1185 $ (hg clone -U --stream ssh://user@dummy/test-repo stream-clone-race-2 --debug 2>> clone-output-2 | egrep '00(changelog|manifest)' >> clone-output-2; touch $HG_TEST_STREAM_WALKED_FILE_3) &
1186 1186 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_1
1187 1187 $ rm test-repo/.hg/store/00changelog.n
1188 1188 $ rm test-repo/.hg/store/00changelog-*.nd
1189 1189 $ hg -R test-repo/ debugupdatecache
1190 1190 $ touch $HG_TEST_STREAM_WALKED_FILE_2
1191 1191 $ $RUNTESTDIR/testlib/wait-on-file 10 $HG_TEST_STREAM_WALKED_FILE_3
1192 1192
1193 1193 (note: the stream clone code wronly pick the `undo.` files)
1194 1194
1195 1195 $ cat clone-output-2
1196 1196 adding [s] 00manifest.n (62 bytes)
1197 1197 adding [s] 00manifest-*.nd (118 KB) (glob)
1198 1198 adding [s] 00manifest.d (492 KB) (zstd !)
1199 1199 adding [s] 00manifest.d (452 KB) (no-zstd !)
1200 1200 adding [s] 00manifest.i (313 KB)
1201 1201 adding [s] 00changelog.n (62 bytes)
1202 1202 adding [s] 00changelog-*.nd (118 KB) (glob)
1203 1203 adding [s] 00changelog.d (360 KB) (no-zstd !)
1204 1204 adding [s] 00changelog.d (368 KB) (zstd !)
1205 1205 adding [s] 00changelog.i (313 KB)
1206 1206
1207 1207 Check the result.
1208 1208
1209 1209 $ f --size stream-clone-race-2/.hg/store/00changelog*
1210 1210 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (rust !)
1211 1211 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121344 (glob) (pure !)
1212 1212 stream-clone-race-2/.hg/store/00changelog-*.nd: size=121152 (glob) (no-rust no-pure !)
1213 1213 stream-clone-race-2/.hg/store/00changelog.d: size=376950 (zstd no-bigendian !)
1214 1214 stream-clone-race-2/.hg/store/00changelog.d: size=376948 (zstd bigendian !)
1215 1215 stream-clone-race-2/.hg/store/00changelog.d: size=368949 (no-zstd !)
1216 1216 stream-clone-race-2/.hg/store/00changelog.i: size=320448
1217 1217 stream-clone-race-2/.hg/store/00changelog.n: size=62
1218 1218
1219 1219 $ hg -R stream-clone-race-2 debugnodemap --metadata | tee client-metadata-2.txt
1220 1220 uid: * (glob)
1221 1221 tip-rev: 5006
1222 1222 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1223 1223 data-length: 121344 (rust !)
1224 1224 data-unused: 192 (rust !)
1225 1225 data-unused: 0.158% (rust !)
1226 1226 data-length: 121152 (no-rust no-pure !)
1227 1227 data-unused: 0 (no-rust no-pure !)
1228 1228 data-unused: 0.000% (no-rust no-pure !)
1229 1229 data-length: 121344 (pure !)
1230 1230 data-unused: 192 (pure !)
1231 1231 data-unused: 0.158% (pure !)
1232 1232
1233 1233 We get a usable nodemap, so no rewrite would be needed and the metadata should be identical
1234 1234 (ie: the following diff should be empty)
1235 1235
1236 1236 This isn't the case for the `no-rust` `no-pure` implementation as it use a very minimal nodemap implementation that unconditionnaly rewrite the nodemap "all the time".
1237 1237
1238 1238 #if no-rust no-pure
1239 1239 $ diff -u server-metadata-2.txt client-metadata-2.txt
1240 1240 --- server-metadata-2.txt * (glob)
1241 1241 +++ client-metadata-2.txt * (glob)
1242 1242 @@ -1,4 +1,4 @@
1243 1243 -uid: * (glob)
1244 1244 +uid: * (glob)
1245 1245 tip-rev: 5006
1246 1246 tip-node: ed2ec1eef9aa2a0ec5057c51483bc148d03e810b
1247 1247 data-length: 121152
1248 1248 [1]
1249 1249 #else
1250 1250 $ diff -u server-metadata-2.txt client-metadata-2.txt
1251 1251 #endif
1252 1252
1253 1253 Clean up after the test
1254 1254
1255 1255 $ rm -f $HG_TEST_STREAM_WALKED_FILE_1
1256 1256 $ rm -f $HG_TEST_STREAM_WALKED_FILE_2
1257 1257 $ rm -f $HG_TEST_STREAM_WALKED_FILE_3
1258 1258
General Comments 0
You need to be logged in to leave comments. Login now