Mercurial > hg
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() |