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"""