<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/xsl" href="../assets/xml/rss.xsl" media="all"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>chris' random ramblings (Posts about buildbot)</title><link>https://atlee.ca/</link><description></description><atom:link href="https://atlee.ca/categories/buildbot.xml" rel="self" type="application/rss+xml"></atom:link><language>en</language><lastBuildDate>Sat, 22 Feb 2025 20:04:32 GMT</lastBuildDate><generator>Nikola (getnikola.com)</generator><docs>http://blogs.law.harvard.edu/tech/rss</docs><item><title>So long Buildbot, and thanks for all the fish</title><link>https://atlee.ca/posts/so-long-buildbot/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;Last week, without a lot of fanfare, we shut off the last of the
&lt;a href="https://buildbot.net/"&gt;Buildbot&lt;/a&gt;
infrastructure here at Mozilla.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://media.giphy.com/media/96quMvXlWpwRO/giphy.gif"&gt;&lt;/p&gt;
&lt;p&gt;Our primary release branches have been &lt;a href="https://atlee.ca/posts/so-long-buildbot/migration-status-3.html"&gt;switched over to taskcluster&lt;/a&gt; for
some time now. We needed to keep buildbot running to support the old
ESR52 branch. With the release of Firefox 60.2.0esr earlier this month,
ESR52 is now officially end-of-life, and therefore so is buildbot here at
Mozilla.&lt;/p&gt;
&lt;p&gt;Looking back in time, the first commits to our
&lt;a href="https://hg.mozilla.org/build/buildbot-configs"&gt;buildbot-configs&lt;/a&gt;
repository was over &lt;strong&gt;10 years ago&lt;/strong&gt; on April 27, 2008 by Ben Hearsum: &lt;a href="https://hg.mozilla.org/build/buildbot-configs/rev/584471372a7e"&gt;"Basic Mozilla2
configs"&lt;/a&gt;.
Buildbot usage at Mozilla actually predates that by at least two years,
Ben was working on &lt;a href="https://wiki.cdot.senecacollege.ca/wiki/Extending_the_Buildbot/Archive"&gt;some
patches&lt;/a&gt; in 2006.&lt;/p&gt;
&lt;p&gt;Earlier in my career here at Mozilla, I was doing a lot of work with
Buildbot, and &lt;a href="https://atlee.ca/categories/buildbot.html"&gt;blogged&lt;/a&gt; quite a bit about our
experiences with it.&lt;/p&gt;
&lt;p&gt;Buildbot served us well, especially in the early days. There really were no
other CI systems at the time that could operate at Mozilla's scale.&lt;/p&gt;
&lt;p&gt;Unfortunately, as we kept increasing the scale of our CI and release
infrastructure, even buildbot started showing some problems. The main
architectural limitations of buildbot we encountered were:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Long lived TCP sessions had to stay connected to specific server
   processes. If the network blipped, or you needed to restart a server,
   then any jobs running on workers were interrupted.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Its monolithic design meant that small components of the project were hard
   to develop independently from each other.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;The database schema used to implement the job queue became a bottleneck
   once we started doing hundreds of thousands of jobs a day.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;On top of that, our configuration for all the various branches and
platforms had grown over the years to a complex set of inheritance rules,
defaults, and overrides. Only a few brave souls outside of RelEng managed
to effectively make changes to these configs.&lt;/p&gt;
&lt;p&gt;Today, much &lt;em&gt;much&lt;/em&gt; more of the CI and release configuration lives &lt;a href="https://hg.mozilla.org/mozilla-central/file/tip/taskcluster/ci"&gt;in
tree&lt;/a&gt;. This has &lt;a href="http://code.v.igoro.us/posts/2016/08/whats-so-special-about-in-tree.html"&gt;many
benefits&lt;/a&gt;
including:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Changes are local to the branches they land on. They ride the trains
   naturally. No need for ugly
   &lt;a href="https://hg.mozilla.org/build/buildbot-configs/file/b89147016685/mozilla/config.py#l2178"&gt;looooooops.&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Developers can self-service most of their own requests. Adding new types
   of tests, or even &lt;a href="https://glandium.org/blog/?p=3888"&gt;changing the
   compiler&lt;/a&gt; are possible without any
   involvement from RelEng!&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Buildbot is dead! Long live &lt;a href="https://taskcluster.net"&gt;taskcluster&lt;/a&gt;!&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><category>releng</category><guid>https://atlee.ca/posts/so-long-buildbot/</guid><pubDate>Thu, 20 Sep 2018 13:24:49 GMT</pubDate></item><item><title>A tale of python profiling and optimizing</title><link>https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;The Release Engineering infrastructure at Mozilla relies heavily on &lt;a class="reference external" href="http://buildbot.net/"&gt;buildbot&lt;/a&gt;
for much of our infrastructure. For various reasons we're running an older
version based on buildbot 0.8.2, which we maintain in our own &lt;a class="reference external" href="http://hg.mozilla.org/build/buildbot"&gt;mercurial
repository&lt;/a&gt;. We have many different masters with all sorts of different
configurations.&lt;/p&gt;
&lt;p&gt;To make sure that we don't break things too often, we wrote a tools called
&lt;a class="reference external" href="http://hg.mozilla.org/build/buildbot-configs/file/ad6a3413a45f/test-masters.sh"&gt;test-masters.sh&lt;/a&gt; that creates local versions of each unique master
configuration and then runs a configuration check on it. Currently there
are 20 unique master configurations to test, and it would take 4 minutes
to run test-masters.sh on all of them on my machine. Recently &lt;a class="reference external" href="https://mozillians.org/en-US/u/sfink/"&gt;sfink&lt;/a&gt; landed &lt;a class="reference external" href="http://hg.mozilla.org/build/buildbot-configs/rev/24c750586403"&gt;some changes&lt;/a&gt;
which would test all the masters in parallel, which brought the time down
from a previously interminable 11 minutes.&lt;/p&gt;
&lt;p&gt;Four minutes is a long time to wait! What's taking so much time?&lt;/p&gt;
&lt;p&gt;My go-to tool for profiling python code is &lt;a class="reference external" href="http://docs.python.org/2/library/profile.html#module-cProfile"&gt;cProfile&lt;/a&gt;. I ended up writing a
small script to do the equivalent of 'buildbot checkconfig':&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-1" name="rest_code_31143fedca14459ab86d22d75c6fc41e-1" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cProfile&lt;/span&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-2" name="rest_code_31143fedca14459ab86d22d75c6fc41e-2" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-3" name="rest_code_31143fedca14459ab86d22d75c6fc41e-3" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;buildbot.scripts.checkconfig&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConfigLoader&lt;/span&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-4" name="rest_code_31143fedca14459ab86d22d75c6fc41e-4" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-5" name="rest_code_31143fedca14459ab86d22d75c6fc41e-5" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadMaster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-6" name="rest_code_31143fedca14459ab86d22d75c6fc41e-6" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ConfigLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configFileName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-7" name="rest_code_31143fedca14459ab86d22d75c6fc41e-7" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_31143fedca14459ab86d22d75c6fc41e-8" name="rest_code_31143fedca14459ab86d22d75c6fc41e-8" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_31143fedca14459ab86d22d75c6fc41e-8"&gt;&lt;/a&gt;&lt;span class="n"&gt;cProfile&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"loadMaster(sys.argv[1])"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"master.prof"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Running this against my buildbot master's master.cfg file will produce a
master.prof file I can load to look at the profile.&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-1" name="rest_code_9fb44879c9274627a7851d3a8ff52745-1" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-1"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;pstats&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-2" name="rest_code_9fb44879c9274627a7851d3a8ff52745-2" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-2"&gt;&lt;/a&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;pstats&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"master.prof"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sort_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'time'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;print_stats&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-3" name="rest_code_9fb44879c9274627a7851d3a8ff52745-3" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-3"&gt;&lt;/a&gt;&lt;span class="n"&gt;Thu&lt;/span&gt; &lt;span class="n"&gt;Nov&lt;/span&gt;  &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="mi"&gt;21&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;25&lt;/span&gt; &lt;span class="mi"&gt;2013&lt;/span&gt;    &lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;prof&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-4" name="rest_code_9fb44879c9274627a7851d3a8ff52745-4" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-5" name="rest_code_9fb44879c9274627a7851d3a8ff52745-5" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-5"&gt;&lt;/a&gt;         &lt;span class="mi"&gt;26601516&lt;/span&gt; &lt;span class="n"&gt;function&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;24716688&lt;/span&gt; &lt;span class="n"&gt;primitive&lt;/span&gt; &lt;span class="n"&gt;calls&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="mf"&gt;439.756&lt;/span&gt; &lt;span class="n"&gt;seconds&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-6" name="rest_code_9fb44879c9274627a7851d3a8ff52745-6" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-6"&gt;&lt;/a&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-7" name="rest_code_9fb44879c9274627a7851d3a8ff52745-7" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-7"&gt;&lt;/a&gt;   &lt;span class="n"&gt;Ordered&lt;/span&gt; &lt;span class="n"&gt;by&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;internal&lt;/span&gt; &lt;span class="n"&gt;time&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-8" name="rest_code_9fb44879c9274627a7851d3a8ff52745-8" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-8"&gt;&lt;/a&gt;   &lt;span class="n"&gt;List&lt;/span&gt; &lt;span class="n"&gt;reduced&lt;/span&gt; &lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="mi"&gt;1997&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt; &lt;span class="n"&gt;due&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;restriction&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-9" name="rest_code_9fb44879c9274627a7851d3a8ff52745-9" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-9"&gt;&lt;/a&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-10" name="rest_code_9fb44879c9274627a7851d3a8ff52745-10" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-10"&gt;&lt;/a&gt;   &lt;span class="n"&gt;ncalls&lt;/span&gt;  &lt;span class="n"&gt;tottime&lt;/span&gt;  &lt;span class="n"&gt;percall&lt;/span&gt;  &lt;span class="n"&gt;cumtime&lt;/span&gt;  &lt;span class="n"&gt;percall&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;lineno&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-11" name="rest_code_9fb44879c9274627a7851d3a8ff52745-11" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-11"&gt;&lt;/a&gt;        &lt;span class="mi"&gt;1&lt;/span&gt;  &lt;span class="mf"&gt;409.381&lt;/span&gt;  &lt;span class="mf"&gt;409.381&lt;/span&gt;  &lt;span class="mf"&gt;438.936&lt;/span&gt;  &lt;span class="mf"&gt;438.936&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;catlee&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;virtualenvs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;080&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.8.2&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-12" name="rest_code_9fb44879c9274627a7851d3a8ff52745-12" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-12"&gt;&lt;/a&gt;&lt;span class="n"&gt;hg_b4673f1f2a86_default&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;egg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;621&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-13" name="rest_code_9fb44879c9274627a7851d3a8ff52745-13" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-13"&gt;&lt;/a&gt;   &lt;span class="mi"&gt;170046&lt;/span&gt;    &lt;span class="mf"&gt;3.907&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;   &lt;span class="mf"&gt;10.706&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;catlee&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;virtualenvs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;080&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.8.2&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-14" name="rest_code_9fb44879c9274627a7851d3a8ff52745-14" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-14"&gt;&lt;/a&gt;&lt;span class="n"&gt;hg_b4673f1f2a86_default&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;egg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;steps&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;shell&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;65&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-15" name="rest_code_9fb44879c9274627a7851d3a8ff52745-15" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-15"&gt;&lt;/a&gt;   &lt;span class="mi"&gt;222809&lt;/span&gt;    &lt;span class="mf"&gt;3.745&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;4.124&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;catlee&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;virtualenvs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;080&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.8.2&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-16" name="rest_code_9fb44879c9274627a7851d3a8ff52745-16" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-16"&gt;&lt;/a&gt;&lt;span class="n"&gt;hg_b4673f1f2a86_default&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;egg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildstep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;611&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-17" name="rest_code_9fb44879c9274627a7851d3a8ff52745-17" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-17"&gt;&lt;/a&gt;        &lt;span class="mi"&gt;1&lt;/span&gt;    &lt;span class="mf"&gt;3.025&lt;/span&gt;    &lt;span class="mf"&gt;3.025&lt;/span&gt;   &lt;span class="mf"&gt;29.352&lt;/span&gt;   &lt;span class="mf"&gt;29.352&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;catlee&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;configs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;tests&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;master&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cfg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;module&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-18" name="rest_code_9fb44879c9274627a7851d3a8ff52745-18" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-18"&gt;&lt;/a&gt;   &lt;span class="mi"&gt;170046&lt;/span&gt;    &lt;span class="mf"&gt;2.385&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt;    &lt;span class="mf"&gt;6.033&lt;/span&gt;    &lt;span class="mf"&gt;0.000&lt;/span&gt; &lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;home&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;catlee&lt;/span&gt;&lt;span class="o"&gt;/.&lt;/span&gt;&lt;span class="n"&gt;virtualenvs&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mozilla&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;080&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;lib&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;python2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;site&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;packages&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.8.2&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;
&lt;a id="rest_code_9fb44879c9274627a7851d3a8ff52745-19" name="rest_code_9fb44879c9274627a7851d3a8ff52745-19" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_9fb44879c9274627a7851d3a8ff52745-19"&gt;&lt;/a&gt;&lt;span class="n"&gt;hg_b4673f1f2a86_default&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;py2&lt;/span&gt;&lt;span class="mf"&gt;.6&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;egg&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildbot&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;process&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;buildstep&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;py&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;1027&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="fm"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Looks like buildbot's loadConfig is taking a long time! Unfortunately we
don't get any more detail than that from cProfile. To get line-by-line
profiling info, I've started using &lt;a class="reference external" href="http://pythonhosted.org/line_profiler/"&gt;kernprof&lt;/a&gt;. This requires a few changes
to our setup. First, we don't want to run cProfile any more, so modify our
test script like this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-1" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-1" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-1"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;cProfile&lt;/span&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-2" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-2" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-2"&gt;&lt;/a&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="nn"&gt;sys&lt;/span&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-3" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-3" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-3"&gt;&lt;/a&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="nn"&gt;buildbot.scripts.checkconfig&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;ConfigLoader&lt;/span&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-4" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-4" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-4"&gt;&lt;/a&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-5" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-5" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-5"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadMaster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-6" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-6" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-6"&gt;&lt;/a&gt;    &lt;span class="n"&gt;ConfigLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;configFileName&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-7" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-7" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-7"&gt;&lt;/a&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-8" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-8" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-8"&gt;&lt;/a&gt;&lt;span class="c1"&gt;#cProfile.run("loadMaster(sys.argv[1])", "master.prof")&lt;/span&gt;
&lt;a id="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-9" name="rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-9" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_5d0b54f5dfcb4ed2b51d7e14f729b0ed-9"&gt;&lt;/a&gt;&lt;span class="n"&gt;loadMaster&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;And since we want to get line-by-line profiling of buildbot's loadConfig,
we need to annotate buildbot's code with the @profile decorator. In
buildbot's master.py, I added @profile to loadConfig so it looks like this
now:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code python"&gt;&lt;a id="rest_code_13966a9fa5b64b85a60e88dfc06391fc-1" name="rest_code_13966a9fa5b64b85a60e88dfc06391fc-1" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_13966a9fa5b64b85a60e88dfc06391fc-1"&gt;&lt;/a&gt;&lt;span class="nd"&gt;@profile&lt;/span&gt;
&lt;a id="rest_code_13966a9fa5b64b85a60e88dfc06391fc-2" name="rest_code_13966a9fa5b64b85a60e88dfc06391fc-2" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_13966a9fa5b64b85a60e88dfc06391fc-2"&gt;&lt;/a&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;loadConfig&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="bp"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;f&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;check_synchronously_only&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="kc"&gt;False&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
&lt;a id="rest_code_13966a9fa5b64b85a60e88dfc06391fc-3" name="rest_code_13966a9fa5b64b85a60e88dfc06391fc-3" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_13966a9fa5b64b85a60e88dfc06391fc-3"&gt;&lt;/a&gt;&lt;span class="w"&gt;    &lt;/span&gt;&lt;span class="sd"&gt;"""Internal function to load a specific configuration file. Any&lt;/span&gt;
&lt;a id="rest_code_13966a9fa5b64b85a60e88dfc06391fc-4" name="rest_code_13966a9fa5b64b85a60e88dfc06391fc-4" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_13966a9fa5b64b85a60e88dfc06391fc-4"&gt;&lt;/a&gt;&lt;span class="sd"&gt;    errors in the file will be signalled by raising an exception.&lt;/span&gt;
&lt;a id="rest_code_13966a9fa5b64b85a60e88dfc06391fc-5" name="rest_code_13966a9fa5b64b85a60e88dfc06391fc-5" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_13966a9fa5b64b85a60e88dfc06391fc-5"&gt;&lt;/a&gt;&lt;span class="sd"&gt;    &amp;lt;snip&amp;gt;&lt;/span&gt;
&lt;a id="rest_code_13966a9fa5b64b85a60e88dfc06391fc-6" name="rest_code_13966a9fa5b64b85a60e88dfc06391fc-6" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_13966a9fa5b64b85a60e88dfc06391fc-6"&gt;&lt;/a&gt;&lt;span class="sd"&gt;    """&lt;/span&gt;
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now run kernprof.py to get our profile:&lt;/p&gt;
&lt;pre class="literal-block"&gt;kernprof.py -l -v ../profile_master.py master.cfg

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   621                                               @profile
   622                                               def loadConfig(self, f, check_synchronously_only=False):
   623                                                   """Internal function to load a specific configuration file. Any
   624                                                   errors in the file will be signalled by raising an exception.
   625
   626                                                   If check_synchronously_only=True, I will return (with None)
   627                                                   synchronously, after checking the config file for sanity, or raise an
   628                                                   exception. I may also emit some DeprecationWarnings.
   629
   630                                                   If check_synchronously_only=False, I will return a Deferred that
   631                                                   fires (with None) when the configuration changes have been completed.
   632                                                   This may involve a round-trip to each buildslave that was involved."""
   633
   634         1           17     17.0      0.0          localDict = {'basedir': os.path.expanduser(self.basedir)}
   635         1            7      7.0      0.0          try:
   636         1     68783020 68783020.0     12.0              exec f in localDict
   637                                                   except:
   638                                                       log.msg("error while parsing config file")
   639                                                       raise

   &amp;lt;snip&amp;gt;

   785     13303        86781      6.5      0.0          for b in builders:
   786     13302        92920      7.0      0.0              if b.has_key('slavename') and b['slavename'] not in slavenames:
   787                                                           raise ValueError("builder %s uses undefined slave %s" \
   788                                                                            % (b['name'], b['slavename']))
   789   6935914     42782768      6.2      7.5              for n in b.get('slavenames', []):
   790   6922612    449928915     65.0     78.4                  if n not in slavenames:
   791                                                               raise ValueError("builder %s uses undefined slave %s" \
   792                                                                                % (b['name'], n))
   793     13302      2478517    186.3      0.4              if b['name'] in buildernames:&lt;/pre&gt;
&lt;p&gt;Wow! Line 790 is taking &lt;em&gt;78%&lt;/em&gt; of our runtime! What's going on?&lt;/p&gt;
&lt;p&gt;If I look at my config, I see that I have 13,302 builders and 13,988 slaves
configured. Each builder has a subset of slaves attached, but we're still doing
6,922,612 checks to see if each slave that's configured for the builder is
configured as a top-level slave. &lt;cite&gt;slavenames&lt;/cite&gt; happens to be a list, so each
check does a full scan of the list to see if the slave exists or not!&lt;/p&gt;
&lt;p&gt;A very simple patch fixes this:&lt;/p&gt;
&lt;div class="code"&gt;&lt;pre class="code diff"&gt;&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-1" name="rest_code_f309dd85a6ef49ada13d029020e87655-1" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-1"&gt;&lt;/a&gt;&lt;span class="gh"&gt;diff --git a/master/buildbot/master.py b/master/buildbot/master.py&lt;/span&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-2" name="rest_code_f309dd85a6ef49ada13d029020e87655-2" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-2"&gt;&lt;/a&gt;&lt;span class="gd"&gt;--- a/master/buildbot/master.py&lt;/span&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-3" name="rest_code_f309dd85a6ef49ada13d029020e87655-3" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-3"&gt;&lt;/a&gt;&lt;span class="gi"&gt;+++ b/master/buildbot/master.py&lt;/span&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-4" name="rest_code_f309dd85a6ef49ada13d029020e87655-4" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-4"&gt;&lt;/a&gt;&lt;span class="gu"&gt;@@ -761,19 +761,19 @@ class BuildMaster(service.MultiService):&lt;/span&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-5" name="rest_code_f309dd85a6ef49ada13d029020e87655-5" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-5"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        errmsg = "c['schedulers'] must be a list of Scheduler instances"
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-6" name="rest_code_f309dd85a6ef49ada13d029020e87655-6" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-6"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        assert isinstance(schedulers, (list, tuple)), errmsg
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-7" name="rest_code_f309dd85a6ef49ada13d029020e87655-7" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-7"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        for s in schedulers:
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-8" name="rest_code_f309dd85a6ef49ada13d029020e87655-8" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-8"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;            assert interfaces.IScheduler(s, None), errmsg
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-9" name="rest_code_f309dd85a6ef49ada13d029020e87655-9" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-9"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        assert isinstance(status, (list, tuple))
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-10" name="rest_code_f309dd85a6ef49ada13d029020e87655-10" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-10"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        for s in status:
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-11" name="rest_code_f309dd85a6ef49ada13d029020e87655-11" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-11"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;            assert interfaces.IStatusReceiver(s, None)
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-12" name="rest_code_f309dd85a6ef49ada13d029020e87655-12" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-12"&gt;&lt;/a&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-13" name="rest_code_f309dd85a6ef49ada13d029020e87655-13" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-13"&gt;&lt;/a&gt;&lt;span class="gd"&gt;-        slavenames = [s.slavename for s in slaves]&lt;/span&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-14" name="rest_code_f309dd85a6ef49ada13d029020e87655-14" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-14"&gt;&lt;/a&gt;&lt;span class="gi"&gt;+        slavenames = set(s.slavename for s in slaves)&lt;/span&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-15" name="rest_code_f309dd85a6ef49ada13d029020e87655-15" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-15"&gt;&lt;/a&gt;
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-16" name="rest_code_f309dd85a6ef49ada13d029020e87655-16" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-16"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        # convert builders from objects to config dictionaries
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-17" name="rest_code_f309dd85a6ef49ada13d029020e87655-17" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-17"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        builders_dicts = []
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-18" name="rest_code_f309dd85a6ef49ada13d029020e87655-18" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-18"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;        for b in builders:
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-19" name="rest_code_f309dd85a6ef49ada13d029020e87655-19" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-19"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;            if isinstance(b, BuilderConfig):
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-20" name="rest_code_f309dd85a6ef49ada13d029020e87655-20" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-20"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;                builders_dicts.append(b.getConfigDict())
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-21" name="rest_code_f309dd85a6ef49ada13d029020e87655-21" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-21"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;            elif type(b) is dict:
&lt;a id="rest_code_f309dd85a6ef49ada13d029020e87655-22" name="rest_code_f309dd85a6ef49ada13d029020e87655-22" href="https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/#rest_code_f309dd85a6ef49ada13d029020e87655-22"&gt;&lt;/a&gt;&lt;span class="w"&gt; &lt;/span&gt;                builders_dicts.append(b)
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;Now our checks are into a set instead of a list, which is an O(log(n))
operation instead of O(n). Let's re-run our profile with this patch:&lt;/p&gt;
&lt;pre class="literal-block"&gt;File: /home/catlee/.virtualenvs/buildbot-mozilla-080/lib/python2.6/site-packages/buildbot-0.8.2_hg_b4673f1f2a86_default-py2.6.egg/buildbot/master.py
Function: loadConfig at line 621
Total time: 109.885 s

Line #      Hits         Time  Per Hit   % Time  Line Contents
==============================================================
   621                                               @profile
   622                                               def loadConfig(self, f, check_synchronously_only=False):
   623                                                   """Internal function to load a specific configuration file. Any
   624                                                   errors in the file will be signalled by raising an exception.
   625
   626                                                   If check_synchronously_only=True, I will return (with None)
   627                                                   synchronously, after checking the config file for sanity, or raise an
   628                                                   exception. I may also emit some DeprecationWarnings.
   629
   630                                                   If check_synchronously_only=False, I will return a Deferred that
   631                                                   fires (with None) when the configuration changes have been completed.
   632                                                   This may involve a round-trip to each buildslave that was involved."""
   633
   634         1           30     30.0      0.0          localDict = {'basedir': os.path.expanduser(self.basedir)}
   635         1           13     13.0      0.0          try:
   636         1     46268025 46268025.0     42.1              exec f in localDict
   637                                                   except:
   638                                                       log.msg("error while parsing config file")
   639                                                       raise

   &amp;lt;snip&amp;gt;

   785     13303        56199      4.2      0.1          for b in builders:
   786     13302        60438      4.5      0.1              if b.has_key('slavename') and b['slavename'] not in slavenames:
   787                                                           raise ValueError("builder %s uses undefined slave %s" \
   788                                                                            % (b['name'], b['slavename']))
   789   6935914     27430456      4.0     25.0              for n in b.get('slavenames', []):
   790   6922612     29028880      4.2     26.4                  if n not in slavenames:
   791                                                               raise ValueError("builder %s uses undefined slave %s" \
   792                                                                                % (b['name'], n))
   793     13302      1286628     96.7      1.2              if b['name'] in buildernames:
   794                                                           raise ValueError("duplicate builder name %s"
   795                                                                            % b['name'])&lt;/pre&gt;
&lt;p&gt;We're down to 25% of the runtime for this check now. If we apply the same treatment to a few of the other data structures here, we get the total time for test-masters down to 45 seconds!&lt;/p&gt;
&lt;p&gt;I've landed &lt;a class="reference external" href="http://hg.mozilla.org/build/buildbot/rev/7189e0a6bead"&gt;resulting patch&lt;/a&gt; into our repo. I encourage you to upgrade!&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><guid>https://atlee.ca/posts/a-tale-of-python-profiling-and-optimizing/</guid><pubDate>Fri, 08 Nov 2013 00:05:42 GMT</pubDate></item><item><title>self-serve builds!</title><link>https://atlee.ca/posts/blog20110217self-serve-builds/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;Do you want to be able to cancel your own try server builds?


Do you want to be able to re-trigger a failed nightly build before the RelEng sheriff wakes up?



Do you want to be able to get additional test runs on your build?



If you answered an enthusiastic &lt;strong&gt;YES&lt;/strong&gt; to any or all of these questions, then &lt;a href="https://build.mozilla.org/buildapi/self-serve"&gt;self-serve&lt;/a&gt; is for you.



self-serve was created to provide an API to allow developers to interact with our build infrastructure, with the goal being that others would then create tools against it. It's still early days for this self-serve API, so just a few caveats:



&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;strong&gt;This is very much pre-alpha&lt;/strong&gt; and may cause your computer to explode, your keg to run dry, or may simply hang.&lt;/li&gt;



&lt;li&gt;It's slower than I want. I've spent a bit of time optimizing and caching, but I think it can be much better. Just look at shaver's &lt;a href="http://shaver.off.net/diary/2011/01/22/i-made-a-thing/"&gt;bugzilla search&lt;/a&gt; to see what's possible for speed. Part of the problem here is that it's currently running on a VM that's doing a few dozen other things. We're working on getting faster hardware, but didn't want to block this pre-alpha-rollout on that.&lt;/li&gt;



&lt;li&gt;You need to log in with your LDAP credentials to work with it.&lt;/li&gt;



&lt;li&gt;The HTML interface is teh suck. Good thing I'm not paid to be a front-end webdev! Really, the goal here wasn't to create a fully functional web interface, but rather to provide a functional &lt;em&gt;programmatic&lt;/em&gt; interface.&lt;/li&gt;



&lt;li&gt;Changing build priorities may run afoul of &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=555664"&gt;bug 555664&lt;/a&gt;...haven't had a chance to test out exactly what happens right now if a high priority job gets merged with a lower priority one.&lt;/li&gt;



&lt;/ul&gt;



That being said, I'm proud to be able to finally make this public. Documentation for the REST API is available as part of the web interface itself, and the code is available as part of the &lt;a href="http://hg.mozilla.org/build/buildapi"&gt;buildapi&lt;/a&gt; repository on hg.mozilla.org



&lt;a href="https://build.mozilla.org/buildapi/self-serve"&gt;https://build.mozilla.org/buildapi/self-serve&lt;/a&gt;



Please be gentle!



Any questions, problems or feedback can be left here, or filed in &lt;a href="https://bugzilla.mozilla.org/enter_bug.cgi?product=mozilla.org&amp;amp;component=Release%20Engineering"&gt;bugzilla.&lt;/a&gt;</description><category>buildbot</category><category>firefox</category><category>mozilla</category><category>python</category><category>utilities</category><category>work</category><guid>https://atlee.ca/posts/blog20110217self-serve-builds/</guid><pubDate>Thu, 17 Feb 2011 23:14:26 GMT</pubDate></item><item><title>Faster try builds!</title><link>https://atlee.ca/posts/blog20110204faster-try-builds/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;When we run a try build, we wipe out the build directory between each job; we want to make sure that every user's build has a fresh environment to build in.


Unfortunately this means that we also wipe out the clone of the try repo, and so we have to re-clone try every time.



On Linux and OSX we were spending an average of 30 minutes to re-clone try, and on Windows 40 minutes.  The majority of that is simply 'hg clone' time, but a good portion is due to locks: we need to limit how many simultaneous build slaves are cloning from try at once, otherwise the hg server blows up.



Way back in September, Steve Fink &lt;a href="http://groups.google.com/group/mozilla.dev.builds/msg/e2ec789a9011cf3a"&gt;suggested using hg's share extension&lt;/a&gt; to make cloning faster.



Then in November, &lt;a href="http://blog.mozilla.com/bhearsum/"&gt;Ben Hearsum&lt;/a&gt; &lt;a href="http://groups.google.com/group/mozilla.dev.tree-management/browse_thread/thread/1a984d8bd828e9e7/0f0382923aa9ecc6"&gt;landed some changes&lt;/a&gt; that paved the way to actually turning this on.



Today we've enabled the share extension for Linux (both 32 and 64-bit) and OSX 10.6 builds on try. Windows and OSX 10.5 are coming too, we need to &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=605292"&gt;upgrade hg on the build machines first&lt;/a&gt;.



Average times for the 'clone' step are down to &lt;strong&gt;less than 5 minutes&lt;/strong&gt; now.



This means you get your builds &lt;strong&gt;25 minutes faster!&lt;/strong&gt;  It also means we're not hammering the try repo so badly, and so hopefully won't have to reset it for a long long time.



We're planning on rolling this out across the board, so nightly builds get faster, release builds get faster, clobber builds get faster, etc...



Enjoy!&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><category>performance</category><category>python</category><category>work</category><guid>https://atlee.ca/posts/blog20110204faster-try-builds/</guid><pubDate>Fri, 04 Feb 2011 22:55:41 GMT</pubDate></item><item><title>Better nightly builds</title><link>https://atlee.ca/posts/blog20101208better-nightly-builds/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;On &lt;a href="http://groups.google.com/group/mozilla.dev.tree-management/browse_thread/thread/578d0eeaf0286c0f/65887c7805a6f535?#65887c7805a6f535"&gt;November 24th&lt;/a&gt; we landed some changes we think are a big improvement to how we've been doing nightly builds.


We've started doing nightly builds on the &lt;strong&gt;same revision&lt;/strong&gt; across platforms, and where possible, on revisions that &lt;strong&gt;we already know compile&lt;/strong&gt;.  In addition, all of the nightly builds will share the &lt;strong&gt;same buildid&lt;/strong&gt;.



We pick the revision to build by looking at the past 24 hours of builds, and finding the latest one that built successfully on all platforms.  If there is no such revision, we'll build the latest revision on the branch and hope for the best.  We also do some extra checking to make sure we don't build a revision that's older than the previous nightly.



Prior to this, we would trigger the nightly builds at the same time every day, but which revision and buildid each platform got was completely dependent on what time the builds actually started.  There were many instances of nightly builds having different revisions between the various platforms.



These changes are a big deal because they mean that we have a much better chance of getting a working nightly build every day (or at least one that compiles!), and all the platforms will be based on the same revision, regardless of when they are run.  No more guessing if today's Windows build includes the same fixes as the Mac build!



If you're interested in the implementation, which includes some pretty fun buildbot hacking, the details are in &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=570814"&gt;bug 570814&lt;/a&gt;.  Background discussion as to how to choose the revision to use can be found in &lt;a href="http://groups.google.com/group/mozilla.dev.tree-management/browse_thread/thread/98d2431051edc7bd/13589ae247a22e79?#13589ae247a22e79"&gt;this thread&lt;/a&gt;.&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><category>python</category><guid>https://atlee.ca/posts/blog20101208better-nightly-builds/</guid><pubDate>Wed, 08 Dec 2010 20:18:03 GMT</pubDate></item><item><title>3 days of fun: a journey into the bowels of buildbot</title><link>https://atlee.ca/posts/blog201010293-days-of-fun-a-journey-into-the-bowels-of-buildbot/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;I've just spent 3 days trying to debug some new code in buildbot.


The code in question is to implement a change to how we do nightly builds such that they &lt;a href="https://bugzilla.mozilla.org/show_bug.cgi?id=570814"&gt;use the same revision for all platforms.&lt;/a&gt;



I was hitting a KeyError exception &lt;a href="http://hg.mozilla.org/build/buildbot/file/58225ba72678/master/buildbot/util/loop.py#l153"&gt;inside buildbot's util.loop code&lt;/a&gt;, specifically at a line where it is trying to delete a key from a dictionary.  In simple form, the loop is doing this:



&lt;/p&gt;&lt;pre lang="python"&gt;

for k in d.keys():
    if condition:
        del d[k] # Raises KeyError....sometimes...
&lt;/pre&gt;



Tricky bit was, it didn't happen every time.  I'd have to wait at least 3 minutes between attempts.



So I added a bunch of debugging code:



&lt;pre lang="python"&gt;

print d

print d.keys()

for k in d.keys():
    print k
    if condition:
        try:
            del d[k] # Raises KeyError....sometimes...
        except KeyError:
            print k in d # sanity check 1
            print k in d.keys() # sanity check 2
&lt;/pre&gt;



Can you guess what the results of sanity checks 1 and 2 were?



&lt;code&gt;'k in d'&lt;/code&gt; is &lt;code&gt;False&lt;/code&gt;, but &lt;code&gt;'k in d.keys()'&lt;/code&gt; is &lt;code&gt;True&lt;/code&gt;.



whhhaaaaa?  Much head scratching and hair pulling ensued.  I tried many different variations of iterating through the loop, all with the same result.



In the end, I posted a question on &lt;a href="http://stackoverflow.com/questions/4036114/how-can-k-in-d-be-false-but-k-in-d-keys-be-true"&gt;Stack Overflow&lt;/a&gt;.



At the same time, &lt;a href="http://code-bear.com/bearlog/category/mozilla/"&gt;Bear&lt;/a&gt; and &lt;a href="http://code.v.igoro.us/"&gt;Dustin&lt;/a&gt; were zeroing in on a solution.  The crucial bit here is that the keys of d are (follow me here...) methods of instances of my new scheduler classes, which inherit from &lt;code&gt;buildbot.util.ComparableMixin&lt;/code&gt;...which implements &lt;code&gt;__cmp__&lt;/code&gt; and &lt;code&gt;__hash__&lt;/code&gt;.  &lt;code&gt;__cmp__&lt;/code&gt; is used in the &lt;code&gt;'k in d.keys()'&lt;/code&gt; test, but &lt;code&gt;__hash__&lt;/code&gt; is used in the &lt;code&gt;'k in d'&lt;/code&gt; test.



Some further digging revealed that my scheduler was modifying state that &lt;code&gt;ComparableMixin.__hash__&lt;/code&gt; was referring to, resulting in the scheduler instances not having stable hashes over time.



Meanwhile, on stackoverflow, &lt;a href="http://stackoverflow.com/users/484688/adw"&gt;adw&lt;/a&gt; came up with an &lt;a href="http://stackoverflow.com/questions/4036114/how-can-k-in-d-be-false-but-k-in-d-keys-be-true/4036202#4036202"&gt;answer&lt;/a&gt; that confirmed what Dustin and Bear were saying, and &lt;a href="http://stackoverflow.com/users/398968/katrielalex"&gt;katrielalex&lt;/a&gt; came up with a &lt;a href="http://stackoverflow.com/questions/4036114/how-can-k-in-d-be-false-but-k-in-d-keys-be-true/4036314#4036314"&gt;simple example&lt;/a&gt; to reproduce the problem.



In the end, the fix was simple, just a few extra lines of python code.  Too bad it took me 3 days to figure out!</description><category>buildbot</category><category>mozilla</category><category>python</category><guid>https://atlee.ca/posts/blog201010293-days-of-fun-a-journey-into-the-bowels-of-buildbot/</guid><pubDate>Fri, 29 Oct 2010 14:00:40 GMT</pubDate></item><item><title>The long road to victory: build &amp; test logs on FTP</title><link>https://atlee.ca/posts/blog20101028the-long-road-to-victory-build-test-logs-on-ftp/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;One of the biggest pieces of infrastructure keeping us attached to &lt;a href="http://tinderbox.mozilla.org/Firefox/"&gt;Tinderbox &lt;/a&gt; is the issue of build logs.  Tinderbox has (and continues to be) the canonical source of a build or test's output.


A while ago we made some changes that should help pry us loose from this close dependency on Tinderbox.



Build &amp;amp; test logs for all regular depend &amp;amp; try builds are now available on FTP.  For example, today's nightly build log for windows is available at &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2010-10-27-06-mozilla-central/mozilla-central-win32-nightly-build26.txt.gz"&gt;http://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/2010-10-27-06-mozilla-central/mozilla-central-win32-nightly-build26.txt.gz&lt;/a&gt;.  It's also in the same directory as the build itself!



Test logs are available for depend builds, e.g. &lt;a href="http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/mozilla-central-macosx64/1288215864/mozilla-central_leopard_test-crashtest-build1283.txt.gz"&gt;http://ftp.mozilla.org/pub/mozilla.org/firefox/tinderbox-builds/mozilla-central-macosx64/1288215864/mozilla-central_leopard_test-crashtest-build1283.txt.gz&lt;/a&gt;.  Again, all the builds, logs and other files (test archives, symbols, etc.) are in the same directory.



Logs for your try builds and test are also available, e.g. &lt;a href="http://stage.mozilla.org/pub/mozilla.org/firefox/tryserver-builds/jdrew@mozilla.com-525f51996a52/tryserver-macosx64/"&gt;http://stage.mozilla.org/pub/mozilla.org/firefox/tryserver-builds/jdrew@mozilla.com-525f51996a52/tryserver-macosx64/&lt;/a&gt;.  Here too all the logs and binaries are in the same directory.  In addition you should be getting links to those logs in your try server notification emails.



There's room for improvement here, and a few places where logs aren't getting put in all the right places, but this is still a major step forward.



Enjoy!



&lt;em&gt;Note to future readers that a some of the links to the logs above will be invalid a few weeks after this post as old directories are purged.&lt;/em&gt;&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><category>tinderbox</category><guid>https://atlee.ca/posts/blog20101028the-long-road-to-victory-build-test-logs-on-ftp/</guid><pubDate>Thu, 28 Oct 2010 13:00:12 GMT</pubDate></item><item><title>A year in RelEng</title><link>https://atlee.ca/posts/blog20100511a-year-in-releng/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;Something prompted me to look at the size of our codebase here in RelEng, and how much it changes over time.  This is the code that drives all the build, test and release automation for Firefox, project branches, and Try, as well as configuration management for the various build and test machines that we have.


Here are some simple stats:



2,193 changesets across 5 repositories...that's about 6 changes a day on average.



We grew from 43,294 lines of code last year to 73,549 lines of code as of today.  That's 70% more code today than we had last year.



We added 88,154 lines to our code base, and removed 51,957.  I'm not sure what this means, but it seems like a pretty high rate of change!&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><category>python</category><category>talos</category><category>technology</category><category>unittests</category><category>work</category><guid>https://atlee.ca/posts/blog20100511a-year-in-releng/</guid><pubDate>Tue, 11 May 2010 14:15:46 GMT</pubDate></item><item><title>What do you want to know about builds?</title><link>https://atlee.ca/posts/blog20100504what-do-you-want-to-know-about-builds/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;Mozilla has been quite involved in recent &lt;a href="http://buildbot.net/trac"&gt;buildbot&lt;/a&gt; development, in particular, helping to make it scale across multiple machines.  More on this in another post!


Once deployed, these changes will give us the ability to give real time access to various information about our build queue: the list of jobs waiting to start, and which jobs are in progress.  This should help other tools like &lt;a href="http://tests.themasta.com/tinderboxpushlog/"&gt;Tinderboxpushlog&lt;/a&gt; show more accurate information.  One limitation of the upstream work so far is that it only captures a very coarse level of detail about builds: start/end time, and result code is pretty much it.  No further detail about the build is captured, like which slave it executed on, what properties it generated (which could include useful information like the URL to the generated binaries), etc.



We've also been exporting a &lt;a href="http://build.mozilla.org/builds/"&gt;json dump&lt;/a&gt; of our build status for &lt;a href="https://atlee.ca/blog/2010/03/04/byo-build-dashboard/"&gt;many months now&lt;/a&gt;.  It's been useful for some analysis, but it also has limitations: the data is always at least 5 minutes old by the time you look, and in-progress builds are not represented at all.



We're starting to look at ways of exporting all this detail in a way that's useful to more people.  You want to get notified when your try builds are done?  You want to look at which test suites are taking the most time?  You want to determine how our build times change over time?  You want to find out what the last all-green revision was on trunk?  We want to make this data available, so anybody can write these tools.



&lt;/p&gt;&lt;h3&gt;Just how big is that firehose?&lt;/h3&gt;

I think we have one of the largest buildbot setups out there and we generate a non-trivial amount of data:

&lt;ul&gt;&lt;li&gt;6-10 buildbot master processes generating updates, on different machines in 2 or 3 data centers&lt;/li&gt;
    &lt;li&gt;around 130 jobs per hour composed of 4,773 individual steps total per hour.  That works out to about 1.4 updates per second that are generated&lt;/li&gt;
&lt;/ul&gt;&lt;h3&gt;How you can help&lt;/h3&gt;

This is where you come in.



I can think of two main classes of interfaces we could set up: a query-type interface where you poll for information that you are interested in, and a notification system where you register a listener for certain types (or all!) events.



What would be the best way for us to make this data available to you?  Some kind of REST API?  A message or event brokering system?  &lt;a href="http://code.google.com/p/pubsubhubbub/"&gt;pubsubhubbub&lt;/a&gt;?



Is there some type of data or filtering that would be super helpful to you?</description><category>buildbot</category><category>end-to-end</category><category>general</category><category>mozilla</category><category>performance</category><category>python</category><category>talos</category><category>technology</category><category>unittests</category><category>work</category><guid>https://atlee.ca/posts/blog20100504what-do-you-want-to-know-about-builds/</guid><pubDate>Tue, 04 May 2010 21:44:14 GMT</pubDate></item><item><title>Buildbot performance and scaling</title><link>https://atlee.ca/posts/blog20100331buildbot-performance-and-scaling/</link><dc:creator>chris</dc:creator><description>&lt;p&gt;It seems like it was ages ago when I posted about &lt;a href="https://atlee.ca/blog/2009/07/28/profiling-buildbot/"&gt;profiling buildbot&lt;/a&gt;.


One of the hot spots identified there was the dataReceived call.  This has been sped up a little bit in recent versions of twisted, but our buildbot masters were still severely overloaded.



It turns out that the buildbot slaves make a &lt;strong&gt;lot&lt;/strong&gt; of RPC calls when sending log data, which results in tens of thousands of dataReceived calls.  Multiply that by several dozen build slaves sending log data peaking at a combined throughput of 10 megabits/s and you've got an awful lot of data to handle.



By adding a small slave-side buffer, the number of RPC calls to send log data is drastically reduced by an order of magnitude in some tests, resulting in a much better load situation on the master.  This is good for us, because it means the masters are much more responsive, and it's good for everybody else because it means we have fewer failures and wasted time due to the master being too busy to handle everything.  It also means we can throw more build slaves onto the masters!



The new code was deployed towards the end of the day on the 26th, or the end of the 12th week.



&lt;img src="https://atlee.ca/blog/wp-content/uploads/123f0df3.png" alt="" title="Buildbot master weekly CPU usage" width="512" height="355" class="alignnone size-full wp-image-702"&gt;&lt;img src="https://atlee.ca/blog/wp-content/uploads/d9c1a2ee.png" alt="" title="Buildbot master monthly CPU usage" width="511" height="352" class="alignnone size-full wp-image-701"&gt;&lt;/p&gt;</description><category>buildbot</category><category>mozilla</category><category>performance</category><category>python</category><category>technology</category><guid>https://atlee.ca/posts/blog20100331buildbot-performance-and-scaling/</guid><pubDate>Wed, 31 Mar 2010 19:47:08 GMT</pubDate></item></channel></rss>