Mercurial > public > mercurial-scm > hg
comparison mercurial/obsolete.py @ 17126:8fa8717b47b6
obsolete: write obsolete marker inside a transaction
Marker are now written as soon as possible but within a transaction. Using a
transaction ensure a proper behavior on error and rollback compatibility.
Flush logic are not necessary anymore and are dropped from lock release.
With this changeset, the obsstore is open, written and closed for every single
added marker. This is expected to be highly inefficient and batched write should
be implemented "quickly".
Another issue is that every flush of the file will invalidate the obsstore
filecache and trigger a full re instantiation of the repo.obsstore attribute
(including, reading and parsing entry). This is also expected to be highly
inefficient and proper filecache operation should be implemented "quickly" too.
A side benefit of the filecache issue is that repo.obsstore object is properly
invalidated on transaction abortion.
author | Pierre-Yves David <pierre-yves.david@ens-lyon.org> |
---|---|
date | Wed, 04 Jul 2012 02:21:04 +0200 |
parents | 95d785ccb4e5 |
children | 48c232873a54 |
comparison
equal
deleted
inserted
replaced
17125:95d785ccb4e5 | 17126:8fa8717b47b6 |
---|---|
157 """ | 157 """ |
158 | 158 |
159 def __init__(self, sopener): | 159 def __init__(self, sopener): |
160 self._all = [] | 160 self._all = [] |
161 # new markers to serialize | 161 # new markers to serialize |
162 self._new = [] | |
163 self.precursors = {} | 162 self.precursors = {} |
164 self.successors = {} | 163 self.successors = {} |
165 self.sopener = sopener | 164 self.sopener = sopener |
166 data = sopener.tryread('obsstore') | 165 data = sopener.tryread('obsstore') |
167 if data: | 166 if data: |
172 return iter(self._all) | 171 return iter(self._all) |
173 | 172 |
174 def __nonzero__(self): | 173 def __nonzero__(self): |
175 return bool(self._all) | 174 return bool(self._all) |
176 | 175 |
177 def create(self, prec, succs=(), flag=0, metadata=None): | 176 def create(self, transaction, prec, succs=(), flag=0, metadata=None): |
178 """obsolete: add a new obsolete marker | 177 """obsolete: add a new obsolete marker |
179 | 178 |
180 * ensuring it is hashable | 179 * ensuring it is hashable |
181 * check mandatory metadata | 180 * check mandatory metadata |
182 * encode metadata | 181 * encode metadata |
187 raise ValueError(prec) | 186 raise ValueError(prec) |
188 for succ in succs: | 187 for succ in succs: |
189 if len(succ) != 20: | 188 if len(succ) != 20: |
190 raise ValueError(succ) | 189 raise ValueError(succ) |
191 marker = (str(prec), tuple(succs), int(flag), encodemeta(metadata)) | 190 marker = (str(prec), tuple(succs), int(flag), encodemeta(metadata)) |
192 self.add(marker) | 191 self.add(transaction, marker) |
193 | 192 |
194 def add(self, marker): | 193 def add(self, transaction, marker): |
195 """Add a new marker to the store | 194 """Add a new marker to the store""" |
196 | 195 if marker not in self._all: |
197 This marker still needs to be written to disk""" | |
198 self._new.append(marker) | |
199 self._load(marker) | |
200 | |
201 def mergemarkers(self, data): | |
202 other = set(_readmarkers(data)) | |
203 local = set(self._all) | |
204 new = other - local | |
205 for marker in new: | |
206 self.add(marker) | |
207 | |
208 def flushmarkers(self): | |
209 """Write all markers on disk | |
210 | |
211 After this operation, "new" markers are considered "known".""" | |
212 # XXX: transaction logic should be used | |
213 if self._new: | |
214 f = self.sopener('obsstore', 'ab') | 196 f = self.sopener('obsstore', 'ab') |
215 try: | 197 try: |
216 if f.tell() == 0: | 198 offset = f.tell() |
217 # plain new obsstore | 199 transaction.add('obsstore', offset) |
200 if offset == 0: | |
201 # new file add version header | |
218 f.write(_pack('>B', _fmversion)) | 202 f.write(_pack('>B', _fmversion)) |
219 _writemarkers(f.write, self._new) | 203 _writemarkers(f.write, [marker]) |
204 finally: | |
205 # XXX: f.close() == filecache invalidation == obsstore rebuilt. | |
206 # call 'filecacheentry.refresh()' here | |
220 f.close() | 207 f.close() |
221 self._new[:] = [] | 208 self._load(marker) |
222 except: # re-raises | 209 |
223 f.discard() | 210 def mergemarkers(self, transation, data): |
224 raise | 211 other = _readmarkers(data) |
212 local = set(self._all) | |
213 new = [m for m in other if m not in local] | |
214 for marker in new: | |
215 # XXX: N marker == N x (open, write, close) | |
216 # we should write them all at once | |
217 self.add(transation, marker) | |
225 | 218 |
226 def _load(self, marker): | 219 def _load(self, marker): |
227 self._all.append(marker) | 220 self._all.append(marker) |
228 pre, sucs = marker[:2] | 221 pre, sucs = marker[:2] |
229 self.precursors.setdefault(pre, set()).add(marker) | 222 self.precursors.setdefault(pre, set()).add(marker) |
259 repo.ui.warn(_('unexpected old value') % key) | 252 repo.ui.warn(_('unexpected old value') % key) |
260 return 0 | 253 return 0 |
261 data = base85.b85decode(new) | 254 data = base85.b85decode(new) |
262 lock = repo.lock() | 255 lock = repo.lock() |
263 try: | 256 try: |
264 repo.obsstore.mergemarkers(data) | 257 tr = repo.transaction('pushkey: obsolete markers') |
265 return 1 | 258 try: |
259 repo.obsstore.mergemarkers(tr, data) | |
260 tr.close() | |
261 return 1 | |
262 finally: | |
263 tr.release() | |
266 finally: | 264 finally: |
267 lock.release() | 265 lock.release() |
268 | 266 |
269 def allmarkers(repo): | 267 def allmarkers(repo): |
270 """all obsolete markers known in a repository""" | 268 """all obsolete markers known in a repository""" |