comparison mercurial/revlogutils/docket.py @ 47241:2219853a1503

revlogv2: track pending write in the docket and expose it to hooks The docket is now able to write pending data. We could have used a distinct intermediate files, however keeping everything in the same file will make it simpler to keep track of the various involved files if necessary. However it might prove more complicated for streaming clone. This will be dealt with later. Note that we lifted the stderr redirection in the test since we no longer suffer from "unkown working directory parent" message. Differential Revision: https://phab.mercurial-scm.org/D10631
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 03 May 2021 12:35:25 +0200
parents 6597255a4f94
children 4abd474a10af
comparison
equal deleted inserted replaced
47240:4f38ada3fc26 47241:2219853a1503
17 17
18 from __future__ import absolute_import 18 from __future__ import absolute_import
19 19
20 import struct 20 import struct
21 21
22 from .. import (
23 error,
24 )
25
22 from . import ( 26 from . import (
23 constants, 27 constants,
24 ) 28 )
25 29
26 # Docket format 30 # Docket format
27 # 31 #
28 # * 4 bytes: revlog version 32 # * 4 bytes: revlog version
29 # | This is mandatory as docket must be compatible with the previous 33 # | This is mandatory as docket must be compatible with the previous
30 # | revlog index header. 34 # | revlog index header.
31 # * 8 bytes: size of index data 35 # * 8 bytes: size of index data
32 S_HEADER = struct.Struct(constants.INDEX_HEADER.format + 'L') 36 # * 8 bytes: pending size of index data
37 S_HEADER = struct.Struct(constants.INDEX_HEADER.format + 'LL')
33 38
34 39
35 class RevlogDocket(object): 40 class RevlogDocket(object):
36 """metadata associated with revlog""" 41 """metadata associated with revlog"""
37 42
38 def __init__(self, revlog, version_header=None, index_end=0): 43 def __init__(
44 self,
45 revlog,
46 use_pending=False,
47 version_header=None,
48 index_end=0,
49 pending_index_end=0,
50 ):
39 self._version_header = version_header 51 self._version_header = version_header
52 self._read_only = bool(use_pending)
40 self._dirty = False 53 self._dirty = False
41 self._radix = revlog.radix 54 self._radix = revlog.radix
42 self._path = revlog._docket_file 55 self._path = revlog._docket_file
43 self._opener = revlog.opener 56 self._opener = revlog.opener
44 self._index_end = index_end 57 # this assert should be True as long as we have a single index filename
58 assert index_end <= pending_index_end
59 self._initial_index_end = index_end
60 self._pending_index_end = pending_index_end
61 if use_pending:
62 self._index_end = self._pending_index_end
63 else:
64 self._index_end = self._initial_index_end
45 65
46 def index_filepath(self): 66 def index_filepath(self):
47 """file path to the current index file associated to this docket""" 67 """file path to the current index file associated to this docket"""
48 # very simplistic version at first 68 # very simplistic version at first
49 return b"%s.idx" % self._radix 69 return b"%s.idx" % self._radix
56 def index_end(self, new_size): 76 def index_end(self, new_size):
57 if new_size != self._index_end: 77 if new_size != self._index_end:
58 self._index_end = new_size 78 self._index_end = new_size
59 self._dirty = True 79 self._dirty = True
60 80
61 def write(self, transaction, stripping=False): 81 def write(self, transaction, pending=False, stripping=False):
62 """write the modification of disk if any 82 """write the modification of disk if any
63 83
64 This make the new content visible to all process""" 84 This make the new content visible to all process"""
65 if self._dirty: 85 if not self._dirty:
86 return False
87 else:
88 if self._read_only:
89 msg = b'writing read-only docket: %s'
90 msg %= self._path
91 raise error.ProgrammingError(msg)
66 if not stripping: 92 if not stripping:
67 # XXX we could, leverage the docket while stripping. However it 93 # XXX we could, leverage the docket while stripping. However it
68 # is not powerfull enough at the time of this comment 94 # is not powerfull enough at the time of this comment
69 transaction.addbackup(self._path, location=b'store') 95 transaction.addbackup(self._path, location=b'store')
70 with self._opener(self._path, mode=b'w', atomictemp=True) as f: 96 with self._opener(self._path, mode=b'w', atomictemp=True) as f:
71 f.write(self._serialize()) 97 f.write(self._serialize(pending=pending))
72 self._dirty = False 98 # if pending we still need to the write final data eventually
99 self._dirty = pending
100 return True
73 101
74 def _serialize(self): 102 def _serialize(self, pending=False):
103 if pending:
104 official_index_end = self._initial_index_end
105 else:
106 official_index_end = self._index_end
107
108 # this assert should be True as long as we have a single index filename
109 assert official_index_end <= self._index_end
75 data = ( 110 data = (
76 self._version_header, 111 self._version_header,
112 official_index_end,
77 self._index_end, 113 self._index_end,
78 ) 114 )
79 return S_HEADER.pack(*data) 115 return S_HEADER.pack(*data)
80 116
81 117
86 docket = RevlogDocket(revlog, version_header=version_header) 122 docket = RevlogDocket(revlog, version_header=version_header)
87 docket._dirty = True 123 docket._dirty = True
88 return docket 124 return docket
89 125
90 126
91 def parse_docket(revlog, data): 127 def parse_docket(revlog, data, use_pending=False):
92 """given some docket data return a docket object for the given revlog""" 128 """given some docket data return a docket object for the given revlog"""
93 header = S_HEADER.unpack(data[: S_HEADER.size]) 129 header = S_HEADER.unpack(data[: S_HEADER.size])
94 version_header, index_size = header 130 version_header, index_size, pending_index_size = header
95 docket = RevlogDocket( 131 docket = RevlogDocket(
96 revlog, 132 revlog,
133 use_pending=use_pending,
97 version_header=version_header, 134 version_header=version_header,
98 index_end=index_size, 135 index_end=index_size,
136 pending_index_end=pending_index_size,
99 ) 137 )
100 return docket 138 return docket