comparison mercurial/changelog.py @ 50316:87f0155d68aa stable

revlog: improve the robustness of the splitting process The previous "in-place" splitting, preserving the splitting on transaction failure had a couple of issue in case of transaction rollback: - a race windows that could still lead to a crash and data loss - it corrupted the `fncache`. So instead, we use a new approach that we summarized as "we do a backup of the inline revlog pre-split, and we restore this in case of failure". To make readers live easier, we don't overwrite the inline index file until transaction finalization. (once the transaction get into its finalization phase, it is not expected to rollback, unless some crash happens). To do so, we write the index of the split index in a temporary file that we use until transaction finalization. We also keep a backup of the initial inline file to be able to rollback the split if needed. As a result, transaction rollback cancel the split and no longer corrupt fncache. We also no longer have a small inconsistency windows where the transaction could be unrecoverable.
author Pierre-Yves David <pierre-yves.david@octobus.net>
date Mon, 20 Mar 2023 11:52:17 +0100
parents 642e31cb55f0
children c690d2cc7f36
comparison
equal deleted inserted replaced
50315:cf6e1d535602 50316:87f0155d68aa
479 fp.write(b"".join(self._delaybuf)) 479 fp.write(b"".join(self._delaybuf))
480 fp.close() 480 fp.close()
481 self._delaybuf = None 481 self._delaybuf = None
482 self._divert = False 482 self._divert = False
483 # split when we're done 483 # split when we're done
484 self._enforceinlinesize(tr) 484 self._enforceinlinesize(tr, side_write=False)
485 485
486 def _writepending(self, tr): 486 def _writepending(self, tr):
487 """create a file containing the unfinalized state for 487 """create a file containing the unfinalized state for
488 pretxnchangegroup""" 488 pretxnchangegroup"""
489 if self._docket: 489 if self._docket:
510 if self._divert: 510 if self._divert:
511 return True 511 return True
512 512
513 return False 513 return False
514 514
515 def _enforceinlinesize(self, tr): 515 def _enforceinlinesize(self, tr, side_write=True):
516 if not self._delayed: 516 if not self._delayed:
517 revlog.revlog._enforceinlinesize(self, tr) 517 revlog.revlog._enforceinlinesize(self, tr, side_write=side_write)
518 518
519 def read(self, nodeorrev): 519 def read(self, nodeorrev):
520 """Obtain data from a parsed changelog revision. 520 """Obtain data from a parsed changelog revision.
521 521
522 Returns a 6-tuple of: 522 Returns a 6-tuple of: