comparison mercurial/revlog.py @ 47238:6597255a4f94

revlogv2: track current index size in the docket This help use to fix transaction safety on repos. See next changesets for details. Differential Revision: https://phab.mercurial-scm.org/D10628
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 03 May 2021 12:34:52 +0200
parents 6b1eae313b2f
children 682f09857d69
comparison
equal deleted inserted replaced
47237:864f4ebe6a8d 47238:6597255a4f94
451 % self._chunkcachesize 451 % self._chunkcachesize
452 ) 452 )
453 force_nodemap = opts.get(b'devel-force-nodemap', False) 453 force_nodemap = opts.get(b'devel-force-nodemap', False)
454 return new_header, mmapindexthreshold, force_nodemap 454 return new_header, mmapindexthreshold, force_nodemap
455 455
456 def _get_data(self, filepath, mmap_threshold): 456 def _get_data(self, filepath, mmap_threshold, size=None):
457 """return a file content with or without mmap 457 """return a file content with or without mmap
458 458
459 If the file is missing return the empty string""" 459 If the file is missing return the empty string"""
460 try: 460 try:
461 with self.opener(filepath) as fp: 461 with self.opener(filepath) as fp:
462 if mmap_threshold is not None: 462 if mmap_threshold is not None:
463 file_size = self.opener.fstat(fp).st_size 463 file_size = self.opener.fstat(fp).st_size
464 if file_size >= mmap_threshold: 464 if file_size >= mmap_threshold:
465 if size is not None:
466 # avoid potentiel mmap crash
467 size = min(file_size, size)
465 # TODO: should .close() to release resources without 468 # TODO: should .close() to release resources without
466 # relying on Python GC 469 # relying on Python GC
467 return util.buffer(util.mmapread(fp)) 470 if size is None:
468 return fp.read() 471 return util.buffer(util.mmapread(fp))
472 else:
473 return util.buffer(util.mmapread(fp, size))
474 if size is None:
475 return fp.read()
476 else:
477 return fp.read(size)
469 except IOError as inst: 478 except IOError as inst:
470 if inst.errno != errno.ENOENT: 479 if inst.errno != errno.ENOENT:
471 raise 480 raise
472 return b'' 481 return b''
473 482
516 if self._initempty: 525 if self._initempty:
517 self._docket = docketutil.default_docket(self, header) 526 self._docket = docketutil.default_docket(self, header)
518 else: 527 else:
519 self._docket = docketutil.parse_docket(self, entry_data) 528 self._docket = docketutil.parse_docket(self, entry_data)
520 self._indexfile = self._docket.index_filepath() 529 self._indexfile = self._docket.index_filepath()
521 index_data = self._get_data(self._indexfile, mmapindexthreshold) 530 index_data = b''
531 index_size = self._docket.index_end
532 if index_size > 0:
533 index_data = self._get_data(
534 self._indexfile, mmapindexthreshold, size=index_size
535 )
536 if len(index_data) < index_size:
537 msg = _(b'too few index data for %s: got %d, expected %d')
538 msg %= (self.display_id, len(index_data), index_size)
539 raise error.RevlogError(msg)
540
522 self._inline = False 541 self._inline = False
523 # generaldelta implied by version 2 revlogs. 542 # generaldelta implied by version 2 revlogs.
524 self._generaldelta = True 543 self._generaldelta = True
525 # the logic for persistent nodemap will be dealt with within the 544 # the logic for persistent nodemap will be dealt with within the
526 # main docket, so disable it for now. 545 # main docket, so disable it for now.
617 # You should not use this directly and use `_writing` instead 636 # You should not use this directly and use `_writing` instead
618 try: 637 try:
619 f = self.opener( 638 f = self.opener(
620 self._indexfile, mode=b"r+", checkambig=self._checkambig 639 self._indexfile, mode=b"r+", checkambig=self._checkambig
621 ) 640 )
622 f.seek(0, os.SEEK_END) 641 if self._docket is None:
642 f.seek(0, os.SEEK_END)
643 else:
644 f.seek(self._docket.index_end, os.SEEK_SET)
623 return f 645 return f
624 except IOError as inst: 646 except IOError as inst:
625 if inst.errno != errno.ENOENT: 647 if inst.errno != errno.ENOENT:
626 raise 648 raise
627 return self.opener( 649 return self.opener(
2020 if i == 0 and self._docket is None: 2042 if i == 0 and self._docket is None:
2021 header = self._format_flags | self._format_version 2043 header = self._format_flags | self._format_version
2022 header = self.index.pack_header(header) 2044 header = self.index.pack_header(header)
2023 e = header + e 2045 e = header + e
2024 fp.write(e) 2046 fp.write(e)
2047 if self._docket is not None:
2048 self._docket.index_end = fp.tell()
2025 # the temp file replace the real index when we exit the context 2049 # the temp file replace the real index when we exit the context
2026 # manager 2050 # manager
2027 2051
2028 tr.replace(self._indexfile, trindex * self.index.entry_size) 2052 tr.replace(self._indexfile, trindex * self.index.entry_size)
2029 nodemaputil.setup_persistent_nodemap(tr, self) 2053 nodemaputil.setup_persistent_nodemap(tr, self)
2438 # to be careful before changing this. 2462 # to be careful before changing this.
2439 if self._writinghandles is None: 2463 if self._writinghandles is None:
2440 msg = b'adding revision outside `revlog._writing` context' 2464 msg = b'adding revision outside `revlog._writing` context'
2441 raise error.ProgrammingError(msg) 2465 raise error.ProgrammingError(msg)
2442 ifh, dfh = self._writinghandles 2466 ifh, dfh = self._writinghandles
2443 ifh.seek(0, os.SEEK_END) 2467 if self._docket is None:
2468 ifh.seek(0, os.SEEK_END)
2469 else:
2470 ifh.seek(self._docket.index_end, os.SEEK_SET)
2444 if dfh: 2471 if dfh:
2445 dfh.seek(0, os.SEEK_END) 2472 dfh.seek(0, os.SEEK_END)
2446 2473
2447 curr = len(self) - 1 2474 curr = len(self) - 1
2448 if not self._inline: 2475 if not self._inline:
2461 ifh.write(data[0]) 2488 ifh.write(data[0])
2462 ifh.write(data[1]) 2489 ifh.write(data[1])
2463 if sidedata: 2490 if sidedata:
2464 ifh.write(sidedata) 2491 ifh.write(sidedata)
2465 self._enforceinlinesize(transaction) 2492 self._enforceinlinesize(transaction)
2493 if self._docket is not None:
2494 self._docket.index_end = self._writinghandles[0].tell()
2495
2466 nodemaputil.setup_persistent_nodemap(transaction, self) 2496 nodemaputil.setup_persistent_nodemap(transaction, self)
2467 2497
2468 def addgroup( 2498 def addgroup(
2469 self, 2499 self,
2470 deltas, 2500 deltas,
2630 end = rev * self.index.entry_size 2660 end = rev * self.index.entry_size
2631 else: 2661 else:
2632 end += rev * self.index.entry_size 2662 end += rev * self.index.entry_size
2633 2663
2634 transaction.add(self._indexfile, end) 2664 transaction.add(self._indexfile, end)
2665 if self._docket is not None:
2666 # XXX we could, leverage the docket while stripping. However it is
2667 # not powerfull enough at the time of this comment
2668 self._docket.index_end = end
2669 self._docket.write(transaction, stripping=True)
2635 2670
2636 # then reset internal state in memory to forget those revisions 2671 # then reset internal state in memory to forget those revisions
2637 self._revisioncache = None 2672 self._revisioncache = None
2638 self._chaininfocache = util.lrucachedict(500) 2673 self._chaininfocache = util.lrucachedict(500)
2639 self._chunkclear() 2674 self._chunkclear()