comparison mercurial/revlog.py @ 51143:849745d7da89 stable

revlog: avoid wrongly updating the data file location on "divert" If we are in the inline case, we need to align the location of the "data" file with the temporary location of the file (i.e. "00changelog.i.a"). However we should not do that for non-inline case? and before this changeset we had been doing it. In addition `index_file` is already a property taking care of updating the "segment file" filename when needed. So we can simply remove all that code. As a result, code trying to read the diverted data before they were committed ended deeply confused as the "00changelog.i.a" file is nothing like the "00changelog.d" file. However nothing corrupted data as all writing where properly handled outside of the "segment file". In "best" cases this small in-memory corruption of the filename when unnoticed until the transaction was committed or rolled back and in the worse case, some data reading was failing during the transaction and resulted in the transaction to be rolled back. However wrong data never reached the disk, so this bug should be have corrupted any repository. This is not catch by tests because most test use a small repository and therefor an inline revlog. In addition the bug only triggers when a changelog read is done in the following "rare" situation: - after some delayed write - after that data have been written in a "divert" file (i.e. `00.changelog.i.a`) - before transaction commit - outside of a "writing" context The issue was introduced in d83d788590a8
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Wed, 06 Dec 2023 16:29:43 +0100
parents 66417f55ea33
children 77b86226dde2 dcaa2df1f688
comparison
equal deleted inserted replaced
51142:66417f55ea33 51143:849745d7da89
1164 # delay or divert already in place 1164 # delay or divert already in place
1165 return None 1165 return None
1166 elif len(self.index) == 0: 1166 elif len(self.index) == 0:
1167 self._orig_index_file = self.index_file 1167 self._orig_index_file = self.index_file
1168 self.index_file = self._divert_index() 1168 self.index_file = self._divert_index()
1169 self._segmentfile.filename = self.index_file
1170 assert self._orig_index_file is not None 1169 assert self._orig_index_file is not None
1171 assert self.index_file is not None 1170 assert self.index_file is not None
1172 if self.opener.exists(self.index_file): 1171 if self.opener.exists(self.index_file):
1173 self.opener.unlink(self.index_file) 1172 self.opener.unlink(self.index_file)
1174 return self.index_file 1173 return self.index_file
1200 self._segmentfile._delay_buffer = self._delay_buffer 1199 self._segmentfile._delay_buffer = self._delay_buffer
1201 else: 1200 else:
1202 assert self._segmentfile._delay_buffer is None 1201 assert self._segmentfile._delay_buffer is None
1203 self._orig_index_file = self.index_file 1202 self._orig_index_file = self.index_file
1204 self.index_file = pending_index_file 1203 self.index_file = pending_index_file
1205 self._segmentfile.filename = self.index_file
1206 return self.index_file, any_pending 1204 return self.index_file, any_pending
1207 1205
1208 def finalize_pending(self): 1206 def finalize_pending(self):
1209 assert not self.is_open 1207 assert not self.is_open
1210 1208
1226 self._orig_index_file, 1224 self._orig_index_file,
1227 checkambig=True, 1225 checkambig=True,
1228 ) 1226 )
1229 self.index_file = self._orig_index_file 1227 self.index_file = self._orig_index_file
1230 self._orig_index_file = None 1228 self._orig_index_file = None
1231 self._segmentfile.filename = self.index_file
1232 else: 1229 else:
1233 msg = b"not delay or divert found on this revlog" 1230 msg = b"not delay or divert found on this revlog"
1234 raise error.ProgrammingError(msg) 1231 raise error.ProgrammingError(msg)
1235 return self.canonical_index_file 1232 return self.canonical_index_file
1236 1233