OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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: |
OLD | NEW |