Mercurial > public > mercurial-scm > hg
comparison mercurial/localrepo.py @ 42512:84aff7e20c55
merge with stable
author | Martin von Zweigbergk <martinvonz@google.com> |
---|---|
date | Fri, 21 Jun 2019 23:35:04 -0700 |
parents | aae93201f758 044045dce23a |
children | 904e0da2e195 |
comparison
equal
deleted
inserted
replaced
42504:a68350a7fc55 | 42512:84aff7e20c55 |
---|---|
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) |
1280 | |
1281 def _refreshchangelog(self): | |
1282 """make sure the in memory changelog match the on-disk one""" | |
1283 if ('changelog' in vars(self) and self.currenttransaction() is None): | |
1284 del self.changelog | |
1231 | 1285 |
1232 @property | 1286 @property |
1233 def _activebookmark(self): | 1287 def _activebookmark(self): |
1234 return self._bookmarks.active | 1288 return self._bookmarks.active |
1235 | 1289 |