mercurial/localrepo.py
branchstable
changeset 42682 e0cf09bc35ef
parent 42621 99ebde4fec99
child 42685 863e9e7f8850
equal deleted inserted replaced
42681:a1f10edcf6a6 42682:e0cf09bc35ef
  1225         return cls(self, name, visibilityexceptions)
  1225         return cls(self, name, visibilityexceptions)
  1226 
  1226 
  1227     @mixedrepostorecache(('bookmarks', 'plain'), ('bookmarks.current', 'plain'),
  1227     @mixedrepostorecache(('bookmarks', 'plain'), ('bookmarks.current', 'plain'),
  1228                          ('bookmarks', ''), ('00changelog.i', ''))
  1228                          ('bookmarks', ''), ('00changelog.i', ''))
  1229     def _bookmarks(self):
  1229     def _bookmarks(self):
       
  1230         # Since the multiple files involved in the transaction cannot be
       
  1231         # written atomically (with current repository format), there is a race
       
  1232         # condition here.
       
  1233         #
       
  1234         # 1) changelog content A is read
       
  1235         # 2) outside transaction update changelog to content B
       
  1236         # 3) outside transaction update bookmark file referring to content B
       
  1237         # 4) bookmarks file content is read and filtered against changelog-A
       
  1238         #
       
  1239         # When this happens, bookmarks against nodes missing from A are dropped.
       
  1240         #
       
  1241         # Having this happening during read is not great, but it become worse
       
  1242         # when this happen during write because the bookmarks to the "unknown"
       
  1243         # nodes will be dropped for good. However, writes happen within locks.
       
  1244         # This locking makes it possible to have a race free consistent read.
       
  1245         # For this purpose data read from disc before locking  are
       
  1246         # "invalidated" right after the locks are taken. This invalidations are
       
  1247         # "light", the `filecache` mechanism keep the data in memory and will
       
  1248         # reuse them if the underlying files did not changed. Not parsing the
       
  1249         # same data multiple times helps performances.
       
  1250         #
       
  1251         # Unfortunately in the case describe above, the files tracked by the
       
  1252         # bookmarks file cache might not have changed, but the in-memory
       
  1253         # content is still "wrong" because we used an older changelog content
       
  1254         # to process the on-disk data. So after locking, the changelog would be
       
  1255         # refreshed but `_bookmarks` would be preserved.
       
  1256         # Adding `00changelog.i` to the list of tracked file is not
       
  1257         # enough, because at the time we build the content for `_bookmarks` in
       
  1258         # (4), the changelog file has already diverged from the content used
       
  1259         # for loading `changelog` in (1)
       
  1260         #
       
  1261         # To prevent the issue, we force the changelog to be explicitly
       
  1262         # reloaded while computing `_bookmarks`. The data race can still happen
       
  1263         # without the lock (with a narrower window), but it would no longer go
       
  1264         # undetected during the lock time refresh.
       
  1265         #
       
  1266         # The new schedule is as follow
       
  1267         #
       
  1268         # 1) filecache logic detect that `_bookmarks` needs to be computed
       
  1269         # 2) cachestat for `bookmarks` and `changelog` are captured (for book)
       
  1270         # 3) We force `changelog` filecache to be tested
       
  1271         # 4) cachestat for `changelog` are captured (for changelog)
       
  1272         # 5) `_bookmarks` is computed and cached
       
  1273         #
       
  1274         # The step in (3) ensure we have a changelog at least as recent as the
       
  1275         # cache stat computed in (1). As a result at locking time:
       
  1276         #  * if the changelog did not changed since (1) -> we can reuse the data
       
  1277         #  * otherwise -> the bookmarks get refreshed.
       
  1278         self._refreshchangelog()
  1230         return bookmarks.bmstore(self)
  1279         return bookmarks.bmstore(self)
  1231 
  1280 
  1232     def _refreshchangelog(self):
  1281     def _refreshchangelog(self):
  1233         """make sure the in memory changelog match the on-disk one"""
  1282         """make sure the in memory changelog match the on-disk one"""
  1234         if ('changelog' in vars(self) and self.currenttransaction() is None):
  1283         if ('changelog' in vars(self) and self.currenttransaction() is None):