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

Side by Side Diff: third_party/buildbot_8_4p1/README.chromium

Issue 9703108: Switch to the good LRU implementation. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/tools/build/
Patch Set: Created 8 years, 9 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
« no previous file with comments | « no previous file | third_party/buildbot_8_4p1/buildbot/status/builder.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 URL: http://buildbot.net/trac 1 URL: http://buildbot.net/trac
2 Version: 0.8.4p1 2 Version: 0.8.4p1
3 License: GNU General Public License (GPL) Version 2 3 License: GNU General Public License (GPL) Version 2
4 4
5 This is a forked copy of buildbot v0.8.4p1. 5 This is a forked copy of buildbot v0.8.4p1.
6 6
7 7
8 Add extra parameters to HttpStatusPush as a very basic authentication mechanism. 8 Add extra parameters to HttpStatusPush as a very basic authentication mechanism.
9 9
10 diff --git a/third_party/buildbot_8_4p1/buildbot/status/status_push.py b/third_p arty/buildbot_8_4p1/buildbot/status/status_push.py 10 diff --git a/third_party/buildbot_8_4p1/buildbot/status/status_push.py b/third_p arty/buildbot_8_4p1/buildbot/status/status_push.py
(...skipping 1181 matching lines...) Expand 10 before | Expand all | Expand 10 after
1192 + tmp = [] 1192 + tmp = []
1193 + if isinstance(self.command, list): 1193 + if isinstance(self.command, list):
1194 + self._flattenList(tmp, self.command) 1194 + self._flattenList(tmp, self.command)
1195 + else: 1195 + else:
1196 + tmp = self.command 1196 + tmp = self.command
1197 + 1197 +
1198 + kwargs['command'] = tmp 1198 + kwargs['command'] = tmp
1199 kwargs['logfiles'] = self.logfiles 1199 kwargs['logfiles'] = self.logfiles
1200 1200
1201 # check for the usePTY flag 1201 # check for the usePTY flag
1202
1203
1204 Replace the simple (and very slow) LRU cache implementation in BuilderStatus
1205 with a better one. Added SyncLRUCache to avoid the expensive concurrency
1206 protection in AsyncLRUCache.
1207
1208 Index: buildbot/status/builder.py
1209 ===================================================================
1210 --- buildbot/status/builder.py (revision 127129)
1211 +++ buildbot/status/builder.py (working copy)
1212 @@ -86,8 +86,8 @@
1213 self.currentBuilds = []
1214 self.nextBuild = None
1215 self.watchers = []
1216 - self.buildCache = weakref.WeakValueDictionary()
1217 - self.buildCache_LRU = []
1218 + self.buildCache = util.lru.SyncLRUCache(self.cacheMiss,
1219 + self.buildCacheSize)
1220 self.logCompressionLimit = False # default to no compression for tests
1221 self.logCompressionMethod = "bz2"
1222 self.logMaxSize = None # No default limit
1223 @@ -103,7 +103,6 @@
1224 d = styles.Versioned.__getstate__(self)
1225 d['watchers'] = []
1226 del d['buildCache']
1227 - del d['buildCache_LRU']
1228 for b in self.currentBuilds:
1229 b.saveYourself()
1230 # TODO: push a 'hey, build was interrupted' event
1231 @@ -119,8 +118,8 @@
1232 # when loading, re-initialize the transient stuff. Remember that
1233 # upgradeToVersion1 and such will be called after this finishes.
1234 styles.Versioned.__setstate__(self, d)
1235 - self.buildCache = weakref.WeakValueDictionary()
1236 - self.buildCache_LRU = []
1237 + self.buildCache = util.lru.SyncLRUCache(self.cacheMiss,
1238 + self.buildCacheSize)
1239 self.currentBuilds = []
1240 self.watchers = []
1241 self.slavenames = []
1242 @@ -132,6 +131,7 @@
1243 # gets pickled and unpickled.
1244 if buildmaster.buildCacheSize is not None:
1245 self.buildCacheSize = buildmaster.buildCacheSize
1246 + self.buildCache.set_max_size(buildmaster.buildCacheSize)
1247
1248 def upgradeToVersion1(self):
1249 if hasattr(self, 'slavename'):
1250 @@ -186,33 +186,17 @@
1251 except:
1252 log.msg("unable to save builder %s" % self.name)
1253 log.err()
1254 -
1255
1256 +
1257 # build cache management
1258
1259 def makeBuildFilename(self, number):
1260 return os.path.join(self.basedir, "%d" % number)
1261
1262 - def touchBuildCache(self, build):
1263 - self.buildCache[build.number] = build
1264 - if build in self.buildCache_LRU:
1265 - self.buildCache_LRU.remove(build)
1266 - self.buildCache_LRU = self.buildCache_LRU[-(self.buildCacheSize-1):] + [ build ]
1267 - return build
1268 -
1269 def getBuildByNumber(self, number):
1270 - # first look in currentBuilds
1271 - for b in self.currentBuilds:
1272 - if b.number == number:
1273 - return self.touchBuildCache(b)
1274 + return self.buildCache.get(number)
1275
1276 - # then in the buildCache
1277 - if number in self.buildCache:
1278 - metrics.MetricCountEvent.log("buildCache.hits", 1)
1279 - return self.touchBuildCache(self.buildCache[number])
1280 - metrics.MetricCountEvent.log("buildCache.misses", 1)
1281 -
1282 - # then fall back to loading it from disk
1283 + def loadBuildFromFile(self, number):
1284 filename = self.makeBuildFilename(number)
1285 try:
1286 log.msg("Loading builder %s's build %d from on-disk pickle"
1287 @@ -235,12 +219,20 @@
1288 build.upgradeLogfiles()
1289 # check that logfiles exist
1290 build.checkLogfiles()
1291 - return self.touchBuildCache(build)
1292 + return build
1293 except IOError:
1294 raise IndexError("no such build %d" % number)
1295 except EOFError:
1296 raise IndexError("corrupted build pickle %d" % number)
1297
1298 + def cacheMiss(self, number):
1299 + # first look in currentBuilds
1300 + for b in self.currentBuilds:
1301 + if b.number == number:
1302 + return b
1303 + # then fall back to loading it from disk
1304 + return self.loadBuildFromFile(number)
1305 +
1306 def prune(self, events_only=False):
1307 # begin by pruning our own events
1308 self.events = self.events[-self.eventHorizon:]
1309 @@ -287,7 +279,7 @@
1310 is_logfile = True
1311
1312 if num is None: continue
1313 - if num in self.buildCache: continue
1314 + if num in self.buildCache.cache: continue
1315
1316 if (is_logfile and num < earliest_log) or num < earliest_build:
1317 pathname = os.path.join(self.basedir, filename)
1318 @@ -510,7 +502,7 @@
1319 assert s.number == self.nextBuildNumber - 1
1320 assert s not in self.currentBuilds
1321 self.currentBuilds.append(s)
1322 - self.touchBuildCache(s)
1323 + self.buildCache.put(s.number, s)
1324
1325 # now that the BuildStatus is prepared to answer queries, we can
1326 # announce the new build to all our watchers
1327 @@ -620,7 +612,7 @@
1328 # Collect build numbers.
1329 # Important: Only grab the *cached* builds numbers to reduce I/O.
1330 current_builds = [b.getNumber() for b in self.currentBuilds]
1331 - cached_builds = list(set(self.buildCache.keys() + current_builds))
1332 + cached_builds = list(set(self.buildCache.cache.keys() + current_builds) )
1333 cached_builds.sort()
1334 result['cachedBuilds'] = cached_builds
1335 result['currentBuilds'] = current_builds
1336 Index: buildbot/util/lru.py
1337 ===================================================================
1338 --- buildbot/util/lru.py (revision 127129)
1339 +++ buildbot/util/lru.py (working copy)
1340 @@ -244,5 +244,82 @@
1341 log.msg(" got:", sorted(self.refcount.items()))
1342 inv_failed = True
1343
1344 +
1345 +class SyncLRUCache(AsyncLRUCache):
1346 + """
1347 +
1348 + A least-recently-used cache using the same strategy as AsyncLRUCache,
1349 + minus the protections for concurrent access. The motivation for this
1350 + class is to provide a speedier implementation for heavily-used caches
1351 + that don't need the concurrency protections.
1352 +
1353 + The constructor takes the same arguments as the AsyncLRUCache
1354 + constructor, except C{miss_fn} must return the missing value, I{not} a
1355 + deferred.
1356 + """
1357 +
1358 + # utility function to record recent use of this key
1359 + def _ref_key(key):
1360 + refcount = self.refcount
1361 + queue = self.queue
1362 +
1363 + queue.append(key)
1364 + refcount[key] = refcount[key] + 1
1365 +
1366 + # periodically compact the queue by eliminating duplicate keys
1367 + # while preserving order of most recent access. Note that this
1368 + # is only required when the cache does not exceed its maximum
1369 + # size
1370 + if len(queue) > self.max_queue:
1371 + refcount.clear()
1372 + queue_appendleft = queue.appendleft
1373 + queue_appendleft(self.sentinel)
1374 + for k in ifilterfalse(refcount.__contains__,
1375 + iter(queue.pop, self.sentinel)):
1376 + queue_appendleft(k)
1377 + refcount[k] = 1
1378 +
1379 + def get(self, key, **miss_fn_kwargs):
1380 + """
1381 + Fetch a value from the cache by key, invoking C{self.miss_fn(key)} if
1382 + the key is not in the cache.
1383 +
1384 + No protection is provided against concurrent access.
1385 +
1386 + @param key: cache key
1387 + @param **miss_fn_kwargs: keyword arguments to the miss_fn
1388 + @returns: cache value
1389 + """
1390 + cache = self.cache
1391 + weakrefs = self.weakrefs
1392 +
1393 + try:
1394 + result = cache[key]
1395 + self.hits += 1
1396 + self._ref_key(key)
1397 + return result
1398 + except KeyError:
1399 + try:
1400 + result = weakrefs[key]
1401 + self.refhits += 1
1402 + cache[key] = result
1403 + self._ref_key(key)
1404 + return result
1405 + except KeyError:
1406 + pass
1407 +
1408 + # if we're here, we've missed and need to fetch
1409 + self.misses += 1
1410 +
1411 + result = self.miss_fn(key, **miss_fn_kwargs)
1412 + if result is not None:
1413 + cache[key] = result
1414 + weakrefs[key] = result
1415 + self._ref_key(key)
1416 + self._purge()
1417 +
1418 + return result
1419 +
1420 +
1421 # for tests
1422 inv_failed = False
1423 Index: buildbot/test/unit/test_status_builder_cache.py
1424 ===================================================================
1425 --- buildbot/test/unit/test_status_builder_cache.py (revision 0)
1426 +++ buildbot/test/unit/test_status_builder_cache.py (revision 0)
1427 @@ -0,0 +1,60 @@
1428 +# This file is part of Buildbot. Buildbot is free software: you can
1429 +# redistribute it and/or modify it under the terms of the GNU General Public
1430 +# License as published by the Free Software Foundation, version 2.
1431 +#
1432 +# This program is distributed in the hope that it will be useful, but WITHOUT
1433 +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1434 +# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1435 +# details.
1436 +#
1437 +# You should have received a copy of the GNU General Public License along with
1438 +# this program; if not, write to the Free Software Foundation, Inc., 51
1439 +# Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
1440 +#
1441 +# Copyright Buildbot Team Members
1442 +
1443 +import os
1444 +from mock import Mock
1445 +from twisted.trial import unittest
1446 +from buildbot.status import builder, master
1447 +
1448 +class TestBuildStatus(unittest.TestCase):
1449 +
1450 + # that buildstep.BuildStepStatus is never instantiated here should tell you
1451 + # that these classes are not well isolated!
1452 +
1453 + def setupBuilder(self, buildername, category=None):
1454 + b = builder.BuilderStatus(buildername=buildername, category=category)
1455 + # Ackwardly, Status sets this member variable.
1456 + b.basedir = os.path.abspath(self.mktemp())
1457 + os.mkdir(b.basedir)
1458 + # Otherwise, builder.nextBuildNumber is not defined.
1459 + b.determineNextBuildNumber()
1460 + # Must initialize these fields before pickling.
1461 + b.currentBigState = 'idle'
1462 + b.status = 'idle'
1463 + return b
1464 +
1465 + def setupStatus(self, b):
1466 + m = Mock()
1467 + m.buildbotURL = 'http://buildbot:8010/'
1468 + m.basedir = '/basedir'
1469 + s = master.Status(m)
1470 + b.status = s
1471 + return s
1472 +
1473 + def testBuildCache(self):
1474 + b = self.setupBuilder('builder_1')
1475 + builds = []
1476 + for i in xrange(5):
1477 + build = b.newBuild()
1478 + build.setProperty('propkey', 'propval%d' % i, 'test')
1479 + builds.append(build)
1480 + build.buildStarted(build)
1481 + build.buildFinished()
1482 + for build in builds:
1483 + build2 = b.getBuild(build.number)
1484 + self.assertTrue(build2)
1485 + self.assertEqual(build2.number, build.number)
1486 + self.assertEqual(build.getProperty('propkey'),
1487 + 'propval%d' % build.number)
OLDNEW
« no previous file with comments | « no previous file | third_party/buildbot_8_4p1/buildbot/status/builder.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698