Within Developer Division at Microsoft, we run a very busy server. We’re up to almost ~3,600 active users.
Just over 6 months ago, the custom tools that our build labs were using were putting an extraordinary strain on the server. More than 40% of the load on the server was just from these tools. After some fairly deep analysis into what the tools were doing and how they were using TFS, we identified some inefficiencies and were able to address them in the tools.
The two most useful resources for tracking down these issues and addressing them were:
- TfsActivityLogging database
- Setting a custom User Agent string
These are examples of the reports that we came up with to track our progress at making the efficiencies. They show total execution time from the TfsActivityLogging database split up by user and by command. You can also select a particular user and drill down and see just their commands. This showed us where our efforts were best spent.
Then the report also shows the details with actual Execution Time, Execution Count and Average Response Time (Time divided by Count) for each command.
Here’s a sample SQL query you can use to start looking at this data yourself:
SUM(ExecutionTime/1000) as TotalTime_ms,
FROM TfsActivityLogging.dbo.tbl_Command with (nolock)
GROUP BY Application, Command
Now I know that as soon as I post this, I’m going to get flooded with comments/emails asking “Can you send me the report file?”. Don’t worry, I’m working with Mario and we should be able to get some goodness posted up on Codeplex.
Setting a custom user agent for the TFS Object Model
Once we had identified which users were contributing the most load, we needed to map their scripts and tools to TFS commands.
One of the columns within the TfsActivityLogging database that gets logged with every request to TFS is the “UserAgent” column. It turns out that you can set this to an arbitrary value using the TeamFoundationServer.ApplicationName property in the TFS object model.
static void Main(string args)
TeamFoundationServer tfs = TeamFoundationServerFactory.GetServer("http://tfs-server:8080", new UICredentialsProvider());
TeamFoundationServer.ApplicationName = "MyApplication v1.1 – Phase1";
VersionControlServer vcs = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
Changeset cs = vcs.GetChangeset(1);
By setting this value dynamically throughout the custom scripts, we were able to map stages in the build process to groups of actual TFS commands. Then we were able to refactor the build scripts and optimize them for better TFS performance.