Skip to content

Commit

Permalink
Remove synchronous db.getBuildRequestWithNumber
Browse files Browse the repository at this point in the history
This caused a number of BuildRequestStatus methods to become async, as
detailed in NEWS.
  • Loading branch information
djmitche committed Apr 27, 2011
1 parent f9a21e1 commit 291820f
Show file tree
Hide file tree
Showing 11 changed files with 133 additions and 84 deletions.
8 changes: 7 additions & 1 deletion master/NEWS
Expand Up @@ -33,13 +33,19 @@ invocations.
*** The `Status.getBuildSets` method now returns its result via Deferred.

*** The `BuilderControl.getPendingBuilds` method has been renamed to
`getPendingBuildRequestControls`. Both now return their results via Deferred.
`getPendingBuildRequestControls`; `BuilderStatus.getPendingBuilds` has been
renamed to `getPendingBuildStatuses`. Both now return their results via
Deferred.

*** The utility method `Builder.getOldesetRequestTime` now returns its result
via a Deferred, and that result is now a DateTime object.

*** The remote BuildSetStatus method `waitForSuccess` is no longer available.

*** The BuildRequestStatus methods `getSubmitTime` and `getSourceStamp` now
return their results via a Deferred. The `asDict` method omits these values,
as it retuns synchronously.

** Scheduler Improvements

*** Nightly scheduler now accepts a change_filter argument
Expand Down
31 changes: 0 additions & 31 deletions master/buildbot/db/connector.py
Expand Up @@ -22,7 +22,6 @@
from buildbot import util
from buildbot.util import collections as bbcollections
from buildbot.sourcestamp import SourceStamp
from buildbot.process.buildrequest import BuildRequest
from buildbot.process.properties import Properties
from buildbot.status.results import SUCCESS, WARNINGS, FAILURE
from buildbot.util.eventual import eventually
Expand Down Expand Up @@ -336,36 +335,6 @@ def _txn_get_properties_from_db(self, t, tablename, idname, id):

# BuildRequest-manipulation methods

def getBuildRequestWithNumber(self, brid, t=None):
assert isinstance(brid, (int, long))
if t:
br = self._txn_getBuildRequestWithNumber(t, brid)
else:
br = self.runInteractionNow(self._txn_getBuildRequestWithNumber,
brid)
return br
def _txn_getBuildRequestWithNumber(self, t, brid):
assert isinstance(brid, (int, long))
t.execute(self.quoteq("SELECT br.buildsetid, bs.reason,"
" bs.sourcestampid, br.buildername,"
" bs.submitted_at, br.priority"
" FROM buildrequests AS br, buildsets AS bs"
" WHERE br.id=? AND br.buildsetid=bs.id"),
(brid,))
r = t.fetchall()
if not r:
return None
(bsid, reason, ssid, builder_name, submitted_at, priority) = r[0]
ss = self.getSourceStampNumberedNow(ssid, t)
properties = self.get_properties_from_db("buildset_properties",
"buildsetid", bsid, t)
br = BuildRequest.oldConstructor(reason, ss, builder_name, properties)
br.submittedAt = submitted_at
br.priority = priority
br.id = brid
br.bsid = bsid
return br

def get_buildername_for_brid(self, brid):
assert isinstance(brid, (int, long))
return self.runInteractionNow(self._txn_get_buildername_for_brid, brid)
Expand Down
26 changes: 13 additions & 13 deletions master/buildbot/interfaces.py
Expand Up @@ -255,18 +255,19 @@ class IBuildRequestStatus(Interface):
finally turned into a Build."""

def getSourceStamp():
"""Return a SourceStamp object which can be used to re-create
the source tree that this build used. This method will
return an absolute SourceStamp if possible, and its results
may change as the build progresses. Specifically, a "HEAD"
build may later be more accurately specified by an absolute
SourceStamp with the specific revision information.
"""
Get a SourceStamp object which can be used to re-create the source tree
that this build used. This method will return an absolute SourceStamp
if possible, and its results may change as the build progresses.
Specifically, a "HEAD" build may later be more accurately specified by
an absolute SourceStamp with the specific revision information.
This method will return None if the source information is no longer
available."""
pass
def getBuilderName():
pass
available.
@returns: SourceStamp via Deferred
"""

def getBuilds():
"""Return a list of IBuildStatus objects for each Build that has been
started in an attempt to satify this BuildRequest."""
Expand All @@ -282,9 +283,8 @@ def subscribe(observer):
def unsubscribe(observer):
"""Unregister the callable that was registered with subscribe()."""
def getSubmitTime():
"""Return the time when this request was submitted"""
def setSubmitTime(t):
"""Sets the time when this request was submitted"""
"""Return the time when this request was submitted. Returns a
Deferred."""


class ISlaveStatus(Interface):
Expand Down
12 changes: 0 additions & 12 deletions master/buildbot/process/buildrequest.py
Expand Up @@ -122,18 +122,6 @@ def fromBrdict(cls, master, brdict):

yield buildrequest # return value

# TODO: This should die when db.connector.getBuildRequestWithNumber does
@classmethod
def oldConstructor(cls, reason, source, builderName, props):
buildrequest = cls()
buildrequest.reason = reason
buildrequest.source = source
buildrequest.buildername = builderName
buildrequest.properties = properties.Properties()
if props:
buildrequest.properties.updateFromProperties(props)
return buildrequest

def canBeMergedWith(self, other):
return self.source.canBeMergedWith(other.source)

Expand Down
3 changes: 2 additions & 1 deletion master/buildbot/status/builder.py
Expand Up @@ -307,7 +307,8 @@ def getPendingBuildRequestStatuses(self):
d = db.buildrequests.getBuildRequests(claimed=False,
buildername=self.name)
def make_statuses(brdicts):
return [BuildRequestStatus(brdict['brid'], self.status, db)
return [BuildRequestStatus(self.name, brdict['brid'],
self.status)
for brdict in brdicts]
d.addCallback(make_statuses)
return d
Expand Down
97 changes: 84 additions & 13 deletions master/buildbot/status/buildrequest.py
Expand Up @@ -16,31 +16,78 @@
from zope.interface import implements
from twisted.internet import defer
from buildbot import interfaces
from buildbot.process import buildrequest
from buildbot.util.eventual import eventually

class BuildRequestStatus:
implements(interfaces.IBuildRequestStatus)

def __init__(self, brid, status, db):
def __init__(self, buildername, brid, status):
self.buildername = buildername
self.brid = brid
self.status = status
self.db = db
self.master = status.master

self._buildrequest = None
self._buildrequest_lock = defer.DeferredLock()

@defer.deferredGenerator
def _getBuildRequest(self):
"""
Get the underlying BuildRequest object for this status. This is a slow
operation!
@returns: BuildRequest instance or None, via Deferred
"""
# this is only set once, so no need to lock if we already have it
if self._buildrequest:
yield self._buildrequest

wfd = defer.waitForDeferred(
self._buildrequest_lock.acquire())
yield wfd
wfd.getResult()

try:
if not self._buildrequest:
wfd = defer.waitForDeferred(
self.master.db.buildrequests.getBuildRequest(self.brid))
yield wfd
brd = wfd.getResult()

wfd = defer.waitForDeferred(
buildrequest.BuildRequest.fromBrdict(self.master, brd))
yield wfd
self._buildrequest = wfd.getResult()
except: # try/finally isn't allowed in generators in older Pythons
self._buildrequest_lock.release()
raise

self._buildrequest_lock.release()

yield self._buildrequest

def buildStarted(self, build):
self.status._buildrequest_buildStarted(build.status)
self.builds.append(build.status)

# methods called by our clients
@defer.deferredGenerator
def getSourceStamp(self):
br = self.db.getBuildRequestWithNumber(self.brid)
return br.source
wfd = defer.waitForDeferred(
self._getBuildRequest())
yield wfd
br = wfd.getResult()

yield br.source

def getBuilderName(self):
br = self.db.getBuildRequestWithNumber(self.brid)
return br.buildername
return self.buildername

def getBuilds(self):
builder = self.status.getBuilder(self.getBuilderName())
builds = []
buildnums = sorted(self.db.get_buildnums_for_brid(self.brid))
buildnums = sorted(self.master.db.get_buildnums_for_brid(self.brid))
for buildnum in buildnums:
bs = builder.getBuild(buildnum)
if bs:
Expand All @@ -55,20 +102,44 @@ def subscribe(self, observer):
def unsubscribe(self, observer):
self.status._buildrequest_unsubscribe(self.brid, observer)

@defer.deferredGenerator
def getSubmitTime(self):
br = self.db.getBuildRequestWithNumber(self.brid)
return br.submittedAt
wfd = defer.waitForDeferred(
self._getBuildRequest())
yield wfd
br = wfd.getResult()

yield br.submittedAt

def asDict(self):
result = {}
# Constant
result['source'] = self.getSourceStamp().asDict()
result['builderName'] = self.getBuilderName()
result['submittedAt'] = self.getSubmitTime()
result['source'] = None # not available sync, sorry
result['builderName'] = self.buildername
result['submittedAt'] = None # not availably sync, sorry

# Transient
result['builds'] = [build.asDict() for build in self.getBuilds()]
return result

@defer.deferredGenerator
def asDict_async(self):
return defer.succeed(self.asDict())
result = {}

wfd = defer.waitForDeferred(
self.getSourceStamp())
yield wfd
ss = wfd.getResult()
result['source'] = ss.asDict()

result['builderName'] = self.getBuilderName()

wfd = defer.waitForDeferred(
self.getSubmitTime())
yield wfd
submittedAt = wfd.getResult()
result['submittedAt'] = submittedAt

result['builds'] = [ build.asDict() for build in self.getBuilds() ]

yield result
9 changes: 5 additions & 4 deletions master/buildbot/status/buildset.py
Expand Up @@ -48,8 +48,9 @@ def getID(self):
def getBuilderNamesAndBuildRequests(self):
brs = {}
brids = self.db.get_buildrequestids_for_buildset(self.id)
for (buildername, brid) in brids.items():
brs[buildername] = BuildRequestStatus(brid, self.status, self.db)
for (buildername, brid) in brids.iteritems():
brs[buildername] = BuildRequestStatus(buildername, brid,
self.status)
return brs

def getBuilderNames(self):
Expand All @@ -58,8 +59,8 @@ def getBuilderNames(self):

def getBuildRequests(self):
brs = self.db.get_buildrequestids_for_buildset(self.id)
return [BuildRequestStatus(brid, self.status, self.db)
for brid in brs.values()]
return [BuildRequestStatus(buildername, brid, self.status)
for buildername, brid in brs.iteritems()]

def isFinished(self):
(external_idstring, reason, ssid, complete, results) = self._get_info()
Expand Down
1 change: 1 addition & 0 deletions master/buildbot/status/client.py
Expand Up @@ -114,6 +114,7 @@ def __init__(self, buildreq):
self.observers = []

def remote_getSourceStamp(self):
# note that this now returns a Deferred
return self.b.getSourceStamp()

def remote_getBuilderName(self):
Expand Down
5 changes: 1 addition & 4 deletions master/buildbot/status/master.py
Expand Up @@ -342,9 +342,6 @@ def buildreqs_retired(self, requests):
# r.bsid: check for completion, notify subscribers, unsubscribe
pass

def get_buildreq_for_id(self, brid):
return buildrequest.BuildRequestStatus(brid, self, self.db)

def _db_builds_changed(self, category, bid):
brid,buildername,buildnum = self.db.get_build_info(bid)
if brid in self._buildreq_observers:
Expand Down Expand Up @@ -400,7 +397,7 @@ def _handle_buildrequest_event(self, mode, brids):
for brid in brids:
buildername = self.db.get_buildername_for_brid(brid)
if buildername in self._builder_observers:
brs = buildrequest.BuildRequestStatus(brid, self, self.db)
brs = buildrequest.BuildRequestStatus(buildername, brid, self)
for observer in self._builder_observers[buildername]:
if mode == "added":
if hasattr(observer, 'requestSubmitted'):
Expand Down
23 changes: 19 additions & 4 deletions master/buildbot/status/web/builder.py
Expand Up @@ -82,9 +82,18 @@ def content(self, req, cxt):
yield wfd
statuses = wfd.getResult()
for pb in statuses:
source = pb.getSourceStamp()
changes = []

wfd = defer.waitForDeferred(
pb.getSourceStamp())
yield wfd
source = wfd.getResult()

wfd = defer.waitForDeferred(
pb.getSubmitTime())
yield wfd
submitTime = wfd.getResult()

if source.changes:
for c in source.changes:
changes.append({ 'url' : path_to_change(req, c),
Expand All @@ -93,8 +102,9 @@ def content(self, req, cxt):
'repo' : c.repository })

cxt['pending'].append({
'when': time.strftime("%b %d %H:%M:%S", time.localtime(pb.getSubmitTime())),
'delay': util.formatInterval(util.now() - pb.getSubmitTime()),
'when': time.strftime("%b %d %H:%M:%S",
time.localtime(submitTime)),
'delay': util.formatInterval(util.now() - submitTime),
'id': pb.brid,
'changes' : changes,
'num_changes' : len(changes),
Expand Down Expand Up @@ -268,9 +278,14 @@ def stopChangeForBuilder(self, req, builder_status, auth_ok=False):
build_req_statuses = wfd.getResult()

for build_req in build_req_statuses:
ss = build_req.getSourceStamp()
wfd = defer.waitForDeferred(
build_req.getSourceStamp())
yield wfd
ss = wfd.getResult()

if not ss.changes:
continue

for change in ss.changes:
if change.number == request_change:
control = build_controls[build_req.brid]
Expand Down
2 changes: 1 addition & 1 deletion master/buildbot/status/words.py
Expand Up @@ -318,7 +318,7 @@ def builderChangedState(self, builderName, state):

def requestSubmitted(self, brstatus):
log.msg('[Contact] BuildRequest %d for %s submitted' %
(brstatus.brid, brstatus.getSourceStamp()))
(brstatus.brid, brstatus.getBuilderName()))

def builderRemoved(self, builderName):
log.msg('[Contact] Builder %s removed' % (builderName))
Expand Down

0 comments on commit 291820f

Please sign in to comment.