Fastest way to get list of builds using IBuildServer.QueryBuilds

Posted by Jonathan Rajotte - September 19, 2012

header-picture

As part of my InRelease product development, today I was implementing a simple view to query builds based on a date filter. Something very similar to the Completed tab of the Build Explorer view in Visual Studio.

The C# code to get a list of builds is simple:

var buildSpec = buildServer.CreateBuildDetailSpec(teamProjectName, buildDefinition);
buildSpec.MinFinishTime = DateTime.Now.AddHours(-lastXHours);
var buildDetails = buildServer.QueryBuilds(buildSpec).Builds;

As soon as I ran the code, I noticed a big problem. Even when the search result was returning just a few builds, it was taking a very long time for the call to return. I went over to the VS Build Explorer and, using the exact same filters, the same list was returned in less than a second.

As you may already know, any functionality that access TFS data is all coded through the same TFS API we have access to. I therefore knew that I could get the same performance if somehow I could improve my code.

I started looking at the different members the IBuildDetailSpec interface has to offer and found one very interesting property: InformationTypes

This property drives the amount of information returned by the query! I don’t need the associated work items for the builds, nor the associated changesets or a bunch of other stuff. In my case, all I needed was the build number, the date it was completed and who requested it. I therefore changed my code to the following:

var buildSpec = buildServer.CreateBuildDetailSpec(teamProjectName, buildDefinition);
buildSpec.InformationTypes = null;
buildSpec.MinFinishTime = DateTime.Now.AddHours(-lastXHours);
var buildDetails = buildServer.QueryBuilds(buildSpec).Builds;

My performance was now as good as in the Build Explorer! When InformationTypes is set to null, it does not fetch the entire list of information about the build, making the query so much faster.

My coworker Leo was curious to see just how much improvement this simple line was giving us. Using a PowerShell script, he got the following results:

Before
Fetched 74 builds ... (00:01:54.47)

After
Fetched 74 builds ... (00:00:01.49)

That is 77 times faster!

Here is the script we used for the test:

[Void][Reflection.Assembly]::Load("Microsoft.TeamFoundation.Client, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[Void][Reflection.Assembly]::Load("Microsoft.TeamFoundation.Build.Client, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
[Void][Reflection.Assembly]::Load("Microsoft.TeamFoundation.VersionControl.Client, Version=11.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")

$uri = [URI]http://<SERVER>[:<PORT>]/tfs[/<COLLECTION>
$project = "<PROJECT>"
$buildDefinition = "<BUILD_DEFINITION>"

function Get-ElapsedTime([System.TimeSpan]$ts)
{
return [String]::Format("{0:00}:{1:00}:{2:00}.{3:00}", $ts.Hours, $ts.Minutes, $ts.Seconds, $ts.Milliseconds / 10);
}

$stopWatch = New-Object System.Diagnostics.Stopwatch

# CONNECT
$stopWatch.Reset()
$stopWatch.Start()
$collection = [Microsoft.TeamFoundation.Client.TfsTeamProjectCollectionFactory]::GetTeamProjectCollection($uri)
$stopWatch.Stop()
$elapsedTime = Get-ElapsedTime $stopWatch.Elapsed
"Connecting to TFS ... ($elapsedTime)"

# BUILDS
$stopWatch.Reset()
$stopWatch.Start()
$buildServer = $collection.GetService([Microsoft.TeamFoundation.Build.Client.IBuildServer])
$buildSpec = $buildServer.CreateBuildDetailSpec($project, $buildDefinition)
# $buildSpec.InformationTypes = $null
$builds = $buildServer.QueryBuilds($buildSpec)
$buildCount = $builds.Builds.Count
$stopWatch.Stop()
$elapsedTime = Get-ElapsedTime $stopWatch.Elapsed
"Fetched $buildCount builds ... ($elapsedTime)"

Topics: Blog


Recent Posts

InCycle Recognized Across Americas

read more

InCycle, Microsoft & Cowboys

read more

InCycle Named Azure Data Explorer (ADX) Partner

read more