97 |
97 |
98 'start-obs-idx': index of the first obs-markers not in the cache. None is |
98 'start-obs-idx': index of the first obs-markers not in the cache. None is |
99 up to date. |
99 up to date. |
100 """ |
100 """ |
101 |
101 |
102 # XXX ideally, this function would return a bounded amount of changeset and |
102 # XXX we need to ensure we use the same changelog and obsstore through the processing |
103 # obsmarkers and the associated new cache key. Otherwise we are exposed to |
103 |
104 # a race condition between the time the cache is updated and the new cache |
104 reset = False |
105 # key is computed. (however, we do not want to compute the full new cache |
|
106 # key in all case because we want to skip reading the obsstore content. We |
|
107 # could have a smarter implementation here. |
|
108 # |
|
109 # In pratice the cache is only updated after each transaction within a |
|
110 # lock. So we should be fine. We could enforce this with a new repository |
|
111 # requirement (or fix the race, that is not too hard). |
|
112 |
105 |
113 status = _checkkey(repo, key) |
106 status = _checkkey(repo, key) |
114 if status is None: |
107 if status is None: |
115 return (False, 0, 0) |
108 reset = True |
|
109 key = emptykey |
|
110 obssize, obskey = repo.obsstore.cachekey() |
|
111 tiprev = len(repo.changelog) - 1 |
|
112 else: |
|
113 tiprev, obssize, obskey = status |
116 |
114 |
117 keytiprev, keytipnode, keyobslength, keyobssize, keyobskey = key |
115 keytiprev, keytipnode, keyobslength, keyobssize, keyobskey = key |
118 tiprev, obssize, __ = status |
116 |
|
117 if not reset and keytiprev == tiprev and keyobssize == obssize: |
|
118 return None # nothing to upgrade |
119 |
119 |
120 ### cache is valid, is there anything to update |
120 ### cache is valid, is there anything to update |
121 |
121 |
122 # any new changesets ? |
122 # any new changesets ? |
123 startrev = None |
123 revs = () |
124 if keytiprev < tiprev: |
124 if keytiprev < tiprev: |
125 startrev = keytiprev + 1 |
125 revs = list(repo.changelog.revs(start=keytiprev + 1, stop=tiprev)) |
126 |
126 |
127 # any new markers |
127 # any new markers |
128 startidx = None |
128 markers = () |
129 if keyobssize < obssize: |
129 if keyobssize < obssize: |
130 startidx = keyobslength |
130 # XXX Three are a small race change here. Since the obsstore might have |
131 |
131 # move forward between the time we computed the cache key and we access |
132 return True, startrev, startidx |
132 # the data. To fix this we need so "up to" argument when fetching the |
|
133 # markers here. Otherwise we might return more markers than covered by |
|
134 # the cache key. |
|
135 # |
|
136 # In pratice the cache is only updated after each transaction within a |
|
137 # lock. So we should be fine. We could enforce this with a new repository |
|
138 # requirement (or fix the race, that is not too hard). |
|
139 markers = markersfrom(repo.obsstore, keyobssize, keyobslength) |
|
140 |
|
141 return reset, revs, markers, (obssize, obskey) |
133 |
142 |
134 def _checkkey(repo, key): |
143 def _checkkey(repo, key): |
135 """internal function""" |
144 """internal function""" |
136 if key is None: |
145 if key is None: |
137 return None |
146 return None |
165 raise error.Abort(_('parsing obsolete marker: unknown version %r') |
174 raise error.Abort(_('parsing obsolete marker: unknown version %r') |
166 % diskversion) |
175 % diskversion) |
167 return diskversion, obsolete.formats[diskversion][0](data, off) |
176 return diskversion, obsolete.formats[diskversion][0](data, off) |
168 |
177 |
169 def markersfrom(obsstore, byteoffset, firstmarker): |
178 def markersfrom(obsstore, byteoffset, firstmarker): |
170 if '_all' in vars(obsstore): |
179 if not firstmarker: |
|
180 return list(obsstore) |
|
181 elif '_all' in vars(obsstore): |
171 # if the data are in memory, just use that |
182 # if the data are in memory, just use that |
172 return obsstore._all[firstmarker:] |
183 return obsstore._all[firstmarker:] |
173 else: |
184 else: |
174 obsdata = obsstore.svfs.tryread('obsstore') |
185 obsdata = obsstore.svfs.tryread('obsstore') |
175 return _readmarkers(obsdata, byteoffset)[1] |
186 return _readmarkers(obsdata, byteoffset)[1] |
251 """Iteratively update the cache with new repository data""" |
262 """Iteratively update the cache with new repository data""" |
252 # If we do not have any data, try loading from disk |
263 # If we do not have any data, try loading from disk |
253 if self._cachekey is None: |
264 if self._cachekey is None: |
254 self.load(repo) |
265 self.load(repo) |
255 |
266 |
256 valid, startrev, startidx = upgradeneeded(repo, self._cachekey) |
267 assert repo.filtername is None |
257 if not valid or self._cachekey is None: |
268 cl = repo.changelog |
|
269 |
|
270 upgrade = upgradeneeded(repo, self._cachekey) |
|
271 if upgrade is None: |
|
272 return |
|
273 |
|
274 reset, revs, obsmarkers, obskeypair = upgrade |
|
275 if reset or self._cachekey is None: |
258 self.clear(reset=True) |
276 self.clear(reset=True) |
259 |
277 |
260 if startrev is None and startidx is None: |
278 if revs: |
261 return |
279 self._updaterevs(repo, revs) |
262 |
280 assert len(self._data) == len(cl), (len(self._data), len(cl)) |
263 # checks we never run 'update' without a lock |
281 if obsmarkers: |
264 # |
282 self._updatemarkers(repo, obsmarkers) |
265 # There are a potential race condition otherwise, since the repo |
|
266 # "might" have changed side the cache update above. However, this code |
|
267 # will only be running in a lock so we ignore the issue for now. |
|
268 # |
|
269 # Lift this limitation, 'upgradeneeded' should return a bounded amount |
|
270 # of changeset and markers to read with their associated cachekey. see |
|
271 # 'upgradeneeded' for detail. |
|
272 assert repo._currentlock(repo._lockref) is not None |
|
273 |
|
274 # process the new changesets |
|
275 cl = repo.changelog |
|
276 if startrev is not None: |
|
277 self._updaterevs(repo, cl.revs(startrev)) |
|
278 assert len(self._data) == len(cl), (len(self._data), len(cl)) |
|
279 |
|
280 # process the new obsmarkers |
|
281 if startidx is not None: |
|
282 if startidx == 0: # all markers |
|
283 markers = repo.obsstore._all |
|
284 else: |
|
285 markers = markersfrom(repo.obsstore, self._cachekey[3], startidx) |
|
286 self._updatemarkers(repo, markers) |
|
287 |
283 |
288 # update the key from the new data |
284 # update the key from the new data |
289 key = list(self._cachekey) |
285 key = list(self._cachekey) |
290 if startrev is not None: |
286 if revs: |
291 key[0] = len(cl) - 1 |
287 key[0] = len(cl) - 1 |
292 key[1] = cl.node(key[0]) |
288 key[1] = cl.node(key[0]) |
293 if startidx is not None: |
289 if obsmarkers: |
294 key[2] += len(markers) |
290 key[2] += len(obsmarkers) |
295 # XXX still a small race here if repo is not locked |
291 key[3], key[4] = obskeypair |
296 key[3], key[4] = repo.obsstore.cachekey() |
|
297 self._cachekey = tuple(key) |
292 self._cachekey = tuple(key) |
298 |
293 |
299 def _updaterevs(self, repo, revs): |
294 def _updaterevs(self, repo, revs): |
300 """update the cache with new revisions |
295 """update the cache with new revisions |
301 |
296 |