Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(274)

Side by Side Diff: third_party/buildbot_8_4p1/buildbot/status/builder.py

Issue 18429003: Utilize buildCache in a smarter way to prevent pathological disk I/O storms. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 # This file is part of Buildbot. Buildbot is free software: you can 1 # This file is part of Buildbot. Buildbot is free software: you can
2 # redistribute it and/or modify it under the terms of the GNU General Public 2 # redistribute it and/or modify it under the terms of the GNU General Public
3 # License as published by the Free Software Foundation, version 2. 3 # License as published by the Free Software Foundation, version 2.
4 # 4 #
5 # This program is distributed in the hope that it will be useful, but WITHOUT 5 # This program is distributed in the hope that it will be useful, but WITHOUT
6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 6 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 7 # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
8 # details. 8 # details.
9 # 9 #
10 # You should have received a copy of the GNU General Public License along with 10 # You should have received a copy of the GNU General Public License along with
11 # this program; if not, write to the Free Software Foundation, Inc., 51 11 # this program; if not, write to the Free Software Foundation, Inc., 51
12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 12 # Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
13 # 13 #
14 # Copyright Buildbot Team Members 14 # Copyright Buildbot Team Members
15 15
16 16
17 import weakref 17 import weakref
18 import gc 18 import gc
19 import os, re, itertools 19 import os, re, itertools
20 import random
20 from cPickle import load, dump 21 from cPickle import load, dump
21 22
22 from zope.interface import implements 23 from zope.interface import implements
23 from twisted.python import log, runtime 24 from twisted.python import log, runtime
24 from twisted.persisted import styles 25 from twisted.persisted import styles
25 from buildbot.process import metrics 26 from buildbot.process import metrics
26 from buildbot import interfaces, util 27 from buildbot import interfaces, util
27 from buildbot.util.lru import SyncLRUCache 28 from buildbot.util.lru import SyncLRUCache
28 from buildbot.status.event import Event 29 from buildbot.status.event import Event
29 from buildbot.status.build import BuildStatus 30 from buildbot.status.build import BuildStatus
(...skipping 286 matching lines...) Expand 10 before | Expand all | Expand 10 after
316 for build in self.generateFinishedBuilds(num_builds=1): 317 for build in self.generateFinishedBuilds(num_builds=1):
317 assert build and build.isFinished, \ 318 assert build and build.isFinished, \
318 'builder %s build %s is not finished' % ( 319 'builder %s build %s is not finished' % (
319 self.getName(), build) 320 self.getName(), build)
320 return build 321 return build
321 return None 322 return None
322 323
323 def getCategory(self): 324 def getCategory(self):
324 return self.category 325 return self.category
325 326
326 def getBuild(self, number): 327 def _resolveBuildNumber(self, number):
327 if number < 0: 328 if number < 0:
328 number = self.nextBuildNumber + number 329 number = self.nextBuildNumber + number
329 if number < 0 or number >= self.nextBuildNumber: 330 if number < 0 or number >= self.nextBuildNumber:
330 return None 331 return None
332 return number
331 333
334 def _safeGetBuild(self, number):
332 try: 335 try:
333 return self.getBuildByNumber(number) 336 return self.getBuildByNumber(number)
334 except IndexError: 337 except IndexError:
335 return None 338 return None
336 339
340 def getBuild(self, number):
341 number = self._resolveBuildNumber(number)
342
343 if number is None:
344 return None
345
346 return self._safeGetBuild(number)
347
348 def getBuilds(self, numbers):
349 """Cache-aware method to get multiple builds.
350
351 Prevents cascading evict/load when multiple builds are requested in
352 succession: requesting build 1 evicts build 2, requesting build 2 evicts
353 build 3, etc.
354
355 We query the buildCache and load hits first, then misses. When loading,
356 we randomize the load order to alleviate the problem when external web
357 requests load builds sequentially (they don't have access to this
358 function).
359 """
360
361 numbers = [self._resolveBuildNumber(x) for x in numbers]
362 valid_numbers = [
363 (i, n) for i, n in enumerate(numbers) if n is not None]
iannucci 2013/07/02 01:15:27 Seems like this filtering could be done in the for
Mike Stip (use stip instead) 2013/07/02 02:18:34 Done.
364
365 # We shuffle here in case external web points access builds
366 # sequentially. Randomizing the LRU pattern will break any eviction
367 # chains.
368 random.shuffle(valid_numbers)
iannucci 2013/07/02 01:15:27 comment is redundant w/ docstring
Mike Stip (use stip instead) 2013/07/02 02:18:34 Done.
369
370 builds = [None] * len(numbers)
371 misses = []
372 for idx, number in valid_numbers:
373 if number in self.buildCache.cache:
374 builds[idx] = self._safeGetBuild(number)
375 else:
376 misses.append((idx, number))
377
378 for idx, number in misses:
379 builds[idx] = self._safeGetBuild(number)
380
381 return builds
382
337 def getEvent(self, number): 383 def getEvent(self, number):
338 try: 384 try:
339 return self.events[number] 385 return self.events[number]
340 except IndexError: 386 except IndexError:
341 return None 387 return None
342 388
343 def generateFinishedBuilds(self, branches=[], 389 def generateFinishedBuilds(self, branches=[],
344 num_builds=None, 390 num_builds=None,
345 max_buildnum=None, 391 max_buildnum=None,
346 finished_before=None, 392 finished_before=None,
(...skipping 262 matching lines...) Expand 10 before | Expand all | Expand 10 after
609 result['basedir'] = os.path.basename(self.basedir) 655 result['basedir'] = os.path.basename(self.basedir)
610 result['category'] = self.category 656 result['category'] = self.category
611 result['slaves'] = self.slavenames 657 result['slaves'] = self.slavenames
612 #result['url'] = self.parent.getURLForThing(self) 658 #result['url'] = self.parent.getURLForThing(self)
613 # TODO(maruel): Add cache settings? Do we care? 659 # TODO(maruel): Add cache settings? Do we care?
614 660
615 # Transient 661 # Transient
616 # Collect build numbers. 662 # Collect build numbers.
617 # Important: Only grab the *cached* builds numbers to reduce I/O. 663 # Important: Only grab the *cached* builds numbers to reduce I/O.
618 current_builds = [b.getNumber() for b in self.currentBuilds] 664 current_builds = [b.getNumber() for b in self.currentBuilds]
665
666 # Populates buildCache with last N builds.
667 buildnums = range(1, self.buildCacheSize - 1)
668 buildnums = [-i for i in buildnums]
iannucci 2013/07/02 01:15:27 Would it be worth having a helper function for thi
Mike Stip (use stip instead) 2013/07/02 02:18:34 well now it's one line...
669 self.getBuilds(buildnums)
670
619 cached_builds = list(set(self.buildCache.cache.keys() + current_builds)) 671 cached_builds = list(set(self.buildCache.cache.keys() + current_builds))
620 cached_builds.sort() 672 cached_builds.sort()
621 result['cachedBuilds'] = cached_builds 673 result['cachedBuilds'] = cached_builds
622 result['currentBuilds'] = current_builds 674 result['currentBuilds'] = current_builds
623 result['state'] = self.getState()[0] 675 result['state'] = self.getState()[0]
624 # lies, but we don't have synchronous access to this info; use 676 # lies, but we don't have synchronous access to this info; use
625 # asDict_async instead 677 # asDict_async instead
626 result['pendingBuilds'] = 0 678 result['pendingBuilds'] = 0
627 return result 679 return result
628 680
629 def asDict_async(self): 681 def asDict_async(self):
630 """Just like L{asDict}, but with a nonzero pendingBuilds.""" 682 """Just like L{asDict}, but with a nonzero pendingBuilds."""
631 result = self.asDict() 683 result = self.asDict()
632 d = self.getPendingBuildRequestStatuses() 684 d = self.getPendingBuildRequestStatuses()
633 def combine(statuses): 685 def combine(statuses):
634 result['pendingBuilds'] = len(statuses) 686 result['pendingBuilds'] = len(statuses)
635 return result 687 return result
636 d.addCallback(combine) 688 d.addCallback(combine)
637 return d 689 return d
638 690
639 def getMetrics(self): 691 def getMetrics(self):
640 return self.botmaster.parent.metrics 692 return self.botmaster.parent.metrics
641 693
642 # vim: set ts=4 sts=4 sw=4 et: 694 # vim: set ts=4 sts=4 sw=4 et:
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698