comparison mercurial/branchmap.py @ 42001:624d6683c705

branchmap: remove the dict interface from the branchcache class (API) The current branchmap computation involves reading the whole branchmap from disk, validating all the nodes even if they are not required. This leads to a lot of time on repos which have large branchmap or a lot of branches. On large repos, this can validate around 1000's of nodes. On some operations, like finding whether a branch exists or not, we don't need to validate all the nodes. Or updating heads for a single branch. Before this patch, branchcache class was having dict interface and it was hard to keep track of reads. This patch removes the dict interface. Upcoming patches will implement lazy loading and validation of data and implement better API's. Differential Revision: https://phab.mercurial-scm.org/D6151
author Pulkit Goyal <pulkit@yandex-team.ru>
date Mon, 18 Mar 2019 18:59:38 +0300
parents 38de3300414f
children 662ffdde5adf
comparison
equal deleted inserted replaced
42000:a66965406528 42001:624d6683c705
125 125
126 def clear(self): 126 def clear(self):
127 self._per_filter.clear() 127 self._per_filter.clear()
128 128
129 129
130 class branchcache(dict): 130 class branchcache(object):
131 """A dict like object that hold branches heads cache. 131 """A dict like object that hold branches heads cache.
132 132
133 This cache is used to avoid costly computations to determine all the 133 This cache is used to avoid costly computations to determine all the
134 branch heads of a repo. 134 branch heads of a repo.
135 135
149 branch head closes a branch or not. 149 branch head closes a branch or not.
150 """ 150 """
151 151
152 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev, 152 def __init__(self, entries=(), tipnode=nullid, tiprev=nullrev,
153 filteredhash=None, closednodes=None): 153 filteredhash=None, closednodes=None):
154 super(branchcache, self).__init__(entries)
155 self.tipnode = tipnode 154 self.tipnode = tipnode
156 self.tiprev = tiprev 155 self.tiprev = tiprev
157 self.filteredhash = filteredhash 156 self.filteredhash = filteredhash
158 # closednodes is a set of nodes that close their branch. If the branch 157 # closednodes is a set of nodes that close their branch. If the branch
159 # cache has been updated, it may contain nodes that are no longer 158 # cache has been updated, it may contain nodes that are no longer
160 # heads. 159 # heads.
161 if closednodes is None: 160 if closednodes is None:
162 self._closednodes = set() 161 self._closednodes = set()
163 else: 162 else:
164 self._closednodes = closednodes 163 self._closednodes = closednodes
164 self.entries = dict(entries)
165
166 def __iter__(self):
167 return iter(self.entries)
168
169 def __setitem__(self, key, value):
170 self.entries[key] = value
171
172 def __getitem__(self, key):
173 return self.entries[key]
174
175 def setdefault(self, *args):
176 return self.entries.setdefault(*args)
177
178 def iteritems(self):
179 return self.entries.iteritems()
180
181 def itervalues(self):
182 return self.entries.itervalues()
165 183
166 @classmethod 184 @classmethod
167 def fromfile(cls, repo): 185 def fromfile(cls, repo):
168 f = None 186 f = None
169 try: 187 try:
269 for bn, heads in self.iteritems(): 287 for bn, heads in self.iteritems():
270 yield (bn, heads) + self._branchtip(heads) 288 yield (bn, heads) + self._branchtip(heads)
271 289
272 def copy(self): 290 def copy(self):
273 """return an deep copy of the branchcache object""" 291 """return an deep copy of the branchcache object"""
274 return type(self)( 292 return branchcache(
275 self, self.tipnode, self.tiprev, self.filteredhash, 293 self.entries, self.tipnode, self.tiprev, self.filteredhash,
276 self._closednodes) 294 self._closednodes)
277 295
278 def write(self, repo): 296 def write(self, repo):
279 try: 297 try:
280 f = repo.cachevfs(self._filename(repo), "w", atomictemp=True) 298 f = repo.cachevfs(self._filename(repo), "w", atomictemp=True)
293 state = 'o' 311 state = 'o'
294 f.write("%s %s %s\n" % (hex(node), state, label)) 312 f.write("%s %s %s\n" % (hex(node), state, label))
295 f.close() 313 f.close()
296 repo.ui.log('branchcache', 314 repo.ui.log('branchcache',
297 'wrote %s branch cache with %d labels and %d nodes\n', 315 'wrote %s branch cache with %d labels and %d nodes\n',
298 repo.filtername, len(self), nodecount) 316 repo.filtername, len(self.entries), nodecount)
299 except (IOError, OSError, error.Abort) as inst: 317 except (IOError, OSError, error.Abort) as inst:
300 # Abort may be raised by read only opener, so log and continue 318 # Abort may be raised by read only opener, so log and continue
301 repo.ui.debug("couldn't write branch cache: %s\n" % 319 repo.ui.debug("couldn't write branch cache: %s\n" %
302 stringutil.forcebytestr(inst)) 320 stringutil.forcebytestr(inst))
303 321
349 367
350 if not self.validfor(repo): 368 if not self.validfor(repo):
351 # cache key are not valid anymore 369 # cache key are not valid anymore
352 self.tipnode = nullid 370 self.tipnode = nullid
353 self.tiprev = nullrev 371 self.tiprev = nullrev
354 for heads in self.values(): 372 for heads in self.itervalues():
355 tiprev = max(cl.rev(node) for node in heads) 373 tiprev = max(cl.rev(node) for node in heads)
356 if tiprev > self.tiprev: 374 if tiprev > self.tiprev:
357 self.tipnode = cl.node(tiprev) 375 self.tipnode = cl.node(tiprev)
358 self.tiprev = tiprev 376 self.tiprev = tiprev
359 self.filteredhash = scmutil.filteredhash(repo, self.tiprev) 377 self.filteredhash = scmutil.filteredhash(repo, self.tiprev)