<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-9120072357542955286</id><updated>2012-02-16T20:34:09.873-06:00</updated><category term='Extraction'/><category term='How it all started'/><category term='SimPy'/><category term='Linear Algebra'/><category term='vuser'/><category term='QTP'/><category term='WinDBG'/><category term='Statistics'/><category term='VirtualBox'/><category term='perl'/><category term='Selenium'/><category term='Win2K8'/><category term='Automatic Load Test'/><category term='Correlation'/><category term='SMTP'/><category term='VM'/><category term='Performance By Design'/><category term='FTP'/><category term='WiX'/><category term='Net::SMTP'/><category term='NAnt'/><category term='Unemployment'/><category term='Push-To-Test'/><category term='JMeter'/><category term='Transition Matrix'/><category term='Portable Server'/><category term='Load Balancing'/><category term='Heuristic Analysis'/><category term='Fiddler2'/><category term='Hyper-V'/><category term='FreeBSD'/><category term='MIME::Lite'/><category term='Employment'/><category term='MSBuild'/><category term='Guerrilla Capacity Planning'/><category term='PDQ-R'/><category term='Grinderstone'/><category term='soapUI'/><category term='SDC Tasks'/><category term='Orthogonal'/><category term='VS2008'/><category term='SQLServer 2005'/><category term='Universal Scaling Law'/><category term='GC'/><category term='Memory'/><category term='Sager'/><category term='PITA'/><category term='LoadRunner'/><category term='Thinktime'/><category term='DHCP'/><category term='TeamQuest Model'/><category term='R'/><category term='Metrics'/><title type='text'>Adventures in Load Testing</title><subtitle type='html'>Living Life on the Edge of Mediocrity in the Load Testing World!</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>70</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5549172046763208887</id><published>2010-01-28T19:31:00.005-06:00</published><updated>2010-01-28T19:54:42.258-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Universal Scaling Law'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>Using R to Plot Universal Scaling Law Curve Automagically</title><content type='html'>The past two days I was doing some benchmarking of a system that might be used to stub back end systems and I was very interested in generating an USL curve based upon the benchmark data I was producing.&lt;br /&gt;&lt;br /&gt;In previous posts I had shown how to use R to generate the kappa and sigma coefficients for use with the USL equation.&lt;br /&gt;&lt;br /&gt;I start with the data that I've compiled on the system based upon the number of concurrent threads and the throughput measured:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;p,x&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;1,1&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;3,2.998108449&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;5,4.995428752&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;7,6.928278689&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;10,7.539249685&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;13,11.50488651&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;15,11.79476671&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;20,15.65936318&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;30,13.61349306&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;40,16.50567465&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;50,14.83716898&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;The number of threads is represented by "p" and the throughput is represented by "x"&lt;br /&gt;&lt;br /&gt;I wrote the following R function to automagically create a plot of the data points for the above file and generate the USL curve along the data points and highlight the maximum theoretical point for the USL:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;# Example usage:&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;# plotUSL(&lt;span class="str"&gt;"c:/benchmark/benchmark.csv"&lt;/span&gt;, &lt;span class="str"&gt;"Benchmark data with USL curve"&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;# CSV file must have two columns with a header of &lt;span class="str"&gt;"p, x"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;# Example:&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;#     p, x&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;#    1, 1&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;#    2, 1.5&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;#    3, 2&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;plotUSL &amp;lt;- function(dataFile, graphTitle) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;  uslData &amp;lt;- read.csv(dataFile, header=TRUE);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;  uslData$c &amp;lt;- uslData$x / uslData$x[1];&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;  usl &amp;lt;- nls(c ~ p/(1+sigma*(p-1)+kappa*p*(p-1)),&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;             uslData,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;             algorithm=&lt;span class="str"&gt;"port"&lt;/span&gt;,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;             start=c(sigma=0.0, kappa=0.0),&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;             lower=c(0,0));&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;  sigma &amp;lt;- coef(usl)[&lt;span class="str"&gt;"sigma"&lt;/span&gt;];&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;  kappa &amp;lt;- coef(usl)[&lt;span class="str"&gt;"kappa"&lt;/span&gt;];&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;  p &amp;lt;- 1:round(1.75 * max(uslData$p));&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;  Relative_Capacity &amp;lt;- p/(1+sigma*(p-1)+kappa*p*(p-1));&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;  plot(p, Relative_Capacity, type=&lt;span class="str"&gt;"l"&lt;/span&gt;, ylim=c(0, round(max(Relative_Capacity, uslData$c)*1.1)));&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;  points(uslData$p, uslData$c, pch=20);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;  indexValue &amp;lt;- 1;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;  testValue  &amp;lt;- Relative_Capacity[1];&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;  maxValue   &amp;lt;- max(Relative_Capacity);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;  &lt;span class="kwrd"&gt;while&lt;/span&gt;(testValue != maxValue) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;    indexValue = indexValue + 1;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;    testValue = Relative_Capacity[indexValue];&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;  };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;  points(p[indexValue], Relative_Capacity[indexValue], col=2, pch=13, cex=2.0);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;  title(main=graphTitle, col.main=&lt;span class="str"&gt;"black"&lt;/span&gt;, font.main=4, cex=1.5);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;  title(sub=sprintf(&lt;span class="str"&gt;"USL max at p = %d with Relative_Capacity = %f"&lt;/span&gt;,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;        indexValue,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;        Relative_Capacity[indexValue]), &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;        col.main=&lt;span class="str"&gt;"black"&lt;/span&gt;, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;        font.main=4, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;        cex=1.5);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;};&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And viola, we can quick and dirty results after invoking the function!&lt;br /&gt;&lt;br /&gt;&gt; plotUSL("g:/usl/benchmark.csv", "Benchmark data with USL curve")&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/S2I_ZC4Y_sI/AAAAAAAAAR4/_3ArkT5M58Q/s1600-h/uslCurve.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/S2I_ZC4Y_sI/AAAAAAAAAR4/_3ArkT5M58Q/s320/uslCurve.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5431973800064908994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This function could easily be modified to accept a third parameter and save off the image to a JPG, PNG or even PDF. But I leave that as an exercise for the reader.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5549172046763208887?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5549172046763208887/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5549172046763208887' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5549172046763208887'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5549172046763208887'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2010/01/using-r-to-plot-universal-scalability.html' title='Using R to Plot Universal Scaling Law Curve Automagically'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qsa4nRIPmPo/S2I_ZC4Y_sI/AAAAAAAAAR4/_3ArkT5M58Q/s72-c/uslCurve.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-833495144293714099</id><published>2009-12-20T22:33:00.013-06:00</published><updated>2009-12-21T22:59:04.519-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Employment'/><title type='text'>11001001</title><content type='html'>What a strange trip it has been.&lt;br /&gt;&lt;br /&gt;Two weeks ago I got a call from an IT Mercenary outfit and my former employer wanted to bring me in for a face-to-face. This isn't the first time that my former employer and I have sniffed out each other but this is the first time that something came of it.&lt;br /&gt;&lt;br /&gt;It appears that on 28 Dec I will be going back to my former employer doing the exact same gig (for now) as a contractor via the above mentioned IT Mercenary firm. It's not a bad rate and it is W2 with a benefits option. The only loss is vacation/sick time. However, if I work 60 hours in a week I get paid for 60 hours. I'll have to wait and see how that goes.&lt;br /&gt;&lt;br /&gt;So, I've been away for roughly 19 months. What have I really done in that time?&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Agile Project Management&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Does Agile work? Yes and no from what I've seen first hand. It can work if management allows it to work. I've also seen Agile as an abstraction for sales. I worked for a firm that shouted Agile from the rooftops but did not allow it's own projects to run in an agile fashion. Agile was squashed for tyrannical project management with tacit upper management approval, developers opinions and experience be damned.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Alternative Load Testing Frameworks&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I got a lot of good work in with Microsoft's load testing tool. Microsoft really needs to have a better naming scheme for it's testing tools. When you say, "LoadRunner" you don't have to explain it too much. Most people know that LoadRunner is for performance testing. Sure, they might not know how it works, but they know it has something to do with performance testing. The Microsoft solution? Visual Studio 2008 Team System Test Edition. That's a tadbit verbose. Sure, you know it's Visual Studio and that it is for the Team Edition and hey, you know it's with the Testing Edition. But still, hardly anybody really knows what the hell it does or how it does it. Visual Studio 2008 Team System Test Edition Coded Web Test is just too verbose. Try doing some job searches on indeed.com for "Visual Studio 2008 Team System Test Edition Coded Web Test" and see how many hits you get versus the generic "LoadRunner."&lt;br /&gt;&lt;br /&gt;I also got to mess around a lot with JMeter. I found JMeter to be handy little tool. I felt a little constrained by the GUI nature of JMeter but still, it gets the job done and has more protocols available than the Microsoft Tool. I preferred the Microsoft tool as I could develop virtual users in C# versus JMeter use of GUI elements and beanshell coding for extraction and correlation. &lt;br /&gt;&lt;br /&gt;While not a performance testing tool, I did get to play with QTP a bit. I'd like to get to know QTP better. It seems to be a real handy tool. I'm also excited by the UI testing tool that will be part of VS2010, as well.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Deployment Tools&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I got to mess around with tools like NAnt, MSBuild and WiX. Of the three I prefer NAnt but generally the XML files get very tiresome. I'm still a perl kind of guy at heart but of the three I prefer NAnt. I got to extend NAnt with objects created for MSBuild via a wrapper which was nifty and solved a problem.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Project Planning&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For a project that I worked on for the Veterans Administration I gutted and re-wrote the performance engineering plan. The end document was over 90 pages of generic performance testing documentation.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Capacity Analysis Tools&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Again for the Veterans Administration I did a comparison of different capacity analysis tools and techniques and the way of combining the efforts of performance testing and capacity analysis and trending. I found the books by Neil Gunther to be most excellent along with the PDQ API. While TeamQuest Model is popular I found the interface to be extremely clunky. It reminded me of VB4/Powerbuilder era interfaces.&lt;br /&gt;&lt;br /&gt;In the past I've done plenty of empirical capacity analysis based upon my load testing results and observations of production systems but this was the first time that I learned the math behind heuristic queuing analysis and the various tools related to queuing theory. &lt;br /&gt;&lt;br /&gt;Of all the things that I learned on my little adventure I think that this ranks near the top in terms of importance and application to future endeavors. It really is quite handy and goes hand-in-hand with performance testing. For my last position I recommended the Python versis of PDQ, PyDQ. I selected PyDQ because I like the clean duck typing syntax of Python coupled with the available IDEs for Windows and Linux.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;The Joys of Virtualization&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I used Hyper-V and VirtualBox on a lot of projects. Virtualization just plain rocks. Prototyping complete systems is just incredibly cool. I found the snapshot system of Hyper-V to be better performing than VirtualBox but VirtualBox is much more flexible as VirtualBox is not bound to Windows 2008. When I was testing deployment scripts with NAnt, Hyper-V was invaluable. Test, check results, fix, rollback and test again. Fabulous! Both Hyper-V R2 and VirtualBox 3.1 support live motion of VMs which is pretty cool. I really hope that Oracle doesn't kill off VirtualBox. VirtualBox just rocks.&lt;br /&gt;&lt;br /&gt;I used VirtualBox to build a JMS prototype system with multiple clients processing messages. For another learning experience I had built an OpenSolaris server exporting an iSCSI target. I learned that ZFS is one of the greatest file systems that I have ever used. Really cool stuff.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Python&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For some of the capacity analysis tools I needed to learn Python and I found that I liked what I found. I've done some Python development in the past (early 2000s) but never did follow up as I used perl for what I needed. Python led to learning Django and my desire to further learn MVC with different frameworks. I also played around a lot with different Python development platforms like Eclipse with PyDev and NetBeans with support for Python. Pretty darn cool.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Java&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;For the VA project I did a bit of JMS and Swing development in Java. I've never done any Java development and I started to learn Java development. I'm now continuing learning Java in continuing education courses at the local community college. While I may not be utilizing Java right now it's a good thing to learn. I would have liked to have done more JMS development but alas, that was put on the back burner when my project was coming to an end in favor of the Swing development. I've never been much of a GUI developer and still don't have any major desire to do front ends. In my heart I'm a command line kinda guy.&lt;br /&gt;&lt;br /&gt;I covered metrics collection and tuning of J2EE apps but I've only truly done J2SE development. The VA project was supposed to expose me to WebLogic but unfortunately that never came to fruition. I've already forgotten all the Oracle training that I crammed into my brain the first few weeks of being on the project.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;R&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I had the opportunity to do some R development to aid in number crunching and I found R to be very handy. If I do a lot of statistical number crunching I'm more likely to do it in R than I am do write perl routines to get the same results. I especially like the built in graphing capabilities of R both in quality and ability to generate jpg and PDF results. I started out learning R as part of PDQ-R but ultimately for the project I was working on I selected PyDQ, the Python implementation of PDQ for the robustness and the widely available IDEs for Python both under Windows and Linux.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Skill Development&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the November time frame I had a job interview and while I did good on the CS portion of the interview (math, algorithms analysis, etc) I bombed the actual coding portion. The coding portion of the interview was using C# to solve a problem using interfaces. I've implemented interfaces in the past but this solution was using interfaces in a way that I had never used them. That told me that it was time to start developing my coding skill set. I've been taking some Java classes at the local community college and have plans to continue with the advanced Java class along with Hibernate and Spring. Also covered will be Ruby on Rails.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Working from Home&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I worked from home for 11 months and I've decided that working from home sucks. I am not a homebody and get cabin fever easily. Face to face communication is definitely better than phone conference meetings with 15 people. I found Skype to be real handy for voice conferencing with 1-800 numbers. Working from home wasn't good for my waist line either. I'm looking to getting back into a regular work routine and making the use of the facility gym with my old trainer and utilizing the running track again once I've dropped a few pounds. Sure, I got up to a 405 pound machine bench press (useful for throwing developers over cube walls) but I've let the old BF% get up too high. Bah!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Working on Government Projects&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I've worked on government projects before, specifically USAF and DoD stuff but this was the first time that I worked on a VA project. Oy! What a mess. Remember the messed up logistics systems from when you were in the US Army and twist the entropy knob to eleven. Yeah, it was really that bad. The sad thing was that the project I was working on was supposed to help those veterans that put their lives on the line for their country and this was the best that could be done? I shudder to think of Guvment Health Care being ran like the project that I was on.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Books&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Here is a short list of books that I found to be handy:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/Analyzing-Computer-System-Performance-ebook/dp/B001FB6DP4/ref=sr_1_3?ie=UTF8&amp;s=books&amp;qid=1261393079&amp;sr=8-3"&gt;Analyzing Computer System Performance with Perl::PDQ&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/Guerrilla-Capacity-Planning-Tactical-Applications/dp/3540261389/ref=sr_1_5?ie=UTF8&amp;s=books&amp;qid=1261393079&amp;sr=8-5"&gt;Guerrilla Capacity Planning: A Tactical Approach to Planning for Highly Scalable Applications and Services&lt;br /&gt;&lt;/a&gt;&lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1261393140&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/s/ref=nb_ss?url=search-alias%3Dstripbooks&amp;field-keywords=art+of+capacity+planning&amp;x=0&amp;y=0"&gt;The Art of Capacity Planning: Scaling Web Resources&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.amazon.com/Art-Application-Performance-Testing-Programmers/dp/0596520662/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1261393207&amp;sr=1-1"&gt;The Art of Application Performance Testing: Help for Programmers and Quality Assurance&lt;br /&gt;&lt;/a&gt;&lt;a href="http://www.amazon.com/Pro-Java-Performance-Management-Optimization/dp/1590596102/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1261393259&amp;sr=1-1"&gt;Pro Java EE 5 Performance Management and Optimization&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;What's next? Who knows? Can't wait to find out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-833495144293714099?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/833495144293714099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=833495144293714099' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/833495144293714099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/833495144293714099'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/12/11001001.html' title='11001001'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6403136199405699129</id><published>2009-11-30T16:29:00.002-06:00</published><updated>2009-12-21T23:00:10.661-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Employment'/><title type='text'>Time to map out all the soup kitchens.</title><content type='html'>Well, I finally packed up my work laptop and sent it back to my employer. As of tomorrow I will be unemployed. But I do have some phone interviews setup so that isn't too bad for a December. Need to file for unemployment tomorrow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6403136199405699129?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6403136199405699129/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6403136199405699129' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6403136199405699129'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6403136199405699129'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/11/time-to-map-out-all-soup-kitchens.html' title='Time to map out all the soup kitchens.'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-4170901805654335977</id><published>2009-11-05T18:07:00.003-06:00</published><updated>2009-11-06T17:26:56.304-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VM'/><category scheme='http://www.blogger.com/atom/ns#' term='VirtualBox'/><title type='text'>VirtualBox is amazing</title><content type='html'>So I went to the Microsoft event yesterday and got a Win 7 Ultimate license. &lt;br /&gt;&lt;br /&gt;A few years back I had done the same and got a Vista Ultimate key and got a copy of the Vista Ultimate 64-bit ISO. When I tried to use the key that was distributed with the 32-bit DVD from the event it didn't work. As a student I was able to get a discount license for Vista Ultimate and for some reason, the key that I got from the Microsoft event worked with the ISO that I had purchased. Weird.&lt;br /&gt;&lt;br /&gt;I thought maybe that would happen again as the handout DVD was only for the 32-bit Win 7 Ultimate. I got a copy of a Win 7 Ultimate 64-bit ISO and decided that I would test out the install and see if the key would work correctly.&lt;br /&gt;&lt;br /&gt;My main rig, an i7 920 machine with 12 gigs of RAM was busy performing a backup so I decided to fire up VirtualBox on my now aging Acer Ferrari 5000 machine and did a VM install of Win 7 Ultimate 64-bit to a VM and verified that the key worked just fine (yay!).&lt;br /&gt;&lt;br /&gt;It wasn't until this morning that I realized that I had installed Win 7 Ultimate 64-bit virtual machine on a laptop running Vista 32-bit. And it worked.&lt;br /&gt;&lt;br /&gt;Wow.&lt;br /&gt;&lt;br /&gt;That just rocks.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-4170901805654335977?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/4170901805654335977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=4170901805654335977' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/4170901805654335977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/4170901805654335977'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/11/virtualbox-is-amazing.html' title='VirtualBox is amazing'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8044061081530422226</id><published>2009-10-09T20:01:00.005-05:00</published><updated>2009-12-21T23:00:34.459-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Employment'/><category scheme='http://www.blogger.com/atom/ns#' term='Grinderstone'/><title type='text'>I'm dreaming of a laid off Chrismas...</title><content type='html'>Got a call. Looks like the program that I am on is not going to be funded past mid-November. I suspect that if laid off in mid-November two weeks before Thanksgiving and the Holiday season not much is going to happen job wise, just like last year.&lt;br /&gt;&lt;br /&gt;At least this time I have some heads up warning and can plan accordingly unlike my previous stint at VT (may JC and RZ rot in the lower bowels of Hell). So, I don't think it'll be so bad this time around.&lt;br /&gt;&lt;br /&gt;During this hiatus I think I'll give The Grinder a closer look as I haven't had a chance to use it yet on anything and I've been doing a lot more Python coding as of late (PyDQ and SimPy stuff). An updated Grinder plug-in has been created for Eclipse and hopefully it works out better than my previous attempt at using &lt;a href="http://code.google.com/p/grinderstone/"&gt;Grinderstone&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Good times!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8044061081530422226?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8044061081530422226/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8044061081530422226' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8044061081530422226'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8044061081530422226'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/10/im-dreaming-of-laid-off-chrismas.html' title='I&apos;m dreaming of a laid off Chrismas...'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-1372608341056667565</id><published>2009-08-31T12:23:00.004-05:00</published><updated>2009-09-01T10:58:58.832-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SimPy'/><title type='text'>Look, everybody! It's a SimPy three tier eBiz simulation! But now with extra statistics! Wowee!</title><content type='html'>Added some more statistics to the output such as waiting queue length and component utilization by using SimPy Monitors. I couldn't easily use the monitors for the page response times due to the split up nature calls to multiple nodes but for .waitQ and node utilizations they worked out perfectly.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;&lt;br /&gt;from SimPy.Simulation import *&lt;br /&gt;from random import Random, expovariate, uniform&lt;br /&gt;&lt;br /&gt;class Metrics:&lt;br /&gt;&lt;br /&gt;  metrics = dict()&lt;br /&gt;&lt;br /&gt;  def Add(self, metricName, frameNumber, value):&lt;br /&gt;    if self.metrics.has_key(metricName):&lt;br /&gt;      if self.metrics[metricName].has_key(frameNumber):&lt;br /&gt;        self.metrics[metricName][frameNumber].append(value)&lt;br /&gt;      else:&lt;br /&gt;        self.metrics[metricName][frameNumber] = list()&lt;br /&gt;        self.metrics[metricName][frameNumber].append(value)&lt;br /&gt;    else:&lt;br /&gt;      self.metrics[metricName] = dict()&lt;br /&gt;      self.metrics[metricName][frameNumber] = list()&lt;br /&gt;      self.metrics[metricName][frameNumber].append(value)&lt;br /&gt;&lt;br /&gt;  def Keys(self):&lt;br /&gt;    return self.metrics.keys()&lt;br /&gt;&lt;br /&gt;  def Mean(self, metricName):&lt;br /&gt;    valueArray = list()&lt;br /&gt;    if self.metrics.has_key(metricName):&lt;br /&gt;      for frame in self.metrics[metricName].keys():&lt;br /&gt;        for values in range(len(self.metrics[metricName][frame])):&lt;br /&gt;          valueArray.append(self.metrics[metricName][frame][values])&lt;br /&gt;&lt;br /&gt;      sum = 0.0&lt;br /&gt;      for i in range(len(valueArray)):&lt;br /&gt;        sum += valueArray[i]&lt;br /&gt;          &lt;br /&gt;      if len(self.metrics[metricName][frame]) != 0:      &lt;br /&gt;        return sum/len(self.metrics[metricName])&lt;br /&gt;      else:&lt;br /&gt;        return 0 # Need to learn python throwing exceptions&lt;br /&gt;    else:&lt;br /&gt;      return 0&lt;br /&gt;&lt;br /&gt;class G: &lt;br /&gt;&lt;br /&gt;  numWS = 1&lt;br /&gt;  numAS = 1&lt;br /&gt;  numDS = 1&lt;br /&gt;&lt;br /&gt;  Rnd         = random.Random(12345)&lt;br /&gt;&lt;br /&gt;  PageNames   = ["Entry", "Home", "Search", "View", "Login", "Create", "Bid", "Exit" ]&lt;br /&gt;&lt;br /&gt;  Entry       = 0&lt;br /&gt;  Home        = 1&lt;br /&gt;  Search      = 2&lt;br /&gt;  View        = 3&lt;br /&gt;  Login       = 4&lt;br /&gt;  Create      = 5&lt;br /&gt;  Bid         = 6&lt;br /&gt;  Exit        = 7&lt;br /&gt;&lt;br /&gt;  WS          = 0&lt;br /&gt;  AS          = 1&lt;br /&gt;  DS          = 2&lt;br /&gt;&lt;br /&gt;  CPU         = 0&lt;br /&gt;  DISK        = 1&lt;br /&gt;&lt;br /&gt;  WS_CPU      = 0&lt;br /&gt;  WS_DISK     = 1&lt;br /&gt;  AS_CPU      = 2&lt;br /&gt;  AS_DISK     = 3&lt;br /&gt;  DS_CPU      = 4&lt;br /&gt;  DS_DISK     = 5&lt;br /&gt;&lt;br /&gt;  metrics     = Metrics()&lt;br /&gt;&lt;br /&gt;  #             e  h  s  v  l  c  b  e&lt;br /&gt;  HitCount   = [0, 0, 0, 0, 0, 0, 0, 0]   &lt;br /&gt;&lt;br /&gt;  Resources = [[ Resource(1), Resource(1) ], # WS CPU and DISK&lt;br /&gt;               [ Resource(1), Resource(1) ], # AS CPU and DISK&lt;br /&gt;               [ Resource(1), Resource(1) ]] # DS CPU and DISK&lt;br /&gt;&lt;br /&gt;  QMon      = [[ Monitor(1), Monitor(1)], # WS CPU and DISK&lt;br /&gt;               [ Monitor(1), Monitor(1)], # AS CPU and DISK&lt;br /&gt;               [ Monitor(1), Monitor(1)]] # DS CPU and DISK&lt;br /&gt;&lt;br /&gt;  SMon      = [[ Monitor(1), Monitor(1)], # WS CPU and DISK&lt;br /&gt;               [ Monitor(1), Monitor(1)], # AS CPU and DISK&lt;br /&gt;               [ Monitor(1), Monitor(1)]] # DS CPU and DISK&lt;br /&gt;&lt;br /&gt;  #                  Enter  Home   Search View   Login  Create Bid    Exit&lt;br /&gt;  ServiceDemand = [ [0.000, 0.008, 0.009, 0.011, 0.060, 0.012, 0.015, 0.000],  # WS_CPU&lt;br /&gt;                    [0.000, 0.030, 0.010, 0.010, 0.010, 0.010, 0.010, 0.000],  # WS_DISK&lt;br /&gt;                    [0.000, 0.000, 0.030, 0.035, 0.025, 0.045, 0.040, 0.000],  # AS_CPU&lt;br /&gt;                    [0.000, 0.000, 0.008, 0.080, 0.009, 0.011, 0.012, 0.000],  # AS_DISK&lt;br /&gt;                    [0.000, 0.000, 0.010, 0.009, 0.015, 0.070, 0.045, 0.000],  # DS_CPU&lt;br /&gt;                    [0.000, 0.000, 0.035, 0.018, 0.050, 0.080, 0.090, 0.000] ] # DS_DISK&lt;br /&gt;&lt;br /&gt;  # Type B shopper&lt;br /&gt;  #                     0     1     2     3     4     5     6     7&lt;br /&gt;  TransitionMatrix = [ [0.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00],  # 0&lt;br /&gt;                       [0.00, 0.00, 0.70, 0.00, 0.10, 0.00, 0.00, 0.20],  # 1&lt;br /&gt;                       [0.00, 0.00, 0.45, 0.15, 0.10, 0.00, 0.00, 0.30],  # 2&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.40, 0.00, 0.00, 0.60],  # 3&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.30, 0.55, 0.15],  # 4&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00],  # 5&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00],  # 6&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00] ] # 7&lt;br /&gt;&lt;br /&gt;class RandomPath:&lt;br /&gt;&lt;br /&gt;  def RowSum(self, Vector):&lt;br /&gt;    rowSum = 0.0&lt;br /&gt;    for i in range(len(Vector)):&lt;br /&gt;      rowSum += Vector[i]&lt;br /&gt;    return rowSum&lt;br /&gt;&lt;br /&gt;  def NextPage(self, T, i):&lt;br /&gt;    rowSum = self.RowSum(T[i])&lt;br /&gt;    randomValue = G.Rnd.uniform(0, rowSum)&lt;br /&gt;&lt;br /&gt;    sumT = 0.0&lt;br /&gt;    &lt;br /&gt;    for j in range(len(T[i])):&lt;br /&gt;      sumT += T[i][j]&lt;br /&gt;      if randomValue &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt; sumT:&lt;br /&gt;        break&lt;br /&gt;    return j&lt;br /&gt;&lt;br /&gt;class ExecuteFrame(Process):&lt;br /&gt;&lt;br /&gt;  def __init__(self, frameNumber, resource, QMon, SMon, serviceDemand, nodeName, pageName):&lt;br /&gt;    Process.__init__(self)&lt;br /&gt;    self.frame         = frameNumber &lt;br /&gt;    self.resource      = resource&lt;br /&gt;    self.serviceDemand = serviceDemand&lt;br /&gt;    self.nodeName      = nodeName&lt;br /&gt;    self.pageName      = pageName&lt;br /&gt;    self.QMon          = QMon&lt;br /&gt;    self.SMon          = SMon&lt;br /&gt;&lt;br /&gt;  def execute(self):&lt;br /&gt;    StartUpTime = now()&lt;br /&gt;&lt;br /&gt;    yield request, self, self.resource&lt;br /&gt;    yield hold,    self, self.serviceDemand&lt;br /&gt;&lt;br /&gt;    self.QMon.observe(len(self.resource.waitQ))&lt;br /&gt;    self.SMon.observe(self.serviceDemand)&lt;br /&gt;&lt;br /&gt;    yield release, self, self.resource&lt;br /&gt;&lt;br /&gt;    R = now() -  StartUpTime&lt;br /&gt;&lt;br /&gt;    G.metrics.Add(self.pageName, self.frame, R)&lt;br /&gt;&lt;br /&gt;class CallPage(Process):&lt;br /&gt;&lt;br /&gt;  def __init__(self, frameNumber, node, pageName):&lt;br /&gt;    Process.__init__(self)&lt;br /&gt;    self.frame          = frameNumber&lt;br /&gt;    self.StartUpTime    = 0.0&lt;br /&gt;    self.currentPage    = node&lt;br /&gt;    self.pageName       = pageName&lt;br /&gt;&lt;br /&gt;  def execute(self):&lt;br /&gt;&lt;br /&gt;    if self.currentPage != G.Exit:&lt;br /&gt;&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Working on Frame # ", self.frame, " @ ", now() , " for page ", self.pageName&lt;br /&gt;&lt;br /&gt;      self.StartUpTime = now()&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.WS_CPU][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        wsCPU = ExecuteFrame(self.frame,                                           \&lt;br /&gt;                             G.Resources[G.WS][G.CPU],                             \&lt;br /&gt;                             G.QMon[G.WS][G.CPU],                                  \&lt;br /&gt;                             G.SMon[G.WS][G.CPU],                                  \&lt;br /&gt;                             G.ServiceDemand[G.WS_CPU][self.currentPage]/G.numWS,  \&lt;br /&gt;                             "wsCPU", self.pageName) &lt;br /&gt;        activate(wsCPU, wsCPU.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.WS_DISK][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        wsDISK = ExecuteFrame(self.frame,                                           \&lt;br /&gt;                              G.Resources[G.WS][G.DISK],                            \&lt;br /&gt;                              G.QMon[G.WS][G.DISK],                                 \&lt;br /&gt;                              G.SMon[G.WS][G.DISK],                                 \&lt;br /&gt;                              G.ServiceDemand[G.WS_DISK][self.currentPage]/G.numWS, \&lt;br /&gt;                              "wsDISK", self.pageName)&lt;br /&gt;        activate(wsDISK, wsDISK.execute())&lt;br /&gt; &lt;br /&gt;      if G.ServiceDemand[G.AS_CPU][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        asCPU = ExecuteFrame(self.frame,                                            \&lt;br /&gt;                             G.Resources[G.AS][G.CPU],                              \&lt;br /&gt;                             G.QMon[G.AS][G.CPU],                                   \&lt;br /&gt;                             G.SMon[G.AS][G.CPU],                                   \&lt;br /&gt;                             G.ServiceDemand[G.AS_CPU][self.currentPage]/G.numAS,   \&lt;br /&gt;                             "asCPU",                                               \&lt;br /&gt;                             self.pageName)&lt;br /&gt;        activate(asCPU, asCPU.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.AS_DISK][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        asDISK = ExecuteFrame(self.frame,                                           \&lt;br /&gt;                              G.Resources[G.AS][G.DISK],                            \&lt;br /&gt;                              G.QMon[G.AS][G.DISK],                                 \&lt;br /&gt;                              G.SMon[G.AS][G.DISK],                                 \&lt;br /&gt;                              G.ServiceDemand[G.AS_DISK][self.currentPage]/G.numAS, \&lt;br /&gt;                              "asDISK",                                             \&lt;br /&gt;                              self.pageName)&lt;br /&gt;        activate(asDISK, asDISK.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.DS_CPU][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        dsCPU = ExecuteFrame(self.frame,                                            \&lt;br /&gt;                             G.Resources[G.DS][G.CPU],                              \&lt;br /&gt;                             G.QMon[G.DS][G.CPU],                                   \&lt;br /&gt;                             G.SMon[G.DS][G.CPU],                                   \&lt;br /&gt;                             G.ServiceDemand[G.DS_CPU][self.currentPage]/G.numDS,   \&lt;br /&gt;                             "dsCPU",                                               \&lt;br /&gt;                             self.pageName)&lt;br /&gt;        activate(dsCPU, dsCPU.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.DS_DISK][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        dsDISK = ExecuteFrame(self.frame,                                           \&lt;br /&gt;                              G.Resources[G.DS][G.DISK],                            \&lt;br /&gt;                              G.QMon[G.DS][G.DISK],                                 \&lt;br /&gt;                              G.SMon[G.DS][G.DISK],                                 \&lt;br /&gt;                              G.ServiceDemand[G.DS_DISK][self.currentPage]/G.numDS, \&lt;br /&gt;                              "dsDISK",                                             \&lt;br /&gt;                              self.pageName)&lt;br /&gt;        activate(dsDISK, dsDISK.execute())&lt;br /&gt;&lt;br /&gt;      G.HitCount[self.currentPage] += 1&lt;br /&gt;&lt;br /&gt;      yield hold, self, 0.00001 &lt;br /&gt;&lt;br /&gt;class Generator(Process):&lt;br /&gt; def __init__(self, rate, maxT):&lt;br /&gt;     Process.__init__(self)&lt;br /&gt;     self.name = "Generator"&lt;br /&gt;     self.rate = rate&lt;br /&gt;     self.maxT = maxT&lt;br /&gt;     self.g = Random(11335577)&lt;br /&gt;     self.i = 0&lt;br /&gt;     self.currentPage = G.Home&lt;br /&gt;&lt;br /&gt; def execute(self):&lt;br /&gt;   while (now() &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt; self.maxT): &lt;br /&gt;     self.i+=1&lt;br /&gt;     p = CallPage(self.i,self.currentPage,G.PageNames[self.currentPage])&lt;br /&gt;     activate(p,p.execute())&lt;br /&gt;     yield hold,self,self.g.expovariate(self.rate)&lt;br /&gt;     randomPath = RandomPath()&lt;br /&gt;&lt;br /&gt;     if self.currentPage == G.Exit:&lt;br /&gt;       self.currentPage = G.Home&lt;br /&gt;     else:&lt;br /&gt;       self.currentPage = randomPath.NextPage(G.TransitionMatrix, self.currentPage)&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;&lt;br /&gt;    Lambda      = 4.026*float(sys.argv[1])&lt;br /&gt;    maxSimTime  = float(sys.argv[2])&lt;br /&gt;&lt;br /&gt;    initialize()&lt;br /&gt;    g = Generator(Lambda, maxSimTime)&lt;br /&gt;    activate(g,g.execute())&lt;br /&gt;&lt;br /&gt;    simulate(until=maxSimTime)&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Simulated Seconds    : ", maxSimTime&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Page Hits            :"&lt;br /&gt;    for i in range(len(G.PageNames)):&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\t", G.PageNames[i], " = ", G.HitCount[i]&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Throughput           : "&lt;br /&gt;    for i in range(len(G.PageNames)):&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\t", G.PageNames[i], " = ", G.HitCount[i]/maxSimTime&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean Response Times:"&lt;br /&gt;&lt;br /&gt;    for i in G.metrics.Keys():&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\t", i, " = ", G.metrics.Mean(i)&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Component Waiting Queues:"&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server CPU          : ", G.QMon[G.WS][G.CPU].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server DISK         : ", G.QMon[G.WS][G.DISK].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server CPU  : ", G.QMon[G.AS][G.CPU].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server DISK : ", G.QMon[G.AS][G.DISK].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server CPU     : ", G.QMon[G.DS][G.CPU].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server DISK    : ", G.QMon[G.DS][G.DISK].mean()&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Component Utilization:"&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server CPU          : ", ((G.SMon[G.WS][G.CPU].mean()*len(G.QMon[G.WS][G.CPU]))/maxSimTime)*100&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server DISK         : ", ((G.SMon[G.WS][G.DISK].mean()*len(G.QMon[G.WS][G.DISK]))/maxSimTime)*100&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server CPU  : ", ((G.SMon[G.AS][G.CPU].mean()*len(G.QMon[G.AS][G.CPU]))/maxSimTime)*100&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server DISK : ", ((G.SMon[G.AS][G.DISK].mean()*len(G.QMon[G.AS][G.DISK]))/maxSimTime)*100&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server CPU     : ", ((G.SMon[G.DS][G.CPU].mean()*len(G.QMon[G.DS][G.CPU]))/maxSimTime)*100&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server DISK    : ", ((G.SMon[G.DS][G.DISK].mean()*len(G.QMon[G.DS][G.DISK]))/maxSimTime)*100&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Total Component Hits      :" &lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server CPU          : ", len(G.QMon[G.WS][G.CPU])&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server DISK         : ", len(G.QMon[G.WS][G.DISK])&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server CPU  : ", len(G.QMon[G.AS][G.CPU])&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server DISK : ", len(G.QMon[G.AS][G.DISK])&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server CPU     : ", len(G.QMon[G.DS][G.CPU])&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server DISK    : ", len(G.QMon[G.DS][G.DISK])&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Total Component Thoughput :" &lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server CPU          : ", len(G.QMon[G.WS][G.CPU])/maxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server DISK         : ", len(G.QMon[G.WS][G.DISK])/maxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server CPU  : ", len(G.QMon[G.AS][G.CPU])/maxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server DISK : ", len(G.QMon[G.AS][G.DISK])/maxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server CPU     : ", len(G.QMon[G.DS][G.CPU])/maxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server DISK    : ", len(G.QMon[G.DS][G.DISK])/maxSimTime&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean Component Svc Demand :"&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server CPU          : ", G.SMon[G.WS][G.CPU].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tWeb Server DISK         : ", G.SMon[G.WS][G.DISK].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server CPU  : ", G.SMon[G.AS][G.CPU].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tApplication Server DISK : ", G.SMon[G.AS][G.DISK].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server CPU     : ", G.SMon[G.DS][G.CPU].mean()&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\tDatabase Server DISK    : ", G.SMon[G.DS][G.DISK].mean()&lt;br /&gt;&lt;br /&gt;    print G.HitCount[G.Home]/maxSimTime, ",",  G.metrics.Mean("Home"), ",",  G.metrics.Mean("View"), ",",  G.metrics.Mean("Search"), ",",  G.metrics.Mean("Login"), ",",  G.metrics.Mean("Create"), ",",  G.metrics.Mean("Bid")&lt;br /&gt; &lt;br /&gt;if __name__ == '__main__': &lt;br /&gt;  main()&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-1372608341056667565?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/1372608341056667565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=1372608341056667565' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1372608341056667565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1372608341056667565'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/08/look-everybody-its-simpy-three-tier.html' title='Look, everybody! It&apos;s a SimPy three tier eBiz simulation! But now with extra statistics! Wowee!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-4857439882310508043</id><published>2009-08-30T00:10:00.003-05:00</published><updated>2009-08-30T00:17:35.139-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SimPy'/><title type='text'>Improved single queue SimPy code</title><content type='html'>In &lt;a href="http://adventuresinloadtesting.blogspot.com/2009/08/hey-everybody-lets-do-quick-comparison.html"&gt;this&lt;/a&gt; blog entry I had a comparison of PDQ and SimPy for a single queue. The code that I wrote was pretty cheezy and based upon a very early example from Norm Matloff's &lt;a href="http://heather.cs.ucdavis.edu/~matloff/156/PLN/DESimIntro.pdf"&gt;Introduction to Discrete-Event Simulation and the SimPy Language&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;After writing the SimPy code to solve for the three tier eBiz solution I wanted to go back and correct some of my initial code with the single queue.&lt;br /&gt;&lt;br /&gt;Below is what I feel is better code than my original single queue solution.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;&lt;br /&gt;from SimPy.Simulation import *&lt;br /&gt;from random import Random, expovariate, uniform&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;class G:&lt;br /&gt;  # Rnd         = random.Random(time.mktime(time.localtime()))&lt;br /&gt;  Rnd            = random.Random(12345)&lt;br /&gt;  MyQueue        = Resource(1)&lt;br /&gt;  QMon           = Monitor()&lt;br /&gt;  ServiceTime    = 0.50 &lt;br /&gt;  TotalCalls     = 0L&lt;br /&gt;  TotalResidence = 0L &lt;br /&gt;  TotalWait      = 0L&lt;br /&gt;  TotalService   = 0L&lt;br /&gt;&lt;br /&gt;class WorkLoad(Process):&lt;br /&gt;&lt;br /&gt;  def __init__(self):&lt;br /&gt;    Process.__init__(self)&lt;br /&gt;    self.StartUpTime    = 0.0&lt;br /&gt;&lt;br /&gt;  def Run(self):&lt;br /&gt;    self.StartUpTime = now()&lt;br /&gt;    yield request, self, G.MyQueue                     &lt;br /&gt;    G.TotalWait  += now() - self.StartUpTime&lt;br /&gt;    yield hold, self, G.ServiceTime&lt;br /&gt;    G.QMon.observe(len(G.MyQueue.waitQ));&lt;br /&gt;    G.TotalResidence += now() - self.StartUpTime&lt;br /&gt;    yield release, self, G.MyQueue&lt;br /&gt;    G.TotalCalls += 1&lt;br /&gt;&lt;br /&gt;class Generator(Process):&lt;br /&gt;&lt;br /&gt;  def __init__(self, Lambda):&lt;br /&gt;    Process.__init__(self)&lt;br /&gt;    self.Lambda = Lambda&lt;br /&gt;&lt;br /&gt;  def execute(self):&lt;br /&gt;&lt;br /&gt;    while 1:&lt;br /&gt;      yield hold, self, G.Rnd.expovariate(self.Lambda)&lt;br /&gt;      W = WorkLoad()&lt;br /&gt;      activate(W, W.Run())&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;&lt;br /&gt;    Lambda      = float(sys.argv[1])&lt;br /&gt;    MaxSimTime = 10000.00&lt;br /&gt;&lt;br /&gt;    G.TotalCalls       = 0L&lt;br /&gt;    G.TotalResidence   = 0L&lt;br /&gt;    G.TotalWait        = 0L&lt;br /&gt;    G.TotalService     = 0L&lt;br /&gt;&lt;br /&gt;    initialize()&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "MaxSimTime  = ", MaxSimTime&lt;br /&gt;&lt;br /&gt;    g = Generator(Lambda)&lt;br /&gt;    activate(g, g.execute())&lt;br /&gt;&lt;br /&gt;    simulate(until=MaxSimTime)&lt;br /&gt;&lt;br /&gt;    print Lambda, ",", MaxSimTime, ",", G.TotalCalls, ",", G.TotalCalls/MaxSimTime, ",", G.TotalWait, ",", G.TotalResidence, ",", G.TotalResidence/G.TotalCalls, ",", G.TotalWait/G.TotalCalls, ",", (G.TotalResidence/G.TotalCalls) - (G.TotalWait/G.TotalCalls) , ",", ((G.ServiceTime * G.TotalCalls) / MaxSimTime) * 100 , "%",",",G.QMon.mean()&lt;br /&gt; &lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Ideal Throughput     : ", Lambda&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Simulated Seconds    : ", MaxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Number of calls      : ", G.TotalCalls&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Throughput           : ", G.TotalCalls/MaxSimTime&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Total Wait Time      : ", G.TotalWait&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Total Residence Time : ", G.TotalResidence&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean Residence Time  : ", G.TotalResidence/G.TotalCalls&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean Wait Time       : ", G.TotalWait/G.TotalCalls&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean Service Time    : ", (G.TotalResidence/G.TotalCalls) - (G.TotalWait/G.TotalCalls) &lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Total Utilization    : ", ((G.ServiceTime * G.TotalCalls) / MaxSimTime) * 100 , " %"&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean WaitQ           : ", G.QMon.mean()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main()&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The results are the same as before, but now instead of creating a gajillion objects to activate only one "thread" is created that in turns makes the calls to the queue. Much faster than my original code and laid out better.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-4857439882310508043?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/4857439882310508043/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=4857439882310508043' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/4857439882310508043'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/4857439882310508043'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/08/improved-single-queue-simpy-code.html' title='Improved single queue SimPy code'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6921681784213550554</id><published>2009-08-28T18:55:00.007-05:00</published><updated>2009-08-29T15:54:31.262-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SimPy'/><category scheme='http://www.blogger.com/atom/ns#' term='PDQ-R'/><title type='text'>Hey, everybody! Let's use SimPy to simulate performance of a three tier eBiz solution!</title><content type='html'>Back in &lt;a href="http://adventuresinloadtesting.blogspot.com/2009/08/hey-everybody-lets-analyze-performance.html"&gt;this&lt;/a&gt; blog post, I wrote about using PDQ-R to analyze the performance of a three tier eBiz solution.&lt;br /&gt;&lt;br /&gt;I had been getting into learning how to simulate queuing networks with SimPy and this week I got my three tier SimPy solution up and running and crunching numbers.&lt;br /&gt;&lt;br /&gt;The SimPy solution was MUCH more involved than the PDQ solution and it took me a bit to wrap my head around all the goodies I needed to know before I started getting good results.&lt;br /&gt;&lt;br /&gt;I started by looking at the &lt;a href="http://simpy.sourceforge.net/examples/Jackson%20network.htm"&gt;jackson.py&lt;/a&gt; code and got an idea of how write the code that was needed to get the job done.&lt;br /&gt;&lt;br /&gt;Like the jackson.py code, I also had three computers but unlike the jackson.py code I had two nodes per computer that represented the CPU and Disk. Get that setup was the real tricky part and I had to bang my wall against the SimPy brick wall for a while until I broke through and figured the solution out.&lt;br /&gt;&lt;br /&gt;Like the jackson.py code, I use a Process to control the rate that I hit web pages by invoking another process that sets up the hits to the individual components that are also processes. One of the big lessons that I learned is that if the service demand for a component is zero, don't make a yield request call for the component resource.&lt;br /&gt;&lt;br /&gt;You'd think that I would have figured that out earlier but it totally slipped past me and it was really kicking my buttocks. Most of the page response times were looking good but in the PDQ analysis, the HomePage didn't have a long response time due to there only being service demand on the Web CPU and Disk and I wasn't seeing that in my SimPy solution.&lt;br /&gt;&lt;br /&gt;Here is what I was seeing in my SimPy solution for page response time for the Home page:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/SphxUPpg3zI/AAAAAAAAAQk/jIJSmT7xAe8/s1600-h/1_Incorrect_HomePage_ResponseTime.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 243px;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/SphxUPpg3zI/AAAAAAAAAQk/jIJSmT7xAe8/s400/1_Incorrect_HomePage_ResponseTime.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375170747878661938" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I had some other chores to do and as I was walking past my machine I noticed out of the corner of my eye some code and it hit me like a ton of bricks. I was making a yield request for each component even if there was no service demand for the component and that yield request was a blocking operation. Well, duh! A few if statements fixed that problem and a quick test showed that the Home Page response time was no diverging from the PDQ analysis like in the above graph!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/SphyUmlRHbI/AAAAAAAAAQs/4EAy0AxUkyE/s1600-h/2_Correct_HomePage_ResponseTime.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 234px;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/SphyUmlRHbI/AAAAAAAAAQs/4EAy0AxUkyE/s400/2_Correct_HomePage_ResponseTime.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375171853546495410" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Wow, what a difference!&lt;br /&gt;&lt;br /&gt;The biggest things that I got out of doing the SimPy solution was that all component requests need to be in their own Processes and if there is no service demand for the component then it should be called at all.&lt;br /&gt;&lt;br /&gt;Perhaps next week I'll add the queuing output and component utilization into my SimPy code for more left/right comparisons against PDQ. But over all, I'm fairly happy with my solution but there is some code that needs to be cleaned up. &lt;br /&gt;&lt;br /&gt;Down below is my code and the comparisons of my SimPy solution versus my PDQ solution.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;&lt;br /&gt;from SimPy.Simulation import *&lt;br /&gt;from random import Random, expovariate, uniform&lt;br /&gt;&lt;br /&gt;class Metrics:&lt;br /&gt;&lt;br /&gt;  metrics = dict()&lt;br /&gt;&lt;br /&gt;  def Add(self, metricName, frameNumber, value):&lt;br /&gt;    if self.metrics.has_key(metricName):&lt;br /&gt;      if self.metrics[metricName].has_key(frameNumber):&lt;br /&gt;        self.metrics[metricName][frameNumber].append(value)&lt;br /&gt;      else:&lt;br /&gt;        self.metrics[metricName][frameNumber] = list()&lt;br /&gt;        self.metrics[metricName][frameNumber].append(value)&lt;br /&gt;    else:&lt;br /&gt;      self.metrics[metricName] = dict()&lt;br /&gt;      self.metrics[metricName][frameNumber] = list()&lt;br /&gt;      self.metrics[metricName][frameNumber].append(value)&lt;br /&gt;&lt;br /&gt;  def Keys(self):&lt;br /&gt;    return self.metrics.keys()&lt;br /&gt;&lt;br /&gt;  def Mean(self, metricName):&lt;br /&gt;    valueArray = list()&lt;br /&gt;    if self.metrics.has_key(metricName):&lt;br /&gt;      for frame in self.metrics[metricName].keys():&lt;br /&gt;        for values in range(len(self.metrics[metricName][frame])):&lt;br /&gt;          valueArray.append(self.metrics[metricName][frame][values])&lt;br /&gt;&lt;br /&gt;      sum = 0.0&lt;br /&gt;      for i in range(len(valueArray)):&lt;br /&gt;        sum += valueArray[i]&lt;br /&gt;          &lt;br /&gt;      if len(self.metrics[metricName][frame]) != 0:      &lt;br /&gt;        return sum/len(self.metrics[metricName])&lt;br /&gt;      else:&lt;br /&gt;        return 0 # Need to learn python throwing exceptions&lt;br /&gt;    else:&lt;br /&gt;      return 0&lt;br /&gt;&lt;br /&gt;class RandomPath:&lt;br /&gt;&lt;br /&gt;  def RowSum(self, Vector):&lt;br /&gt;    rowSum = 0.0&lt;br /&gt;    for i in range(len(Vector)):&lt;br /&gt;      rowSum += Vector[i]&lt;br /&gt;    return rowSum&lt;br /&gt;&lt;br /&gt;  def NextPage(self, T, i):&lt;br /&gt;    rowSum = self.RowSum(T[i])&lt;br /&gt;    randomValue = G.Rnd.uniform(0, rowSum)&lt;br /&gt;&lt;br /&gt;    sumT = 0.0&lt;br /&gt;    &lt;br /&gt;    for j in range(len(T[i])):&lt;br /&gt;      sumT += T[i][j]&lt;br /&gt;      if randomValue &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt; sumT:&lt;br /&gt;        break&lt;br /&gt;&lt;br /&gt;    return j&lt;br /&gt;&lt;br /&gt;class G: &lt;br /&gt;&lt;br /&gt;  numWS = 1&lt;br /&gt;  numAS = 1&lt;br /&gt;  numDS = 2&lt;br /&gt;&lt;br /&gt;  Rnd         = random.Random(12345)&lt;br /&gt;&lt;br /&gt;  PageNames   = ["Entry", "Home", "Search", "View", "Login", "Create", "Bid", "Exit" ]&lt;br /&gt;&lt;br /&gt;  Entry       = 0&lt;br /&gt;  Home        = 1&lt;br /&gt;  Search      = 2&lt;br /&gt;  View        = 3&lt;br /&gt;  Login       = 4&lt;br /&gt;  Create      = 5&lt;br /&gt;  Bid         = 6&lt;br /&gt;  Exit        = 7&lt;br /&gt;&lt;br /&gt;  WS          = 0&lt;br /&gt;  AS          = 1&lt;br /&gt;  DS          = 2&lt;br /&gt;&lt;br /&gt;  CPU         = 0&lt;br /&gt;  DISK        = 1&lt;br /&gt;&lt;br /&gt;  WS_CPU      = 0&lt;br /&gt;  WS_DISK     = 1&lt;br /&gt;  AS_CPU      = 2&lt;br /&gt;  AS_DISK     = 3&lt;br /&gt;  DS_CPU      = 4&lt;br /&gt;  DS_DISK     = 5&lt;br /&gt;&lt;br /&gt;  metrics     = Metrics()&lt;br /&gt;&lt;br /&gt;  #             e  h  s  v  l  c  b  e&lt;br /&gt;  HitCount   = [0, 0, 0, 0, 0, 0, 0, 0]   &lt;br /&gt;&lt;br /&gt;  Resources = [[ Resource(1), Resource(1) ], # WS CPU and DISK&lt;br /&gt;               [ Resource(1), Resource(1) ], # AS CPU and DISK&lt;br /&gt;               [ Resource(1), Resource(1) ]] # DS CPU and DISK&lt;br /&gt;&lt;br /&gt;  #                  Enter  Home   Search View   Login  Create Bid    Exit&lt;br /&gt;  ServiceDemand = [ [0.000, 0.008, 0.009, 0.011, 0.060, 0.012, 0.015, 0.000],  # WS_CPU&lt;br /&gt;                    [0.000, 0.030, 0.010, 0.010, 0.010, 0.010, 0.010, 0.000],  # WS_DISK&lt;br /&gt;                    [0.000, 0.000, 0.030, 0.035, 0.025, 0.045, 0.040, 0.000],  # AS_CPU&lt;br /&gt;                    [0.000, 0.000, 0.008, 0.080, 0.009, 0.011, 0.012, 0.000],  # AS_DISK&lt;br /&gt;                    [0.000, 0.000, 0.010, 0.009, 0.015, 0.070, 0.045, 0.000],  # DS_CPU&lt;br /&gt;                    [0.000, 0.000, 0.035, 0.018, 0.050, 0.080, 0.090, 0.000] ] # DS_DISK&lt;br /&gt;&lt;br /&gt;  # Type B shopper&lt;br /&gt;  #                     0     1     2     3     4     5     6     7&lt;br /&gt;  TransitionMatrix = [ [0.00, 1.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00],  # 0&lt;br /&gt;                       [0.00, 0.00, 0.70, 0.00, 0.10, 0.00, 0.00, 0.20],  # 1&lt;br /&gt;                       [0.00, 0.00, 0.45, 0.15, 0.10, 0.00, 0.00, 0.30],  # 2&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.40, 0.00, 0.00, 0.60],  # 3&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.30, 0.55, 0.15],  # 4&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00],  # 5&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 1.00],  # 6&lt;br /&gt;                       [0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00, 0.00] ] # 7&lt;br /&gt;&lt;br /&gt;class DoWork(Process):&lt;br /&gt;&lt;br /&gt;  def __init__(self, i, resource, serviceDemand, nodeName, pageName):&lt;br /&gt;    Process.__init__(self)&lt;br /&gt;    self.frame         = i&lt;br /&gt;    self.resource      = resource&lt;br /&gt;    self.serviceDemand = serviceDemand&lt;br /&gt;    self.nodeName      = nodeName&lt;br /&gt;    self.pageName      = pageName&lt;br /&gt;&lt;br /&gt;  def execute(self):&lt;br /&gt;    StartUpTime = now()&lt;br /&gt;    yield request, self, self.resource&lt;br /&gt;    yield hold, self, self.serviceDemand&lt;br /&gt;    yield release, self, self.resource&lt;br /&gt;    R = now() -  StartUpTime&lt;br /&gt;&lt;br /&gt;    G.metrics.Add(self.pageName, self.frame, R)&lt;br /&gt;&lt;br /&gt;class CallPage(Process):&lt;br /&gt;&lt;br /&gt;  def __init__(self, i, node, pageName):&lt;br /&gt;    Process.__init__(self)&lt;br /&gt;    self.frame          = i&lt;br /&gt;    self.StartUpTime    = 0.0&lt;br /&gt;    self.currentPage    = node&lt;br /&gt;    self.pageName       = pageName&lt;br /&gt;&lt;br /&gt;  def execute(self):&lt;br /&gt;&lt;br /&gt;    if self.currentPage != G.Exit:&lt;br /&gt;&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Working on Frame # ", self.frame, " @ ", now() , " for page ", self.pageName&lt;br /&gt;&lt;br /&gt;      self.StartUpTime = now()&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.WS_CPU][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        wsCPU = DoWork(self.frame, G.Resources[G.WS][G.CPU],  G.ServiceDemand[G.WS_CPU][self.currentPage]/G.numWS, "wsCPU", self.pageName) &lt;br /&gt;        activate(wsCPU,  wsCPU.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.WS_DISK][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        wsDISK = DoWork(self.frame, G.Resources[G.WS][G.DISK], G.ServiceDemand[G.WS_DISK][self.currentPage]/G.numWS, "wsDISK", self.pageName)&lt;br /&gt;        activate(wsDISK, wsDISK.execute())&lt;br /&gt; &lt;br /&gt;      if G.ServiceDemand[G.AS_CPU][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        asCPU = DoWork(self.frame, G.Resources[G.AS][G.CPU],  G.ServiceDemand[G.AS_CPU][self.currentPage]/G.numAS, "asCPU", self.pageName)&lt;br /&gt;        activate(asCPU,  asCPU.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.AS_DISK][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        asDISK = DoWork(self.frame, G.Resources[G.AS][G.DISK], G.ServiceDemand[G.AS_DISK][self.currentPage]/G.numAS, "asDISK", self.pageName)&lt;br /&gt;        activate(asDISK, asDISK.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.DS_CPU][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        dsCPU = DoWork(self.frame, G.Resources[G.DS][G.CPU],  G.ServiceDemand[G.DS_CPU][self.currentPage]/G.numDS, "dsCPU", self.pageName)&lt;br /&gt;        activate(dsCPU,  dsCPU.execute())&lt;br /&gt;&lt;br /&gt;      if G.ServiceDemand[G.DS_DISK][self.currentPage] &lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; 0.0:&lt;br /&gt;        dsDISK = DoWork(self.frame, G.Resources[G.DS][G.DISK], G.ServiceDemand[G.DS_DISK][self.currentPage]/G.numDS, "dsDISK", self.pageName)&lt;br /&gt;        activate(dsDISK, dsDISK.execute())&lt;br /&gt;&lt;br /&gt;      G.HitCount[self.currentPage] += 1&lt;br /&gt;&lt;br /&gt;      yield hold, self, 0.00001 # Needed to prevent an error. Doesn't add any blocking to the six queues above&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;class Generator(Process):&lt;br /&gt; def __init__(self, rate, maxT, maxN):&lt;br /&gt;     Process.__init__(self)&lt;br /&gt;     self.name = "Generator"&lt;br /&gt;     self.rate = rate&lt;br /&gt;     self.maxN = maxN&lt;br /&gt;     self.maxT = maxT&lt;br /&gt;     self.g = Random(11335577)&lt;br /&gt;     self.i = 0&lt;br /&gt;     self.currentPage = G.Home&lt;br /&gt;&lt;br /&gt; def execute(self):&lt;br /&gt;   while (now() &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt; self.maxT): &lt;br /&gt;     self.i+=1&lt;br /&gt;     p = CallPage(self.i,self.currentPage,G.PageNames[self.currentPage])&lt;br /&gt;     activate(p,p.execute())&lt;br /&gt;     yield hold,self,self.g.expovariate(self.rate)&lt;br /&gt;     randomPath = RandomPath()&lt;br /&gt;&lt;br /&gt;     if self.currentPage == G.Exit:&lt;br /&gt;       self.currentPage = G.Home&lt;br /&gt;     else:&lt;br /&gt;       self.currentPage = randomPath.NextPage(G.TransitionMatrix, self.currentPage)&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;&lt;br /&gt;    maxWorkLoad = 10000&lt;br /&gt;    Lambda      = 4.026*float(sys.argv[1])&lt;br /&gt;    maxSimTime  = float(sys.argv[2])&lt;br /&gt;&lt;br /&gt;    initialize()&lt;br /&gt;    g = Generator(Lambda, maxSimTime, maxWorkLoad)&lt;br /&gt;    activate(g,g.execute())&lt;br /&gt;&lt;br /&gt;    simulate(until=maxSimTime)&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Simulated Seconds    : ", maxSimTime&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Page Hits            :"&lt;br /&gt;    for i in range(len(G.PageNames)):&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\t", G.PageNames[i], " = ", G.HitCount[i]&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Throughput           : "&lt;br /&gt;    for i in range(len(G.PageNames)):&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\t", G.PageNames[i], " = ", G.HitCount[i]/maxSimTime&lt;br /&gt;&lt;br /&gt;    print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "Mean Response Times:"&lt;br /&gt;&lt;br /&gt;    for i in G.metrics.Keys():&lt;br /&gt;      print &lt;span class="kwrd"&gt;&amp;gt;&amp;gt;&lt;/span&gt; sys.stderr, "\t", i, " = ", G.metrics.Mean(i)&lt;br /&gt;&lt;br /&gt;    print G.HitCount[G.Home]/maxSimTime, ",",  G.metrics.Mean("Home"), ",",  G.metrics.Mean("View"), ",",  G.metrics.Mean("Search"), ",",  G.metrics.Mean("Login"), ",",  G.metrics.Mean("Create"), ",",  G.metrics.Mean("Bid")&lt;br /&gt; &lt;br /&gt;if __name__ == '__main__': main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/SphyUmlRHbI/AAAAAAAAAQs/4EAy0AxUkyE/s1600-h/2_Correct_HomePage_ResponseTime.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 234px;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/SphyUmlRHbI/AAAAAAAAAQs/4EAy0AxUkyE/s400/2_Correct_HomePage_ResponseTime.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375171853546495410" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/Sph1ThpPmcI/AAAAAAAAARU/dlq2jUFXtbg/s1600-h/7_Bid.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 220px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/Sph1ThpPmcI/AAAAAAAAARU/dlq2jUFXtbg/s400/7_Bid.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375175133576010178" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/Sph1TcTFAWI/AAAAAAAAARM/lsGNqO68Xk4/s1600-h/6_Create.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 220px;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/Sph1TcTFAWI/AAAAAAAAARM/lsGNqO68Xk4/s400/6_Create.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375175132140863842" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/Sph1Sy9QD2I/AAAAAAAAARE/pBhr1cCI5Sk/s1600-h/5_Login.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 218px;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/Sph1Sy9QD2I/AAAAAAAAARE/pBhr1cCI5Sk/s400/5_Login.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375175121043459938" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sph1SqUioTI/AAAAAAAAAQ8/hjZ4KYo2dNo/s1600-h/4_Search.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 217px;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sph1SqUioTI/AAAAAAAAAQ8/hjZ4KYo2dNo/s400/4_Search.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375175118725226802" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sph1STOb0lI/AAAAAAAAAQ0/rNjzTbksED8/s1600-h/3_View.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 217px;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sph1STOb0lI/AAAAAAAAAQ0/rNjzTbksED8/s400/3_View.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5375175112525599314" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Like my previous comparison of SimPy versus PDQ for a single queue, the PDQ results are slightly higher, so no major surprises there.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6921681784213550554?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6921681784213550554/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6921681784213550554' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6921681784213550554'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6921681784213550554'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/08/hey-everybody-lets-use-simpy-to.html' title='Hey, everybody! Let&apos;s use SimPy to simulate performance of a three tier eBiz solution!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qsa4nRIPmPo/SphxUPpg3zI/AAAAAAAAAQk/jIJSmT7xAe8/s72-c/1_Incorrect_HomePage_ResponseTime.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-47073565707660381</id><published>2009-08-24T13:44:00.007-05:00</published><updated>2009-08-25T13:50:00.553-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SimPy'/><category scheme='http://www.blogger.com/atom/ns#' term='PDQ-R'/><title type='text'>Hey, everybody! Let's do a quick comparison of SimPy and PDQ for a single queue!</title><content type='html'>On the side I have slowly been looking into SimPy for queue simulation and I finally got around to doing a comparison of a single queue written up with SimPy and with PDQ.&lt;br /&gt;&lt;br /&gt;The scenario is very simple. A single queue with a service time of 0.5 seconds with one visit for a service demand total of 0.5 seconds.&lt;br /&gt;&lt;br /&gt;The PDQ solution is very simple to write up and only took a few minutes:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;library("pdq");&lt;br /&gt;&lt;br /&gt;lambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- seq(1, 20, 1);&lt;br /&gt;lambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- lambda / 10;&lt;br /&gt;&lt;br /&gt;for (rate in lambda) {&lt;br /&gt;  Init("Single Queue");&lt;br /&gt;&lt;br /&gt;  CreateOpen("WorkLoad", rate);&lt;br /&gt;&lt;br /&gt;  CreateNode("MyQueue",   CEN, FCFS);&lt;br /&gt;&lt;br /&gt;  SetDemand("MyQueue",  "WorkLoad" , 0.5);&lt;br /&gt;&lt;br /&gt;  SetWUnit("Trans");&lt;br /&gt;  SetTUnit("Second");&lt;br /&gt;&lt;br /&gt;  Solve(CANON);&lt;br /&gt;  #Report();&lt;br /&gt;  print(sprintf("%f, %f, %f", &lt;br /&gt;                rate, &lt;br /&gt;                GetResidenceTime("MyQueue","WorkLoad", TRANS), &lt;br /&gt;                GetUtilization("MyQueue", "WorkLoad", TRANS)));&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The SimPy solution took a bit longer for me to write up and to make sure I was doing things correctly. I thought it was neat that I was able to setup a lambda into the queue that was greater than the queue was capable of processing, in this case, the lambda max is 2 requests per second as is given by the reciprocal of 0.5 seconds, 1/0.5 == 2.&lt;br /&gt;&lt;br /&gt;PDQ gives a response time of "Inf" for 2.0 requests per second where as the SimPy solution will crunch away, but never get above 2 requests per second and will result with unbounded queueing as we'd expect.&lt;br /&gt;&lt;br /&gt;I still need to work on my SimPy solution to add in more metrics collection, but it's a start. After I wrap my brain around metrics collection I'm going to write a solution of one of the previous problems that I worked with related to Jackson networks and the three tier E-biz solution.&lt;br /&gt;&lt;br /&gt;One of the interesting things about SimPy is that I had to create a large number of worker "threads" to hit the queue over a long period of time to get to the correct lambda that I had specified. If I only created one worker "thread" there was no queueing taking place and wait time was always zero, as you'd normally expect. I played around a lot with the number of worker "threads" and found that for my open network that between 10,000 to 100,000 workers worked out just fine and that 10,000 seconds was an adequate time frame for the solution.&lt;br /&gt;&lt;br /&gt;One of the first things I had to handle with the large number of worker "threads" was to control the inter arrival rate so that the requests per second of all the workers would be the correct value. After that, it was just playing around with the code to find a good number of simulation time and workers.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;#!/usr/bin/env python&lt;br /&gt;&lt;br /&gt;from SimPy.Simulation import *&lt;br /&gt;from random import Random, expovariate, uniform&lt;br /&gt;import time&lt;br /&gt;&lt;br /&gt;class G:&lt;br /&gt;  Rnd         = random.Random(12345)&lt;br /&gt;  MyQueue     = Resource(1)&lt;br /&gt;  QMon        = Monitor()&lt;br /&gt;  ServiceTime = 0.50&lt;br /&gt;&lt;br /&gt;class WorkLoad(Process):&lt;br /&gt;&lt;br /&gt;  TotalCalls       = 0L&lt;br /&gt;  TotalResidence   = 0L &lt;br /&gt;  TotalWait        = 0L&lt;br /&gt;  TotalService     = 0L&lt;br /&gt;&lt;br /&gt;  def __init__(self):&lt;br /&gt;    Process.__init__(self) &lt;br /&gt;    self.StartUpTime    = 0.0&lt;br /&gt;&lt;br /&gt;  def Run(self, MaxWorkLoad, Lambda):&lt;br /&gt;    InterArrivalRate = (1.0/Lambda)&lt;br /&gt;    while 1:&lt;br /&gt;      WaitTime = G.Rnd.expovariate(1.0/(InterArrivalRate*MaxWorkLoad))  &lt;br /&gt;      yield hold, self, WaitTime&lt;br /&gt;      self.StartUpTime = now()&lt;br /&gt;      yield request, self, G.MyQueue                     &lt;br /&gt;      WorkLoad.TotalWait  += now() - self.StartUpTime      # W&lt;br /&gt;      yield hold, self, G.ServiceTime                      # S&lt;br /&gt;      G.QMon.observe(len(G.MyQueue.waitQ));&lt;br /&gt;      WorkLoad.TotalResidence += now() - self.StartUpTime  # R = W + S&lt;br /&gt;      WorkLoad.TotalCalls += 1&lt;br /&gt;      yield release, self, G.MyQueue&lt;br /&gt;&lt;br /&gt;def main():&lt;br /&gt;&lt;br /&gt;    MaxWorkLoad = 1000&lt;br /&gt;    Lambda      = float(sys.argv[1])&lt;br /&gt;    MaxSimTime = 1000.00&lt;br /&gt;&lt;br /&gt;    WorkLoad.TotalCalls       = 0L&lt;br /&gt;    WorkLoad.TotalResidence   = 0L&lt;br /&gt;    WorkLoad.TotalWait        = 0L&lt;br /&gt;    WorkLoad.TotalService     = 0L&lt;br /&gt;&lt;br /&gt;    initialize()&lt;br /&gt;&lt;br /&gt;    print "MaxWorkLoad = ", MaxWorkLoad&lt;br /&gt;    print "MaxSimTime  = ", MaxSimTime&lt;br /&gt;&lt;br /&gt;    for I in range(MaxWorkLoad):&lt;br /&gt;      W = WorkLoad()&lt;br /&gt;      activate(W, W.Run(MaxWorkLoad, Lambda))&lt;br /&gt;&lt;br /&gt;    TotalWorkLoad = I + 1&lt;br /&gt;&lt;br /&gt;    simulate(until=MaxSimTime)&lt;br /&gt;&lt;br /&gt;    print "Ideal Throughput     : ", Lambda&lt;br /&gt;    print "Simulated Seconds    : ", MaxSimTime&lt;br /&gt;    print "TotalWorkLoads       : ", TotalWorkLoad&lt;br /&gt;    print "Number of calls      : ", WorkLoad.TotalCalls&lt;br /&gt;    print "Throughput           : ", WorkLoad.TotalCalls/MaxSimTime&lt;br /&gt;    print "Total Wait Time      : ", WorkLoad.TotalWait&lt;br /&gt;    print "Total Residence Time : ", WorkLoad.TotalResidence&lt;br /&gt;    print "Mean Residence Time  : ", WorkLoad.TotalResidence/WorkLoad.TotalCalls&lt;br /&gt;    print "Mean Wait Time       : ", WorkLoad.TotalWait/WorkLoad.TotalCalls&lt;br /&gt;    print "Mean Service Time    : ", (WorkLoad.TotalResidence/WorkLoad.TotalCalls) - &lt;br /&gt;                                     (WorkLoad.TotalWait/WorkLoad.TotalCalls) &lt;br /&gt;    print "Total Utilization    : ", ((G.ServiceTime * WorkLoad.TotalCalls) / MaxSimTime) * 100 , " %"&lt;br /&gt;    print "Mean waitQ           : ", G.QMon.mean()&lt;br /&gt;&lt;br /&gt;if __name__ == '__main__': main()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I still have a lot of learning to do with SimPy, that's for sure. It's been almost a decade since I've messed with Python and I am really rusty. Time to take care of that.&lt;br /&gt;&lt;br /&gt;Here is the comparison of metrics for utilization and response time for the single queue with both solutions in handy, dandy line graph form:&lt;br /&gt;&lt;br /&gt;Utilization:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/SpLjkDyjo2I/AAAAAAAAAQM/GMNtGi6Y2OY/s1600-h/1_Utilization.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 220px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/SpLjkDyjo2I/AAAAAAAAAQM/GMNtGi6Y2OY/s400/1_Utilization.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5373607514038575970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;No surprises here as both lines are almost the same.&lt;br /&gt;&lt;br /&gt;Response Time:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/SpLjyaFjx4I/AAAAAAAAAQU/KR8ggAPOuhA/s1600-h/2_ResponseTime.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 219px;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/SpLjyaFjx4I/AAAAAAAAAQU/KR8ggAPOuhA/s400/2_ResponseTime.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5373607760542025602" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is the big surprise for me. As utilization increases the response times between SimPy and PDQ start to diverge. At low utilizations there almost the same but look at around 30% utilization (0.6 requests per sec) and the divergence starts. I knew that there would be some differences between SimPy and PDQ but I didn't expect this much of a difference. I need to go over the SimPy solution and make sure I didn't fat finger anything as this is my first SimPy solution for a queue.&lt;br /&gt;&lt;br /&gt;Queue Waiting:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/SpQxlnqxwUI/AAAAAAAAAQc/XzbjPiOneaI/s1600-h/3_waitQ.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 200px;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/SpQxlnqxwUI/AAAAAAAAAQc/XzbjPiOneaI/s400/3_waitQ.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5373974777733169474" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Like the Response Time graph, we show that PDQ is gonkulating more queuing as a result of higher expected response times than the SimPy model.&lt;br /&gt;&lt;br /&gt;While PDQ will respond with "Inf" for 2.0 requests per second and error out with anything greater than 2 req/sec the SimPy solution response time is asymptotic to 2 requests per second. I can push the SimPy solution is 1.9999 requests per second but never hit exactly 2.&lt;br /&gt;&lt;br /&gt;Here is an example of the SimPy solution with 2.1 requests per second:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;auswipe@auswipe-desktop:~/Desktop/pbe/simpy$ ./q.py 2.1&lt;br /&gt;MaxWorkLoad =  10000&lt;br /&gt;MaxSimTime  =  10000.0&lt;br /&gt;Ideal Throughput     :  2.1&lt;br /&gt;Simulated Seconds    :  10000.0&lt;br /&gt;TotalWorkLoads       :  10000&lt;br /&gt;Number of calls      :  19976&lt;br /&gt;Throughput           :  1.9976&lt;br /&gt;Total Wait Time      :  1996958.71394&lt;br /&gt;Total Residence Time :  2006728.44111&lt;br /&gt;Mean Residence Time  :  100.45697042&lt;br /&gt;Mean Wait Time       :  99.9678971738&lt;br /&gt;Mean Service Time    :  0.489073246233&lt;br /&gt;Total Utilization    :  99.88  %&lt;br /&gt;Mean WaitQ           :  18.6269640142&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I've still got a long way to go with SimPy but I thought this was a decent first attempt with a simple problem.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-47073565707660381?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/47073565707660381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=47073565707660381' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/47073565707660381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/47073565707660381'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/08/hey-everybody-lets-do-quick-comparison.html' title='Hey, everybody! Let&apos;s do a quick comparison of SimPy and PDQ for a single queue!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_qsa4nRIPmPo/SpLjkDyjo2I/AAAAAAAAAQM/GMNtGi6Y2OY/s72-c/1_Utilization.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-2413373042180786222</id><published>2009-08-08T11:27:00.012-05:00</published><updated>2009-08-10T14:41:32.002-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='TeamQuest Model'/><category scheme='http://www.blogger.com/atom/ns#' term='PDQ-R'/><title type='text'>Manual model building TeamQuest Model gotcha</title><content type='html'>A while back I started to play around with &lt;a href="http://www.amazon.com/Analyzing-Computer-Systems-Performance-Perl/dp/3540208658/ref=sr_1_1?ie=UTF8&amp;qid=1249749363&amp;sr=8-1"&gt;Perl::PDQ&lt;/a&gt; and decided in order to help me learn the R language that I'd just stick with PDQ-R for now. At work I don't have a system yet in place where I can perform measurements and comparisons of actual response times versus PDQ-R results.&lt;br /&gt;&lt;br /&gt;I did the best next thing for me, which was to compare the results in a case study. As with some of my previous blog entries I was using case studies from &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;qid=1249749029&amp;sr=8-1"&gt;Performance By Design&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;For my first attempt of PDQ-R coding I was using the baseline from Chapter 5: Case Study I: A Database Service. This chapter is pretty nifty because it covers the topic of cluster analysis for workload analysis with three classes of workload. That led me to learning the basics of cluster analysis with R.&lt;br /&gt;&lt;br /&gt;Here was my solution for the baseline found in 5.4 of &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;qid=1249749029&amp;sr=8-1"&gt;Performance By Design&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;library("pdq");&lt;br /&gt;&lt;br /&gt;# Total req/sec into the open queue model&lt;br /&gt;lambda_into_system &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1.33;&lt;br /&gt;&lt;br /&gt;# Split up the req/sec into each class based upon&lt;br /&gt;# previously supplied ratios&lt;br /&gt;&lt;br /&gt;coeff &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c(0.33/1.33, 0.53/1.33, 0.47/1.33);&lt;br /&gt;&lt;br /&gt;lambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- coeff * lambda_into_system;&lt;br /&gt;&lt;br /&gt;#                 Class1,Class2,Class3&lt;br /&gt;cpu_demand   &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c(0.096, 0.615, 0.193);&lt;br /&gt;disk1_demand &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c(0.088, 0.683, 0.763);&lt;br /&gt;disk2_demand &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c(0.119, 0.795, 0.400);&lt;br /&gt;&lt;br /&gt;workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:3;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Init("Chapter 5.4");&lt;br /&gt;&lt;br /&gt;for (n in 1:3) {&lt;br /&gt;  workStreamName[n] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("class_%d", n);&lt;br /&gt;  CreateOpen(workStreamName[n], lambda[n]);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;CreateNode("CPU",   CEN, FCFS);&lt;br /&gt;CreateNode("Disk1", CEN, FCFS);&lt;br /&gt;CreateNode("Disk2", CEN, FCFS);&lt;br /&gt;&lt;br /&gt;for (n in 1:3) {&lt;br /&gt;  SetDemand("CPU",   workStreamName[n], cpu_demand[n]  );&lt;br /&gt;  SetDemand("Disk1", workStreamName[n], disk1_demand[n]);&lt;br /&gt;  SetDemand("Disk2", workStreamName[n], disk2_demand[n]);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;SetWUnit("Trans");&lt;br /&gt;SetTUnit("Second");&lt;br /&gt;&lt;br /&gt;Solve(CANON);&lt;br /&gt;#Report();&lt;br /&gt;&lt;br /&gt;response &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:3;&lt;br /&gt;&lt;br /&gt;for (n in 1:3) {&lt;br /&gt;  response[n] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- GetResponse(TRANS, workStreamName[n]       );&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;for (n in 1:3) {&lt;br /&gt;  print(sprintf("  PDQ-R computation shows response time for class %d is %f seconds", n, response[n]));&lt;br /&gt;};&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Which yields the results of:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; source('baseline.R')&lt;br /&gt;[1] "  PDQ-R computation shows response time for class 1 is 0.864179 seconds"&lt;br /&gt;[1] "  PDQ-R computation shows response time for class 2 is 6.105397 seconds"&lt;br /&gt;[1] "  PDQ-R computation shows response time for class 3 is 4.535833 seconds"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;These values match the results in the book and the available Excel spreadsheet the authors developed in support of the book.&lt;br /&gt;&lt;br /&gt;After I coded my PDQ-R solution I started to look into TeamQuest Model for capacity analysis and decided to do the same case study and see if the numbers agreed between all three sources.&lt;br /&gt;&lt;br /&gt;I developed my TeamQuest Model and setup the visits and service time based upon the computed service demand for all three classes of workload:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sn2sKS22bwI/AAAAAAAAAPs/frwWhoC2r1k/s1600-h/1_TeamQuest_ManualModel_ARWL.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 135px;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sn2sKS22bwI/AAAAAAAAAPs/frwWhoC2r1k/s400/1_TeamQuest_ManualModel_ARWL.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5367635623756066562" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;However, the results did not match at all!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/Sn2sUsQuKKI/AAAAAAAAAP0/bYBstbfmh6Y/s1600-h/2_TeamQuest_ManualModel_Results.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 51px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/Sn2sUsQuKKI/AAAAAAAAAP0/bYBstbfmh6Y/s400/2_TeamQuest_ManualModel_Results.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5367635802374154402" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I contacted the TeamQuest folks with what I had done and showed my initial work both with PDQ-R and computations by hand and figured that I was doing something wrong with TeamQuest Model. I wanted to know what was up with the difference in values.&lt;br /&gt;&lt;br /&gt;TeamQuest tech support finally got back to me with the solution. It appears that TeamQuest Model expects the service time to be set to 0.001 seconds and the number of visits modified to meet the service demand desired. I must've overlooked that in the TeamQuest Model tutorial but sure enough, it works. After sending some e-mails back and forth with TeamQuest it appears that the 0.001 second service time limit is only for CPUs. I am told that it is OK to use the actual visits and service times with AR IO's, but I haven't tested it out yet.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sn2tINOnk7I/AAAAAAAAAP8/eG4Y2VZsSoc/s1600-h/3_TeamQuest_ManualModel_Corrected_ARWL.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 134px;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/Sn2tINOnk7I/AAAAAAAAAP8/eG4Y2VZsSoc/s400/3_TeamQuest_ManualModel_Corrected_ARWL.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5367636687397032882" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Results with:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/Sn2uc-zxGMI/AAAAAAAAAQE/dy8krMm29oM/s1600-h/4_TeamQuest_ManualModel_Corrected_Results.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 49px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/Sn2uc-zxGMI/AAAAAAAAAQE/dy8krMm29oM/s400/4_TeamQuest_ManualModel_Corrected_Results.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5367638143815194818" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Apparently if the TeamQuest agent is used to automatically create models based upon hardware and system utilization this is automatically set and is a non-issue. It's only when building a model from scratch from the ground up that it shows up. It's an easy fix but an unexpected issue.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-2413373042180786222?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/2413373042180786222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=2413373042180786222' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2413373042180786222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2413373042180786222'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/08/manual-model-building-teamquest-model.html' title='Manual model building TeamQuest Model gotcha'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qsa4nRIPmPo/Sn2sKS22bwI/AAAAAAAAAPs/frwWhoC2r1k/s72-c/1_TeamQuest_ManualModel_ARWL.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8663057108957000009</id><published>2009-08-02T21:37:00.010-05:00</published><updated>2009-08-04T22:38:07.034-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Heuristic Analysis'/><category scheme='http://www.blogger.com/atom/ns#' term='PDQ-R'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>Hey, everybody! Let's analyze the performance of a three tier system with PDQ-R!</title><content type='html'>This post is a continuation of &lt;a href="http://adventuresinloadtesting.blogspot.com/2009/07/hey-lets-take-transistion-matrix-and.html"&gt;this&lt;/a&gt; post that I did earlier this week.&lt;br /&gt;&lt;br /&gt;Continuing the solution for Case Study IV: An E-Business Service from the book, &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974818&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;In the previous post I discuss how to take the transition probability matrix and work backwards toward the original series of linear equations for solving the number of visits to a series of web pages. In this case study there are actually two types of visitors that result in two transition probability matrices that must be utilized. There are 25% of Type A visitors and 75% of Type B visitors.&lt;br /&gt;&lt;br /&gt;Each tier of the hypothetical e-biz service is made up by a single CPU and a single disk drive. A matrix is supplied with the total service demand for each component by each page that is hit by visitors.&lt;br /&gt;&lt;br /&gt;While it is simple to write some code to analyze web logs to generate the transition probability matrix based upon customer traffic it is very difficult to isolate the total demand at each component with chaotic customer traffic. But that is why we have load testing tools that are available to us. In a pseudo-production environment we are capable of simulating customer traffic to one page at a time and calculating the total demand for components. In this particular case only the CPU and disk drives are being modeled but for a real service we'd want to model the CPU, disk drives, memory system, network system, etc. &lt;br /&gt;&lt;br /&gt;After running simulated customer traffic against isolated page hits we could generate a similar demand matrix for components and use it for what-if analysis.&lt;br /&gt;&lt;br /&gt;I went ahead and kept my solution in R even though I saw where I thought that a perl solution would be more elegant (did I just use perl and elegant in the same sentence?) I designed my solution with two separate programs, one to spit out numbers and another to generate graphs of page response times and another showing component utilization. Both pieces of code make use of PDQ-R and allow for a variable number of web servers, application servers and database servers.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;# Solution parameters &lt;br /&gt;&lt;br /&gt;gamma  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 10.96;   # Rate into system&lt;br /&gt;numWS  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;       # Number of Web Servers&lt;br /&gt;numAS  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;       # Number of Application Servers&lt;br /&gt;numDS  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;       # Number of Database Servers&lt;br /&gt;&lt;br /&gt;# external library&lt;br /&gt;library("pdq");&lt;br /&gt;&lt;br /&gt;# Constants #&lt;br /&gt;&lt;br /&gt;E &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;&lt;br /&gt;H &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 2;&lt;br /&gt;S &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 3;&lt;br /&gt;V &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 4;&lt;br /&gt;G &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 5;&lt;br /&gt;C &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 6;&lt;br /&gt;B &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 7;&lt;br /&gt;X &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 8;&lt;br /&gt;&lt;br /&gt;PAGE_NAMES &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c("Enter", "HomePage", "Search", "ViewBids", "Login", "CreateAuction", "PlaceBid", "Exit");&lt;br /&gt;COMPONENTS &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c("CPU", "Disk");&lt;br /&gt;SERVER_TYPES &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c("WS", "AS", "DS");&lt;br /&gt;&lt;br /&gt;WS_CPU  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;&lt;br /&gt;WS_DISK &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 2;&lt;br /&gt;AS_CPU  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 3;&lt;br /&gt;AS_DISK &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 4;&lt;br /&gt;DS_CPU  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 5;&lt;br /&gt;DS_DISK &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 6;&lt;br /&gt;&lt;br /&gt;# Functions used in solution&lt;br /&gt;&lt;br /&gt;VisitsByTransitionMatrix &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- function(M, B) {&lt;br /&gt;   A &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- t(M);&lt;br /&gt;   A &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- -1 * A;&lt;br /&gt;   for (i in 1:sqrt(length(A))) {&lt;br /&gt;     j &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- i;&lt;br /&gt;     A[i,j] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- A[i,j] + 1;&lt;br /&gt;   };&lt;br /&gt;   return(solve(A,B));&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;CalculateLambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- function(gamma, f_a, f_b, V_a, V_b, index) {&lt;br /&gt;  return (&lt;br /&gt;           gamma*((f_a*V_a[index]) + (f_b*V_b[index]))&lt;br /&gt;         );&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;f_a    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0.25;    # Fraction of TypeA users&lt;br /&gt;f_b    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1 - f_a; # Fraction of TypeB users&lt;br /&gt;&lt;br /&gt;lambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:X;     # Array of lambda for each page &lt;br /&gt;&lt;br /&gt;SystemInput &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(1,0,0,0,0,0,0,0),nrow=8,ncol=1)                             # 8.3, Figure 8.2, page 208&lt;br /&gt;TypeA       &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(0,1,0,0,0,0,0,0,0,0,0.7,0,0.1,0,0,&lt;br /&gt;                        0.2,0,0,0.4,0.2,0.15,0,0,0.25,0,0,&lt;br /&gt;                        0,0,0.65,0,0,0.35,0,0,0,0,0,0.3,0.6,&lt;br /&gt;                        0.1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,&lt;br /&gt;                        0,0,0,0,0,0,0,0), ncol=8, nrow=8, byrow=TRUE);              # 8.4, Table 8.1, page 209&lt;br /&gt;TypeB       &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(0,1,0,0,0,0,0,0,0,0,0.7,0,0.1,0,0,&lt;br /&gt;                        0.2,0,0,0.45,0.15,0.1,0,0,0.3,0,0,&lt;br /&gt;                        0,0,0.4,0,0,0.6,0,0,0,0,0,0.3,0.55,&lt;br /&gt;                        0.15,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,&lt;br /&gt;                        1,0,0,0,0,0,0,0,0), nrow=8, ncol=8, byrow=TRUE);            # 8.4, Table 8.2, page 210&lt;br /&gt;DemandTable &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(0,0.008,0.009,0.011,0.06,0.012,0.015,&lt;br /&gt;                        0,0,0.03,0.01,0.01,0.01,0.01,0.01,0,&lt;br /&gt;                        0,0,0.03,0.035,0.025,0.045,0.04,0,0,&lt;br /&gt;                        0,0.008,0.08,0.009,0.011,0.012,0,0,&lt;br /&gt;                        0,0.01,0.009,0.015,0.07,0.045,0,0,0,&lt;br /&gt;                        0.035,0.018,0.05,0.08,0.09,0), ncol=8, nrow=6, byrow=TRUE); # 8.4, Table 8.4, page 212 (with modifications)&lt;br /&gt;&lt;br /&gt;VisitsA &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- VisitsByTransitionMatrix(TypeA, SystemInput);&lt;br /&gt;VisitsB &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- VisitsByTransitionMatrix(TypeB, SystemInput);&lt;br /&gt;&lt;br /&gt;lambda[E] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0; # Not used in calculations&lt;br /&gt;lambda[H] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, H);&lt;br /&gt;lambda[S] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, S);&lt;br /&gt;lambda[V] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, V);&lt;br /&gt;lambda[G] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, G);&lt;br /&gt;lambda[C] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, C);&lt;br /&gt;lambda[B] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, B);&lt;br /&gt;lambda[X] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0 # Not used in calculations&lt;br /&gt;&lt;br /&gt;Init("e_biz_service");&lt;br /&gt;&lt;br /&gt;# Define workstreams &lt;br /&gt;&lt;br /&gt;for (n in H:B) {&lt;br /&gt;  workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[n]);&lt;br /&gt;  CreateOpen(workStreamName, lambda[n]);&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Define Web Server Queues&lt;br /&gt;&lt;br /&gt;for (i in 1:numWS) {&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("WS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;    CreateNode(nodeName, CEN, FCFS);&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Define Application Server Queues&lt;br /&gt;&lt;br /&gt;for (i in 1:numAS) {&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("AS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;    CreateNode(nodeName, CEN, FCFS);&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Define Database Server Queues&lt;br /&gt;&lt;br /&gt;for (i in 1:numDS) {&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("DS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;    CreateNode(nodeName, CEN, FCFS);&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Set Demand for the Web Servers&lt;br /&gt;&lt;br /&gt;for (i in 1:numWS) {&lt;br /&gt;  demandIndex &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- WS_CPU;&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("WS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;    for (k in H:B) {&lt;br /&gt;      workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;      SetDemand(nodeName, workStreamName, (DemandTable[demandIndex + (j-1), k])/numWS);&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Set Demand for the App Servers&lt;br /&gt;&lt;br /&gt;for (i in 1:numAS) {&lt;br /&gt;  demandIndex &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- AS_CPU;&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("AS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;    for (k in H:B) {&lt;br /&gt;      workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;      SetDemand(nodeName, workStreamName, (DemandTable[demandIndex + (j-1), k])/numAS);&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Set Demand for the Database Servers&lt;br /&gt;&lt;br /&gt;for (i in 1:numDS) {&lt;br /&gt;  demandIndex &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- DS_CPU;&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("DS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;    for (k in H:B) {&lt;br /&gt;      workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;      SetDemand(nodeName, workStreamName, (DemandTable[demandIndex + (j-1), k])/numDS);&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;SetWUnit("Trans");&lt;br /&gt;SetTUnit("Second");&lt;br /&gt;&lt;br /&gt;Solve(CANON);&lt;br /&gt;&lt;br /&gt;print("Arrival Rates for each page:");&lt;br /&gt;&lt;br /&gt;for (i in H:B) {&lt;br /&gt;  print(sprintf("%s = %f", PAGE_NAMES[i], lambda[i]));&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;print("[-------------------------------------------------]");&lt;br /&gt;&lt;br /&gt;print("Page Response Times");&lt;br /&gt;&lt;br /&gt;for (i in H:B) {&lt;br /&gt;  workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[i]);&lt;br /&gt;  print(sprintf("%s = %f seconds.", PAGE_NAMES[i], GetResponse(TRANS, workStreamName)));&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;print("[-------------------------------------------------]");&lt;br /&gt;&lt;br /&gt;print("Component Utilizations");&lt;br /&gt;&lt;br /&gt;for (i in 1:numWS) {&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("WS_%s_%s", i, COMPONENTS[j]);&lt;br /&gt;    for (k in H:B) {&lt;br /&gt;      workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;      totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization + GetUtilization(nodeName, workStreamName, TRANS);&lt;br /&gt;    };&lt;br /&gt;    print(sprintf("%s = %3.2f %%", nodeName, totalUtilization * 100));&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;for (i in 1:numAS) {&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("AS_%s_%s", i, COMPONENTS[j]);&lt;br /&gt;    for (k in H:B) {&lt;br /&gt;      workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;      totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization + GetUtilization(nodeName, workStreamName, TRANS);&lt;br /&gt;    };&lt;br /&gt;    print(sprintf("%s = %3.2f %%", nodeName, totalUtilization * 100));&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;for (i in 1:numDS) {&lt;br /&gt;  for (j in 1:length(COMPONENTS)) {&lt;br /&gt;    totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;    nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("DS_%s_%s", i, COMPONENTS[j]);&lt;br /&gt;    for (k in H:B) {&lt;br /&gt;      workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;      totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization + GetUtilization(nodeName, workStreamName, TRANS);&lt;br /&gt;    };&lt;br /&gt;    print(sprintf("%s = %3.2f %%", nodeName, totalUtilization * 100));&lt;br /&gt;  };&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Here is a bit of sample output with 10.96 users entering the system per second:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;[1] "Arrival Rates for each page:"&lt;br /&gt;[1] "HomePage = 10.960000"&lt;br /&gt;[1] "Search = 13.658485"&lt;br /&gt;[1] "ViewBids = 2.208606"&lt;br /&gt;[1] "Login = 3.664958"&lt;br /&gt;[1] "CreateAuction = 1.099487"&lt;br /&gt;[1] "PlaceBid = 2.074180"&lt;br /&gt;[1] "[-------------------------------------------------]"&lt;br /&gt;[1] "Page Response Times"&lt;br /&gt;[1] "HomePage = 0.083517 seconds."&lt;br /&gt;[1] "Search = 1.612366 seconds."&lt;br /&gt;[1] "ViewBids = 1.044683 seconds."&lt;br /&gt;[1] "Login = 2.323417 seconds."&lt;br /&gt;[1] "CreateAuction = 3.622690 seconds."&lt;br /&gt;[1] "PlaceBid = 3.983755 seconds."&lt;br /&gt;[1] "[-------------------------------------------------]"&lt;br /&gt;[1] "Component Utilizations"&lt;br /&gt;[1] "WS_1_CPU = 49.91 %"&lt;br /&gt;[1] "WS_1_Disk = 55.59 %"&lt;br /&gt;[1] "AS_1_CPU = 71.11 %"&lt;br /&gt;[1] "AS_1_Disk = 35.59 %"&lt;br /&gt;[1] "DS_1_CPU = 38.17 %"&lt;br /&gt;[1] "DS_1_Disk = 97.57 %"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Take a look at that database server disk utilization. Almost 100%! That isn't any good. Let's run the model with two database servers just to ease up on the poor drives!&lt;br /&gt;&lt;br /&gt;I modify the line that reads "numDS  &lt;- 1;       # Number of Database Servers" to read "numDS  &lt;- 2;       # Number of Database Servers" and let 'er rip:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;[1] "Arrival Rates for each page:"&lt;br /&gt;[1] "HomePage = 10.960000"&lt;br /&gt;[1] "Search = 13.658485"&lt;br /&gt;[1] "ViewBids = 2.208606"&lt;br /&gt;[1] "Login = 3.664958"&lt;br /&gt;[1] "CreateAuction = 1.099487"&lt;br /&gt;[1] "PlaceBid = 2.074180"&lt;br /&gt;[1] "[-------------------------------------------------]"&lt;br /&gt;[1] "Page Response Times"&lt;br /&gt;[1] "HomePage = 0.083517 seconds."&lt;br /&gt;[1] "Search = 0.237452 seconds."&lt;br /&gt;[1] "ViewBids = 0.336113 seconds."&lt;br /&gt;[1] "Login = 0.358981 seconds."&lt;br /&gt;[1] "CreateAuction = 0.462042 seconds."&lt;br /&gt;[1] "PlaceBid = 0.440903 seconds."&lt;br /&gt;[1] "[-------------------------------------------------]"&lt;br /&gt;[1] "Component Utilizations"&lt;br /&gt;[1] "WS_1_CPU = 49.91 %"&lt;br /&gt;[1] "WS_1_Disk = 55.59 %"&lt;br /&gt;[1] "AS_1_CPU = 71.11 %"&lt;br /&gt;[1] "AS_1_Disk = 35.59 %"&lt;br /&gt;[1] "DS_1_CPU = 19.09 %"&lt;br /&gt;[1] "DS_1_Disk = 48.78 %"&lt;br /&gt;[1] "DS_2_CPU = 19.09 %"&lt;br /&gt;[1] "DS_2_Disk = 48.78 %"&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's better. There is a significant reduction in page response time to boot:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;Page            1 DS            2 DS            Diff&lt;br /&gt;HomePage        0.083517        0.083517        0&lt;br /&gt;Search          1.612366        0.237452       -1.374914&lt;br /&gt;ViewBids        1.044683        0.336113       -0.70857&lt;br /&gt;Login           2.323417        0.358981       -1.964436&lt;br /&gt;CreateAuction   3.62269         0.462042       -3.160648&lt;br /&gt;PlaceBid        3.983755        0.440903       -3.542852&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Holy smoke! Adding that second database server makes a heck of a difference, doesn't it?&lt;br /&gt;&lt;br /&gt;Being able to pull up numbers is great, but it doesn't have the impact that a good graph does. I love graphs! They are so great for conveying information to non-technical folks.&lt;br /&gt;&lt;br /&gt;So, to do that I took my original solution and did a code spin, fold and mutilation to allow the generation of graphs. Here is the code that I wrote to generate the graphs.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;# Solution parameters &lt;br /&gt;&lt;br /&gt;maxGamma      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 11.2 # Maximum rate into system&lt;br /&gt;steppingValue &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0.1;&lt;br /&gt;&lt;br /&gt;numWS  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;       # Number of Web Servers&lt;br /&gt;numAS  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;       # Number of Application Servers&lt;br /&gt;numDS  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;       # Number of Database Servers&lt;br /&gt;&lt;br /&gt;# external library&lt;br /&gt;library("pdq");&lt;br /&gt;&lt;br /&gt;# Constants&lt;br /&gt;&lt;br /&gt;E &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;&lt;br /&gt;H &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 2;&lt;br /&gt;S &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 3;&lt;br /&gt;V &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 4;&lt;br /&gt;G &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 5;&lt;br /&gt;C &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 6;&lt;br /&gt;B &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 7;&lt;br /&gt;X &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 8;&lt;br /&gt;&lt;br /&gt;PAGE_NAMES &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c("Enter", "HomePage", "Search", "ViewBids", "Login", "CreateAuction", "PlaceBid", "Exit");&lt;br /&gt;COMPONENTS &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c("CPU", "Disk");&lt;br /&gt;SERVER_TYPES &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- c("WS", "AS", "DS");&lt;br /&gt;&lt;br /&gt;WS_CPU  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;&lt;br /&gt;WS_DISK &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 2;&lt;br /&gt;AS_CPU  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 3;&lt;br /&gt;AS_DISK &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 4;&lt;br /&gt;DS_CPU  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 5;&lt;br /&gt;DS_DISK &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 6;&lt;br /&gt;&lt;br /&gt;# Functions used in solution&lt;br /&gt;&lt;br /&gt;VisitsByTransitionMatrix &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- function(M, B) {&lt;br /&gt;   A &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- t(M);&lt;br /&gt;   A &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- -1 * A;&lt;br /&gt;   for (i in 1:sqrt(length(A))) {&lt;br /&gt;     j &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- i;&lt;br /&gt;     A[i,j] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- A[i,j] + 1;&lt;br /&gt;   };&lt;br /&gt;   return(solve(A,B));&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;CalculateLambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- function(gamma, f_a, f_b, V_a, V_b, index) {&lt;br /&gt;  return (&lt;br /&gt;           gamma*((f_a*V_a[index]) + (f_b*V_b[index]))&lt;br /&gt;         );&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;f_a    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0.25;    # Fraction of TypeA users&lt;br /&gt;f_b    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1 - f_a; # Fraction of TypeB users&lt;br /&gt;&lt;br /&gt;lambda &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:X;     # Array of lambda for each page &lt;br /&gt;&lt;br /&gt;SystemInput &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(1,0,0,0,0,0,0,0),nrow=8,ncol=1)                             # 8.3, Figure 8.2, page 208&lt;br /&gt;TypeA       &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(0,1,0,0,0,0,0,0,0,0,0.7,0,0.1,0,0,&lt;br /&gt;                        0.2,0,0,0.4,0.2,0.15,0,0,0.25,0,0,&lt;br /&gt;                        0,0,0.65,0,0,0.35,0,0,0,0,0,0.3,0.6,&lt;br /&gt;                        0.1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,&lt;br /&gt;                        0,0,0,0,0,0,0,0), ncol=8, nrow=8, byrow=TRUE);              # 8.4, Table 8.1, page 209&lt;br /&gt;TypeB       &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(0,1,0,0,0,0,0,0,0,0,0.7,0,0.1,0,0,&lt;br /&gt;                        0.2,0,0,0.45,0.15,0.1,0,0,0.3,0,0,&lt;br /&gt;                        0,0,0.4,0,0,0.6,0,0,0,0,0,0.3,0.55,&lt;br /&gt;                        0.15,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,&lt;br /&gt;                        1,0,0,0,0,0,0,0,0), nrow=8, ncol=8, byrow=TRUE);            # 8.4, Table 8.2, page 210&lt;br /&gt;DemandTable &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- matrix(c(0,0.008,0.009,0.011,0.06,0.012,0.015,&lt;br /&gt;                        0,0,0.03,0.01,0.01,0.01,0.01,0.01,0,&lt;br /&gt;                        0,0,0.03,0.035,0.025,0.045,0.04,0,0,&lt;br /&gt;                        0,0.008,0.08,0.009,0.011,0.012,0,0,&lt;br /&gt;                        0,0.01,0.009,0.015,0.07,0.045,0,0,0,&lt;br /&gt;                        0.035,0.018,0.05,0.08,0.09,0), ncol=8, nrow=6, byrow=TRUE); # 8.4, Table 8.4, page 212 (with modifications)&lt;br /&gt;&lt;br /&gt;VisitsA &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- VisitsByTransitionMatrix(TypeA, SystemInput);&lt;br /&gt;VisitsB &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- VisitsByTransitionMatrix(TypeB, SystemInput);&lt;br /&gt;&lt;br /&gt;numSteps     &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- (maxGamma/steppingValue)+1;&lt;br /&gt;#numSteps    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- (maxGamma/steppingValue);&lt;br /&gt;numElements  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- numSteps*X;&lt;br /&gt;numUElements &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- numSteps*(3*length(COMPONENTS));&lt;br /&gt;&lt;br /&gt;steppingArray &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:numSteps;&lt;br /&gt;responseArray &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:numElements;&lt;br /&gt;utilArray     &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1:numUElements;&lt;br /&gt;&lt;br /&gt;responseArray &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- responseArray * 0;&lt;br /&gt;utilArray     &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- utilArray * 0;&lt;br /&gt;&lt;br /&gt;componentList &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- length(COMPONENTS)*length(SERVER_TYPES);&lt;br /&gt;&lt;br /&gt;entryNumber &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;&lt;br /&gt;for (serverType in SERVER_TYPES) {&lt;br /&gt;  for (serverComponent in COMPONENTS) {&lt;br /&gt;    componentList[entryNumber] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s %s", serverType, serverComponent); &lt;br /&gt;    entryNumber &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- entryNumber + 1;&lt;br /&gt;  };&lt;br /&gt;}; &lt;br /&gt;&lt;br /&gt;dim(responseArray) = c(X, round(numElements/X));&lt;br /&gt;dim(utilArray) = c(3*length(COMPONENTS), round(numUElements/(3*length(COMPONENTS))));&lt;br /&gt;&lt;br /&gt;loopCount &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 1;&lt;br /&gt;&lt;br /&gt;for (gamma in seq(0, maxGamma, steppingValue)) {&lt;br /&gt;&lt;br /&gt;  steppingArray[loopCount] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- gamma;&lt;br /&gt;&lt;br /&gt;  lambda[E] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0; # Not used in calculations&lt;br /&gt;  lambda[H] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, H);&lt;br /&gt;  lambda[S] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, S);&lt;br /&gt;  lambda[V] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, V);&lt;br /&gt;  lambda[G] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, G);&lt;br /&gt;  lambda[C] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, C);&lt;br /&gt;  lambda[B] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- CalculateLambda(gamma, f_a, f_b, VisitsA, VisitsB, B);&lt;br /&gt;  lambda[X] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0 # Not used in calculations&lt;br /&gt;&lt;br /&gt;  Init("e_biz_service");&lt;br /&gt;&lt;br /&gt;  # Define workstreams &lt;br /&gt;&lt;br /&gt;  for (n in H:B) {&lt;br /&gt;    workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[n]);&lt;br /&gt;    CreateOpen(workStreamName, lambda[n]);&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  # Define Web Server Queues&lt;br /&gt;&lt;br /&gt;  for (i in 1:numWS) {&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("WS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;      CreateNode(nodeName, CEN, FCFS);&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  # Define Application Server Queues&lt;br /&gt;&lt;br /&gt;  for (i in 1:numAS) {&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("AS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;      CreateNode(nodeName, CEN, FCFS);&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  # Define Database Server Queues&lt;br /&gt;&lt;br /&gt;  for (i in 1:numDS) {&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("DS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;      CreateNode(nodeName, CEN, FCFS);&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  # Set Demand for the Web Servers&lt;br /&gt;&lt;br /&gt;  for (i in 1:numWS) {&lt;br /&gt;    demandIndex &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- WS_CPU;&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("WS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;      for (k in H:B) {&lt;br /&gt;        workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;        SetDemand(nodeName, workStreamName, (DemandTable[demandIndex + (j-1), k])/numWS);&lt;br /&gt;      };&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  # Set Demand for the App Servers&lt;br /&gt;&lt;br /&gt;  for (i in 1:numAS) {&lt;br /&gt;    demandIndex &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- AS_CPU;&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("AS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;      for (k in H:B) {&lt;br /&gt;        workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;        SetDemand(nodeName, workStreamName, (DemandTable[demandIndex + (j-1), k])/numAS);&lt;br /&gt;      };&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  # Set Demand for the Database Servers&lt;br /&gt;&lt;br /&gt;  for (i in 1:numDS) {&lt;br /&gt;    demandIndex &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- DS_CPU;&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("DS_%d_%s", i, COMPONENTS[j]);&lt;br /&gt;      for (k in H:B) {&lt;br /&gt;        workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;        SetDemand(nodeName, workStreamName, (DemandTable[demandIndex + (j-1), k])/numDS);&lt;br /&gt;      };&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  Solve(CANON);&lt;br /&gt;&lt;br /&gt;  for (i in H:B) {&lt;br /&gt;    workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[i]);&lt;br /&gt;    responseArray[i, loopCount] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- GetResponse(TRANS, workStreamName);&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  uArrayEntries &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;  for (i in 1:numWS) {&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("WS_%s_%s", i, COMPONENTS[j]);&lt;br /&gt;      for (k in H:B) {&lt;br /&gt;        workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;        totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization + GetUtilization(nodeName, workStreamName, TRANS);&lt;br /&gt;        if (i == 1) {&lt;br /&gt;          utilArray[uArrayEntries+j, loopCount] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization*100;&lt;br /&gt;        };&lt;br /&gt;      };&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  uArrayEntries &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 2;&lt;br /&gt;  for (i in 1:numAS) {&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("AS_%s_%s", i, COMPONENTS[j]);&lt;br /&gt;      for (k in H:B) {&lt;br /&gt;        workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;        totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization + GetUtilization(nodeName, workStreamName, TRANS);&lt;br /&gt;        if (i == 1) {&lt;br /&gt;          utilArray[uArrayEntries+j, loopCount] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization * 100;&lt;br /&gt;        };&lt;br /&gt;      };&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  uArrayEntries &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 4;&lt;br /&gt;  for (i in 1:numDS) {&lt;br /&gt;    for (j in 1:length(COMPONENTS)) {&lt;br /&gt;      totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;      nodeName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("DS_%s_%s", i, COMPONENTS[j]);&lt;br /&gt;      for (k in H:B) {&lt;br /&gt;        workStreamName &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- sprintf("%s", PAGE_NAMES[k]);&lt;br /&gt;        totalUtilization &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization + GetUtilization(nodeName, workStreamName, TRANS);&lt;br /&gt;        if (i == 1) {&lt;br /&gt;          utilArray[uArrayEntries+j, loopCount] &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- totalUtilization * 100;&lt;br /&gt;        };&lt;br /&gt;      };&lt;br /&gt;    };&lt;br /&gt;  };&lt;br /&gt;&lt;br /&gt;  loopCount &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- loopCount + 1;&lt;br /&gt;&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;# Generate Response Time Graph&lt;br /&gt;&lt;br /&gt;loopCount &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;for (i in H:B) {&lt;br /&gt;  arrayLength = numElements/X;&lt;br /&gt;  if (loopCount == 0) {&lt;br /&gt;    jpeg(file=sprintf("response_time_%d_WS_%d_AS_%d_DS.jpg", numWS, numAS, numDS), height=768, width=1024, quality=100);&lt;br /&gt;    plot(steppingArray, &lt;br /&gt;         responseArray[i, 1:arrayLength], &lt;br /&gt;         xlab="Hits Per Second into System", &lt;br /&gt;         ylab="Response Time", &lt;br /&gt;         col=i,&lt;br /&gt;         ylim=c(0,ceiling(max(responseArray))),&lt;br /&gt;         type="l",&lt;br /&gt;         pch=i,&lt;br /&gt;         lwd=4);&lt;br /&gt;    title(main=sprintf("Page Response Times for E-Biz with %d WS, %d AS and %d DS", numWS, numAS, numDS), font.main=2);&lt;br /&gt;  } else {&lt;br /&gt;    lines(steppingArray, responseArray[i, 1:arrayLength], col=i, pch=i, lwd=4);&lt;br /&gt;  };&lt;br /&gt;    &lt;br /&gt;  loopCount &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- loopCount + 1;&lt;br /&gt;};&lt;br /&gt;legend(1, ceiling(max(responseArray)), PAGE_NAMES[H:B], col=H:B, lty=1, lwd=4, cex=1.2);  &lt;br /&gt;dev.off()&lt;br /&gt;&lt;br /&gt;# Graph component utilization&lt;br /&gt;&lt;br /&gt;loopCount &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- 0;&lt;br /&gt;for (i in 1:length(componentList)) {&lt;br /&gt;  arrayLength = numSteps;&lt;br /&gt;  if (loopCount == 0) {&lt;br /&gt;    jpeg(file=sprintf("resource_utilization_%d_WS_%d_AS_%d_DS.jpg", numWS, numAS, numDS), height=768, width=1024, quality=100);&lt;br /&gt;    plot(steppingArray, &lt;br /&gt;         utilArray[i, 1:arrayLength], &lt;br /&gt;         xlab="Hits Per Second into System", &lt;br /&gt;         ylab="Resource Utilization, %", &lt;br /&gt;         col=i,&lt;br /&gt;         ylim=c(0,100),&lt;br /&gt;         type="l",&lt;br /&gt;         pch=i,&lt;br /&gt;         lwd=4);&lt;br /&gt;    title(main=sprintf("Resource Utilization for E-Biz with %d WS, %d AS and %d DS", numWS, numAS, numDS), font.main=2);&lt;br /&gt;  } else {&lt;br /&gt;    lines(steppingArray, utilArray[i, 1:arrayLength], col=i, pch=i, lwd=4);&lt;br /&gt;  };&lt;br /&gt;  loopCount &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;- loopCount + 1;&lt;br /&gt;};&lt;br /&gt;legend(0, 100, componentList, col=1:length(componentList), lty=1, lwd=4, cex=1.2);  &lt;br /&gt;dev.off()&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The code isn't what I would call elegant at this time as a lot of it is still hardcoded for the specific solution but it is a step in the right direction.&lt;br /&gt;&lt;br /&gt;And let's take a look at response time between the single database server and dual load bearing database servers:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZk0k_FSnI/AAAAAAAAAPM/GaRFWBz92cM/s1600-h/response_1DS.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZk0k_FSnI/AAAAAAAAAPM/GaRFWBz92cM/s320/response_1DS.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5365586860502764146" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Take a look at the effect that the overworked database disk drive has on the response time. At 11.2 customers per second into the site and the response time has shot up to the 30 second mark. Definitely not what you want your customers to have to suffer through to be sure.&lt;br /&gt;&lt;br /&gt;Here is a graph generated with the dual load bearing database server solution:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZlRymwKJI/AAAAAAAAAPU/1Pwr7n6XPYo/s1600-h/response_2DS.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZlRymwKJI/AAAAAAAAAPU/1Pwr7n6XPYo/s320/response_2DS.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5365587362375018642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Holy guacamole is that a heck of an improvement or what? Any management type can look at these two graphs and immediately realize the impact to the system and the need for extra equipment.&lt;br /&gt;&lt;br /&gt;And just for good measure, here is the resource utilization with both single and dual database servers:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZmNbM6i8I/AAAAAAAAAPc/UPx5pGs7ioM/s1600-h/resource_1DS.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZmNbM6i8I/AAAAAAAAAPc/UPx5pGs7ioM/s320/resource_1DS.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5365588386884783042" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/SnZmV0dDm_I/AAAAAAAAAPk/fbekb2BTQlo/s1600-h/resource_2DS.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/SnZmV0dDm_I/AAAAAAAAAPk/fbekb2BTQlo/s320/resource_2DS.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5365588531102325746" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Even a MBA grad can look at those graphs and realize that there is a problem that needs to be solved. w00t!&lt;br /&gt;&lt;br /&gt;Ain't heuristic analysis fun?&lt;br /&gt;&lt;br /&gt;Applying PDQ-R to this case study was a great exercise and I'm glad I undertook it. I can't wait to apply this to a real system and compare the results to see how well it works "in the real world." One thing that this type of heuristic analysis won't show is the interaction between pages with negative performance. In some circumstances I have seen performance programs between page that were thought to not be related. When Page A is put under duress Page B slows down even though it is thought that Page A is not related to Page B. Often it has been found that in a spaghetti line of object dependency that in some way the two pages were related. With the analysis of service demand of pages we can also find pages that have a lot of high service demand as well.&lt;br /&gt;&lt;br /&gt;In the future along with page response times and normal metric reporting I think I'll add in service demand as well so that when pages do start to perform poorly hopefully the change in service demand will give an area to look into at the start of the analysis of the root cause of the performance problem to assist the developers.&lt;br /&gt;&lt;br /&gt;Dr. Gunther has some more examples of applying Perl::PDQ to a variety of systems in his book, &lt;a href="http://www.amazon.com/Analyzing-Computer-Systems-Performance-Perl/dp/3540208658/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1249273622&amp;sr=1-1"&gt;Analyzing Computer System Performance with Perl::PDQ&lt;/a&gt;. The source to the solutions in the book can be found in the PDQ download. But developing this solution from the ground up really drove some points home for me that will no doubt be useful in my future endeavors.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8663057108957000009?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8663057108957000009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8663057108957000009' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8663057108957000009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8663057108957000009'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/08/hey-everybody-lets-analyze-performance.html' title='Hey, everybody! Let&apos;s analyze the performance of a three tier system with PDQ-R!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_qsa4nRIPmPo/SnZk0k_FSnI/AAAAAAAAAPM/GaRFWBz92cM/s72-c/response_1DS.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-4271716343440029836</id><published>2009-07-30T12:23:00.006-05:00</published><updated>2009-07-30T14:22:56.100-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linear Algebra'/><category scheme='http://www.blogger.com/atom/ns#' term='Performance By Design'/><category scheme='http://www.blogger.com/atom/ns#' term='Transition Matrix'/><category scheme='http://www.blogger.com/atom/ns#' term='PDQ-R'/><title type='text'>Hey! Let's take a transistion matrix and convert it into a series of linear equations to gonkulate the number of visits to various pages!</title><content type='html'>I've been working on learning the wonderful world of queuing theory. Two of the books that I've been using are &lt;a href="http://www.amazon.com/Analyzing-Computer-Systems-Performance-Perl/dp/3540208658/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974750&amp;sr=8-1"&gt;Analyzing Computer Systems Performance: With Perl: PDQ&lt;/a&gt; and &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974818&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I've been learning how to apply Dr. Gunther's &lt;a href="http://www.perfdynamics.com/Tools/PDQ.html"&gt;PDQ&lt;/a&gt; API, specifically, PDQ-R which is PDQ for the R programming language. Normally I'd use the Perl::PDQ module but I've found R to be quite handy for crunching numbers. If anything, I'd use perl to munge the data in preparation for crunching by routines in R.&lt;br /&gt;&lt;br /&gt;I decided that I would apply PDQ-R to several of the case studies found in &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974818&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;. In my first attempt to do so I found some numbers that didn't match between the results of PDQ-R and the Excel spreadsheet available as part of &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974818&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;. I contacted Dr. Gunther and he found that there was a bug in PDQ 5.0.1 that he blogged about &lt;a href="http://perfdynamics.blogspot.com/2009/06/pdq-50-test-suite-or-how-i-spent-my.html"&gt;here&lt;/a&gt;. I guess that is my 15 mS of internet fame!&lt;br /&gt;&lt;br /&gt;I got side tracked with some work related stuff and went back to the task of applying PDQ-R to some of the case studies found in &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974818&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The case study that I have started working on is in Chapter 8: Case Study IV: An E-Business Service.&lt;br /&gt;&lt;br /&gt;The author provides us with a Customer Behavior Model Graph of a website:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/SnHZ5j6ULiI/AAAAAAAAAPE/HvcyFTEig3Q/s1600-h/CBMG.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 194px;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/SnHZ5j6ULiI/AAAAAAAAAPE/HvcyFTEig3Q/s400/CBMG.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5364308214090575394" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;From this graph we can create a set of linear equations that describes the number of visits to each page of the system presented in the CBMG (seen above). Taking a closer look at the graph we can see that there are probabilities of transitioning between one page to another. For example, the probability of moving from the Home Page (h) to the Login (g) is Phg. The probability of moving from the Login (g) page to the Search (s) page is Psg. &lt;br /&gt;&lt;br /&gt;Using these probabilities we can create a set of linear equations to describe the number of visits to each page in the system based upon the number of visits into the entry of the system. In this case, there is only one way into the system and that is via the Entry (e) page.&lt;br /&gt;&lt;br /&gt;We can look at the graph and manually derive the linear equations. For example, we can easily see that Vh = Peh*Ve. We can also see that Vg = Phg*Vh + Psg*Vs + Pvg*Vv. Doing this we eventually come up with eight linear equations that we can then solve. (The equations are listed on page 208 of &lt;a href="http://www.amazon.com/Performance-Design-Computer-Capacity-Planning/dp/0130906735/ref=sr_1_1?ie=UTF8&amp;s=books&amp;qid=1248974818&amp;sr=1-1"&gt;Performance by Design: Computer Capacity Planning By Example&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;The authors of the book provide us with all the probabilities in the form of a Matrix of Transition Probabilities on page 209. Here is that matrix:&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;                        (e)     (h)   (s)   (v)   (g)   (c)   (b)   (x)&lt;br /&gt;Entry (e)               0       1     0     0     0     0     0     0&lt;br /&gt;Home (h)                0       0     0.7   0     0.1   0     0     0.2&lt;br /&gt;Search (s)              0       0     0.4   0.2   0.15  0     0     0.25&lt;br /&gt;View Bids (v)           0       0     0     0     0.65  0     0     0.35&lt;br /&gt;Login (g)               0       0     0     0     0     0.3   0.6   0.1&lt;br /&gt;Create Auction (c)      0       0     0     0     0     0     0     1&lt;br /&gt;Place Bid (b)           0       0     0     0     0     0     0     1&lt;br /&gt;Exit (x)                0       0     0     0     0     0     0     0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So we can take those values found in the transition matrix and eventually gonkulate all the visits to each page. Although the transition matrix is provided by the author it is easy enough to re-create this matrix from web logs for a site.&lt;br /&gt;&lt;br /&gt;A number of years ago I did the same thing for a major e-Commerce site. At the time my goal was to use the transition matrix to create a universal virtual user that would emulate what real customers did in production. At the time we were using a number of different virtual users and it was a real PITA to increase the number of vusers in a scenario. I figured if I could generate the transition matrix and generate random walks via the transition matrix that my vusers would better represent the traffic of real customers. Unfortunately, I never got to finish the implementation of the UberVuser but I did get to the point of generating a transition matrix. And that sucker was huge! It wasn't a nice 8x8 matrix but rather a 538x538 matrix. Yeah... That's a lot of elements!&lt;br /&gt;&lt;br /&gt;So, thinking of my previous experience and looking at the author's provided transition matrix got me to thinking. There's bound to be a way of backing the transition matrix into the set of linear equations that can easily be solved in R (or any other method that can solve linear equations).&lt;br /&gt;&lt;br /&gt;I worked it out by hand (more about this below) and everything looked good but to really make this useful I needed to automate the routine. I sat down looked at the problem and thought that I was going to have to do a bunch of element manipulation when the solution hit me. It turned out to be so damned simple! I love it when a complicated problem can be easily solved. It just amuses the hell out of me.&lt;br /&gt;&lt;br /&gt;This is how I backed out the equations by hand. I know that the transition matrix has from "from" pages down the rows and the "to" along the columns. So for example, if I wanted to figure out the equation for the Vs I would work down the column and use the coefficients of the transition matrix as multipliers for the V variables.&lt;br /&gt;&lt;br /&gt;For example, with the 3rd column with solving for Vs:&lt;br /&gt;&lt;br /&gt;The third column of the transition matrix is:&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;    (s)&lt;br /&gt;(e) 0&lt;br /&gt;(h) 0.7&lt;br /&gt;(s) 0.4&lt;br /&gt;(v) 0&lt;br /&gt;(g) 0&lt;br /&gt;(c) 0&lt;br /&gt;(b) 0&lt;br /&gt;(x) 0&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;So to solve for Vs:&lt;br /&gt;&lt;br /&gt;Vs = 0*Ve + 0.7*Vh + 0.4Vs + 0*Vv + 0*Vg + 0*Vc + 0*Vb + 0*Vx&lt;br /&gt;&lt;br /&gt;Notice that we have a Vs on both sides of the equal sign. This corresponds to the loop on the Search node.&lt;br /&gt;&lt;br /&gt;We can drop out the variables that are multiplied by zero.&lt;br /&gt;&lt;br /&gt;Vs = 0.7*Vh + 0.4Vs&lt;br /&gt;&lt;br /&gt;Repeat the steps for all the columns of the transition matrix and you end up with the equations to solve for. But, most packages want the equations setup in matrix form. So, let's get all the variables on the left side of the equal sign.&lt;br /&gt;&lt;br /&gt;Vs - 0.4Vs - 0.7*Vh&lt;br /&gt;&lt;br /&gt;Simplifies into:&lt;br /&gt;&lt;br /&gt;0.6Vs - 0.7*Vh&lt;br /&gt;&lt;br /&gt;If we do this with all the equations we can finally put it into matrix form. But we can't solve it yet. We need another vector to solve with. This vector will contain the input into the system. In this case the entry into the system is into the Entry page so the vertical vector is:&lt;br /&gt;&lt;br /&gt;(e) 1&lt;br /&gt;(h) 0&lt;br /&gt;(s) 0&lt;br /&gt;(v) 0&lt;br /&gt;(g) 0&lt;br /&gt;(c) 0&lt;br /&gt;(b) 0&lt;br /&gt;(x) 0&lt;br /&gt;&lt;br /&gt;If we call our first matrix that we created from the transition matrix A and our input vertical vector B, we have the classic A*B = 0 that we can solve for.&lt;br /&gt;&lt;br /&gt;Now we get into the R solution for the above. As I was working out the algorithm to setup the problem for calling the R solve() routine it hit me how simple the solution was!&lt;br /&gt;&lt;br /&gt;Let's call our transition matrix supplied by the authors as M.&lt;br /&gt;&lt;br /&gt;We can take transpose of M and let's call it A.&lt;br /&gt;&lt;br /&gt;Take A and multiply by the scalar value of -1. This is the act of moving all the variables to the left side of the equals sign.&lt;br /&gt;&lt;br /&gt;Now we combine variables we are solving for in each row of A by adding 1 to the trace of matrix A.&lt;br /&gt;&lt;br /&gt;And there we have our final form of A. We can now solve for A*B = 0!&lt;br /&gt;&lt;br /&gt;Here is my solution in R&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;pre class="csharpcode"&gt;&lt;br /&gt;# Define the column information &lt;span class="kwrd"&gt;for&lt;/span&gt; easy access by name&lt;br /&gt;&lt;br /&gt;e &amp;lt;- 1;&lt;br /&gt;h &amp;lt;- 2;&lt;br /&gt;s &amp;lt;- 3;&lt;br /&gt;v &amp;lt;- 4;&lt;br /&gt;g &amp;lt;- 5;&lt;br /&gt;c &amp;lt;- 6;&lt;br /&gt;b &amp;lt;- 7;&lt;br /&gt;x &amp;lt;- 8;&lt;br /&gt;&lt;br /&gt;# M will be the supplied transition matrix&lt;br /&gt;&lt;br /&gt;M &amp;lt;- matrix(c(0,1,0,0,0,0,0,0,0,0,0.7,0,0.1,0,0,0.2,0,0,0.4,0.2,0.15,0,0,0.25,0,0,0,0,0.65,0,0,0.35,0,0,0,0,0,0.3,0.6,0.1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0), ncol=8, nrow=8, byrow=TRUE);&lt;br /&gt;&lt;br /&gt;# B &lt;span class="kwrd"&gt;is&lt;/span&gt; the vector that describes entry into the system&lt;br /&gt;B &amp;lt;- matrix(c(1,0,0,0,0,0,0,0),nrow=8,ncol=1);&lt;br /&gt;&lt;br /&gt;VisitsByTransitionMatrix &amp;lt;- function(M, B) {&lt;br /&gt;   A &amp;lt;- t(M);&lt;br /&gt;   A &amp;lt;- -1 * A;&lt;br /&gt;   &lt;span class="kwrd"&gt;for&lt;/span&gt; (i &lt;span class="kwrd"&gt;in&lt;/span&gt; 1:sqrt(length(A))) {&lt;br /&gt;     j &amp;lt;- i;&lt;br /&gt;     A[i,j] &amp;lt;- A[i,j] + 1;&lt;br /&gt;   };&lt;br /&gt;   &lt;span class="kwrd"&gt;return&lt;/span&gt;(solve(A,B));&lt;br /&gt;};&lt;br /&gt;&lt;br /&gt;Visits &amp;lt;- VisitsByTransitionMatrix(M, B);&lt;br /&gt;&lt;br /&gt;&amp;gt; Visits&lt;br /&gt;          [,1]&lt;br /&gt;[1,] 1.0000000&lt;br /&gt;[2,] 1.0000000&lt;br /&gt;[3,] 1.1666667&lt;br /&gt;[4,] 0.2333333&lt;br /&gt;[5,] 0.4266667&lt;br /&gt;[6,] 0.1280000&lt;br /&gt;[7,] 0.2560000&lt;br /&gt;[8,] 1.0000000&lt;br /&gt;&lt;br /&gt;&amp;gt; Visits[e]&lt;br /&gt;[1] 1&lt;br /&gt;&amp;gt; Visits[h]&lt;br /&gt;[1] 1&lt;br /&gt;&amp;gt; Visits[s]&lt;br /&gt;[1] 1.166667&lt;br /&gt;&amp;gt; Visits[v]&lt;br /&gt;[1] 0.2333333&lt;br /&gt;&amp;gt; Visits[g]&lt;br /&gt;[1] 0.4266667&lt;br /&gt;&amp;gt; Visits[c]&lt;br /&gt;[1] 0.128&lt;br /&gt;&amp;gt; Visits[b]&lt;br /&gt;[1] 0.256&lt;br /&gt;&amp;gt; Visits[x]&lt;br /&gt;[1] 1&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And there we go! Using this I could have taken that monster 538x538 matrix and easily figured out the number of hits for each page based upon the derived transition matrix and used the R routine for playing a lot of what-if scenarios. For example, what if we want to change some of the probabilities between nodes, how will that effect the visit to other pages? Now it is simple to crunch the numbers. Huzzah!&lt;br /&gt;&lt;br /&gt;My next task is to apply PDQ-R to solving the rest of the case study. Later in the chapter there are service times associated with each page and that will determine the total service demand per page and ultimately the page response time for each page and the effect on internal components that will be modeled in my PDQ-R model.&lt;br /&gt;&lt;br /&gt;I can really see where I could have used this modeling at my previous job at a major e-commerce site. It would have come in really stinkin' handy to be sure.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-4271716343440029836?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/4271716343440029836/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=4271716343440029836' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/4271716343440029836'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/4271716343440029836'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/07/hey-lets-take-transistion-matrix-and.html' title='Hey! Let&apos;s take a transistion matrix and convert it into a series of linear equations to gonkulate the number of visits to various pages!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qsa4nRIPmPo/SnHZ5j6ULiI/AAAAAAAAAPE/HvcyFTEig3Q/s72-c/CBMG.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-7755765795171830563</id><published>2009-06-01T15:14:00.005-05:00</published><updated>2009-06-08T21:17:32.762-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Universal Scaling Law'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>Automating the finding of coefficients for the USL</title><content type='html'>I got to playing around with R more and I've always found that to learn a language I need to solve problems with the language. I'm sure most everybody else does the same thing. My goal was to write a R function that imported a CSV with performance information to gonkulate against.&lt;br /&gt;&lt;br /&gt;In my case, I'm using the basic performance information from "Guerrilla Capacity Planning." I've created a CSV file with the number of procs and the resultant ray trace benchmark from Table 5.1:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;C:\Users\auswipe\Desktop&amp;gt;cat raw_throughput.csv&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;p,x&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;1,20&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;4,78&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;8,130&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;12,170&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;16,190&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;20,200&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;24,210&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;28,230&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;32,260&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;48,280&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;64,310&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Then I wrote an R function to crunch the numbers:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;uslCoefficients &amp;lt;- function(dataFile) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  uslData &amp;lt;- read.csv(dataFile, header=TRUE);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  uslData$c &amp;lt;- uslData$x / uslData$x[1];&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  usl &amp;lt;- nls(c ~ p/(1+sigma*(p-1)+kappa*p*(p-1)),&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;             uslData,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;             algorithm=&lt;span class="str"&gt;"port"&lt;/span&gt;,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;             start=c(sigma=0.0, kappa=0.0),&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;             lower=c(0,0));&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;  sigma &amp;lt;- coef(usl)[&lt;span class="str"&gt;"sigma"&lt;/span&gt;];&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;  kappa &amp;lt;- coef(usl)[&lt;span class="str"&gt;"kappa"&lt;/span&gt;];&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;  &lt;span class="kwrd"&gt;return&lt;/span&gt;(list(sigma=sigma, kappa=kappa));&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;};&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The function uslCoefficient returns a list where I can reference the "sigma" and "kappa" by named index:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&amp;gt; uslCoef &amp;lt;- uslCoefficients(&lt;span class="str"&gt;"c:\\Users\\auswipe\\Desktop\\raw_throughput.csv"&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&amp;gt; uslCoef[&lt;span class="str"&gt;"sigma"&lt;/span&gt;]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;$sigma&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    sigma &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;0.0497973 &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&amp;gt; uslCoef[&lt;span class="str"&gt;"kappa"&lt;/span&gt;]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;$kappa&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;       kappa &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;1.143404e-05 &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;&amp;gt; uslCoef&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;$sigma&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    sigma &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;0.0497973 &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;$kappa&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;       kappa &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;1.143404e-05 &lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;R is pretty nifty. I doubt I'll ever make use of all the power that is available but it'll be better than writing my own stat routines.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-7755765795171830563?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/7755765795171830563/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=7755765795171830563' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7755765795171830563'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7755765795171830563'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/06/automating-finding-of-coefficients-for.html' title='Automating the finding of coefficients for the USL'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5893958631830633745</id><published>2009-06-01T08:51:00.007-05:00</published><updated>2009-06-08T21:17:59.890-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Universal Scaling Law'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><category scheme='http://www.blogger.com/atom/ns#' term='Guerrilla Capacity Planning'/><category scheme='http://www.blogger.com/atom/ns#' term='R'/><title type='text'>Using R to calculate coefficients of the Universal Scaling Law with Non-Linear Regression</title><content type='html'>Ooh! Doesn't that sound fancy?&lt;br /&gt;&lt;br /&gt;Several weeks ago I purchased the eBook from O'Reilly called "The Art of Capacity Planning." I've always thought that load testing and capacity planning went hand-in-hand. One is not a replacement for the other but one can assist with the other. Load test helps out capacity planning by applying load to psuedo-production systems and capacity planning helps load testing by verifying results in load test against real world systems.&lt;br /&gt;&lt;br /&gt;I finished "The Art of Capacity Planning" and wanted to read more on the subject and picked up a copy of "Guerrilla Capacity Planning" which has a lot more math than "The Art of Capacity Planning." One of the concepts is the Universal Scaling Law based on Amdhal's Law. Dr. Neil J Gunther is a smart cookie. He even has a Ph.D in Theoretical Physics which makes him closer to Gordon Freeman than I'll ever be! (Side question: Do Ph.D's in Theoretical Physics get crowbars at graduation?)&lt;br /&gt;&lt;br /&gt;Anyhoo, in section 5.6.1 one of the methods in the book is to use Excel to do second degree polynomial regression for the calculation of two necessary coefficients, sigma and kappa. But when I tried to use Excel I was getting a negative value for sigma and one of the rules of the Universal Scaling Law is that the coefficients can never, ever, ever be negative. I just figured that I fat fingered something and tried it again and once again, got mismatching results.&lt;br /&gt;&lt;br /&gt;I scratched my noggin, tried to figure out where I err'd and did some Googling and came across this entry of Dr. Gunther's blog:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://perfdynamics.blogspot.com/2009/05/negative-usl-coefficients-in-excel.html"&gt;Negative Scalability Coefficients in Excel&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Because in Excel (and some other packages, like my TI-89) you can't put a constraint on the lower limits of the coefficient, you might from time to time get negative coefficients. But from reading the blog entry I see that other people are using R with success.&lt;br /&gt;&lt;br /&gt;This is the first time that I've ever messed around with R for statistical purposes. In the past I've written some stat routines (years ago!) in C# for comparing before/after load testing results.&lt;br /&gt;&lt;br /&gt;Here is how I used R from start to finish to gonkulate the coefficients.&lt;br /&gt;&lt;br /&gt;Using the data from Section 5.3 I did the following in R:&lt;br /&gt;&lt;br /&gt;First I defined my p array, which in the book is the number of processors used for ray tracing:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt;    font-size: small;&lt;br /&gt;    color: black;&lt;br /&gt;    font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt;    background-color: #ffffff;&lt;br /&gt;    /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt&lt;br /&gt;{&lt;br /&gt;    background-color: #f4f4f4;&lt;br /&gt;    width: 100%;&lt;br /&gt;    margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;p &amp;lt;- c(1, 4, 8, 12, 16, 20, 24, 28, 32, 48, 64)&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Then I defined my c array, which is the relative capacity for the number of processors used for ray tracing:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;c &amp;lt;- c(1.0, 3.9, 6.5, 8.5, 9.5, 10.0, 10.5, 11.5, 13.0, 14.0, 15.5)&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I combined both arrays into a data frame for later use.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;df &amp;lt;- data.frame(p, c)&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And when I check out the contents of df I get:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;df&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;    p    c&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;1   1  1.0&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;2   4  3.9&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;3   8  6.5&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;4  12  8.5&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;5  16  9.5&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;6  20 10.0&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;7  24 10.5&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;8  28 11.5&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;9  32 13.0&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;10 48 14.0&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;11 64 15.5&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I can now use a non-linear regression routine with my data frame that I entered above.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;usl &amp;lt;- nls(c ~ p/(1+sigma*(p-1)+kappa*p*(p-1)), df, algorithm=&lt;span class="str"&gt;"port"&lt;/span&gt;, start=c(sigma=0.0, kappa=0.0), lower=c(0,0))&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I can then access the coefficients by named index:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;sigma &amp;lt;- coef(usl)["sigma"]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;kappa &amp;lt;- coef(usl)["kappa"]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;sigma&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    sigma &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;0.0497973 &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;kappa&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;       kappa &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;1.143404e-05 &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Huzzah!&lt;br /&gt;&lt;br /&gt;I can now interpolate the relative capacity based upon the USL and the coefficients that were previously gonkulated and add that to my current data frame, df, that I defined earlier. I do have to note that I was a slackard and did not apply the significant digits rules as outlined in Chapter 3 of "Guerrilla Capacity Planning."&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;df$proj_c &amp;lt;- p/(1 + sigma * (p - 1) + kappa * p * (p - 1))&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There are the projected relative capacities. Yay!&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;df&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;    p    c    proj_c&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;1   1  1.0  1.000000&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;2   4  3.9  3.479686&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;3   8  6.5  5.929346&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;4  12  8.5  7.745536&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;5  16  9.5  9.144406&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;6  20 10.0 10.253815&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;7  24 10.5 11.154233&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;8  28 11.5 11.898837&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;9  32 13.0 12.524174&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;10 48 14.0 14.259114&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;11 64 15.5 15.298811&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And here I will make a simple little graph of the actual versus projected relative capacity:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;style type="text/css"&gt;&lt;br /&gt;.csharpcode, .csharpcode pre&lt;br /&gt;{&lt;br /&gt; font-size: small;&lt;br /&gt; color: black;&lt;br /&gt; font-family: Consolas, "Courier New", Courier, Monospace;&lt;br /&gt; background-color: #ffffff;&lt;br /&gt; /*white-space: pre;*/&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode pre { margin: 0em; }&lt;br /&gt;&lt;br /&gt;.csharpcode .rem { color: #008000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .kwrd { color: #0000ff; }&lt;br /&gt;&lt;br /&gt;.csharpcode .str { color: #006080; }&lt;br /&gt;&lt;br /&gt;.csharpcode .op { color: #0000c0; }&lt;br /&gt;&lt;br /&gt;.csharpcode .preproc { color: #cc6633; }&lt;br /&gt;&lt;br /&gt;.csharpcode .asp { background-color: #ffff00; }&lt;br /&gt;&lt;br /&gt;.csharpcode .html { color: #800000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .attr { color: #ff0000; }&lt;br /&gt;&lt;br /&gt;.csharpcode .alt &lt;br /&gt;{&lt;br /&gt; background-color: #f4f4f4;&lt;br /&gt; width: 100%;&lt;br /&gt; margin: 0em;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;.csharpcode .lnum { color: #606060; }&lt;br /&gt;&lt;/style&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;plot(p, c)&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;lines(p, proj_c)&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And here is the graph that is generated:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/SiPuUeu7O9I/AAAAAAAAAO8/dCJkuIPo3JQ/s1600-h/interpolation_graph.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer; width: 320px; height: 320px;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/SiPuUeu7O9I/AAAAAAAAAO8/dCJkuIPo3JQ/s320/interpolation_graph.jpg" alt="" id="BLOGGER_PHOTO_ID_5342375618606218194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Kinda nifty, eh?&lt;br /&gt;&lt;br /&gt;I can see myself using R more in the future. I'd rather write routines for automagic analysis of data with R than write my own routines from the ground up.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5893958631830633745?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5893958631830633745/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5893958631830633745' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5893958631830633745'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5893958631830633745'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/06/using-r-to-calculate-coefficients-of.html' title='Using R to calculate coefficients of the Universal Scaling Law with Non-Linear Regression'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qsa4nRIPmPo/SiPuUeu7O9I/AAAAAAAAAO8/dCJkuIPo3JQ/s72-c/interpolation_graph.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5928459081981136453</id><published>2009-05-02T14:28:00.002-05:00</published><updated>2009-05-02T14:36:29.352-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Push-To-Test'/><category scheme='http://www.blogger.com/atom/ns#' term='Selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='soapUI'/><title type='text'>Push-To-Test</title><content type='html'>I went to a four hour presentation on Push-To-Test yesterday. It seems pretty nifty, the idea of wrapping an automated testing framework around a bunch of open source projects such as Selenium, soapUI and other goodies.&lt;br /&gt;&lt;br /&gt;The presentation was a little hectic but it got the general idea across. I would have preferred to get to the meat of the subject quicker but the presenter did have 20 people to deal with and we had to go with the common denominator. No biggie.&lt;br /&gt;&lt;br /&gt;The only thing that I wasn't too big was the idea of converting the Selenium tests for web tests from XUL into Java or Jython for more programmable control. After the conversion there is no going back to the original SeleniumIDE tool from what I saw. But, I guess that isn't much different from what I've done with LoadRunner and VSTS a bajillion times before when I had a bunch of custom code from the tree view into code. They did have an IDE so it's not too bad in hind site. I didn't get a chance to play around with the IDE to see how it compares to Eclipse or Visual Studio.&lt;br /&gt;&lt;br /&gt;But, it is free for download to use the unsupported version, so I say thumbs up.&lt;br /&gt;&lt;br /&gt;The idea of using Selenium for the web tests is pretty nifty as you get good control with AJAX controls that are a bit of a pain to control in HTTP Virtual user. I hadn't thought of doing that in the past.&lt;br /&gt;&lt;br /&gt;I was told that transactions and nested transactions were supported but didn't get a chance to see them in action.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5928459081981136453?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5928459081981136453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5928459081981136453' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5928459081981136453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5928459081981136453'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/05/push-to-test.html' title='Push-To-Test'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6427946017604200481</id><published>2009-03-26T10:20:00.003-05:00</published><updated>2009-03-26T10:25:33.292-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='QTP'/><title type='text'>Learning QTP</title><content type='html'>It's been a long time between blog posts. Since this blog is mainly for myself, that isn't a problem. :-)&lt;br /&gt;&lt;br /&gt;I'm starting to learn QTP and finding that it looks like a real handy tool for functional and integration testing. I'm still trying to get myself to not look at it as an alternate load testing tool and trying to use the interface and not look at each problem as more code to be written. I had the same problem initially with JMeter as well having come from a background where I would normally add a bunch of custom code after the initial script creation.&lt;br /&gt;&lt;br /&gt;I'm not a big fan of VBScript but for the purpose at hand I have to say it is probably a better choice than using C like LoadRunner. Easier to create a COM object to be invoked with CreateObject for slicing and dicing of data.&lt;br /&gt;&lt;br /&gt;I'm not sure if raw HTML can be pulled in with QTP, yet. I wouldn't be surprised if it can. QTP seems to be a pretty nifty tool. No doubt it will help with future job searches.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6427946017604200481?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6427946017604200481/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6427946017604200481' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6427946017604200481'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6427946017604200481'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/03/learning-qtp.html' title='Learning QTP'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8837117013693072491</id><published>2009-01-08T20:12:00.003-06:00</published><updated>2009-01-08T20:21:33.443-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Employment'/><title type='text'>Got a job!</title><content type='html'>I accepted an informal job offer today at the same pay as my previous job (which wasn't too shabby). I will be a performance engineer on a rather large project involving Red Hat RHEL 4.5, Orace and Java/WebLogic. Here is a really good opportunity to bone up on Java performance analysis along with WebLogic and Java. I'm not much of a Java dude so I will finally be getting up to speed.&lt;br /&gt;&lt;br /&gt;I'm not to worried about the RHEL as I've been using various flavors of Unices (primarily FreeBSD) over the years. I won't get into the whole debate of genetic UNIX versus copied UNIX. Just not worth wasting my breath.&lt;br /&gt;&lt;br /&gt;Another opportunity to learn Oracle performance tuning as well. I've done a lot with SQL Server over the years and I hope that some of it applies to the Oracle side of the house.&lt;br /&gt;&lt;br /&gt;It looks like I'll be back to using Load Runner again. Looks like either Winsock/SOA or HTTP vusers. I've heard some bad things about the SOA Vusers with respect to generated script complexity and ease of correlation of data. All I know at this point is that the client is a swing UI running a Java application that will connect to the middle tier. Hopefully it is all done via SOA/WebServies. I have an interesting idea of utilizing WireShark and chaosreader.pl and some custom perl scripts for generationg clean vusers for the probject. We'll just have to wait and see how it goes.&lt;br /&gt;&lt;br /&gt;Another interesting thing will be working out of the house full time. I have two previous coworkers that do this and they love it but I am a very social creature. We'll see how it goes. I will get a laptop and an aircard from my new employer so that means I will be able to work from any location so I should be able to get out of the house often and see friends for lunch, which has always been important to me.&lt;br /&gt;&lt;br /&gt;So, my adventure continues, just not on the original vector. :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8837117013693072491?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8837117013693072491/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8837117013693072491' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8837117013693072491'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8837117013693072491'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2009/01/got-job.html' title='Got a job!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-1456849945414212591</id><published>2008-12-11T16:14:00.005-06:00</published><updated>2008-12-11T16:43:14.710-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMeter'/><category scheme='http://www.blogger.com/atom/ns#' term='Unemployment'/><title type='text'>Taking some time to keep on learning.</title><content type='html'>Since I am unemployed and not having anything better to do with my time I decided I would check out "Apache JMeter" by Emily H. Halili (Packt, 2008).&lt;br /&gt;&lt;br /&gt;It's a light read and I found that Chapter 7 (Advanced Features) was the most helpful to myself as I am not a n00b to JMeter. However, I felt that Chapter 7 would have been better served if BeanShell processing would have been tackled. I've found from my load testing experience in the past that at some point the return HTML is gonna have to be sliced and diced and data extracted that cannot be done with a simple RegEx extraction (like what is covered in Chapter 7 of the book).&lt;br /&gt;&lt;br /&gt;Other than that, I think that if you are a total n00b to JMeter it isn't half bad as a simple introduction to using JMeter for performance/load testing. There's a lot more to performance/load testing than the book covers such as metrics collection, number crunching, et cetera but the book doesn't purport itself to be the end all be all of explaining performance/load testing so I can't complain.&lt;br /&gt;&lt;br /&gt;Three stars.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-1456849945414212591?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/1456849945414212591/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=1456849945414212591' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1456849945414212591'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1456849945414212591'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/12/taking-some-time-to-keep-on-learning.html' title='Taking some time to keep on learning.'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5665402116255875138</id><published>2008-12-03T15:23:00.003-06:00</published><updated>2008-12-11T16:14:20.127-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Unemployment'/><title type='text'>And that job is toast!</title><content type='html'>Well. Got laid off today with 20 other folks from the Dallas area. Cannot say that I am surprised. Good thing I've been saving up for this possibility. Didn't get to keep the Uberlaptop of Powah (and I wasn't gonna pay the $3700 to keep it).&lt;br /&gt;&lt;br /&gt;I don't expect to get another job in December but I have some possibilities lined up in January. What am I gonna do with that time? Hmmmm. Wasn't GTA IV just released? Too bad I don't have a machine capable of playing the PC version and I'm not going to pay for a console while laid off just to play GTA IV.&lt;br /&gt;&lt;br /&gt;I have no doubt that my load testing adventure will continue in January.&lt;br /&gt;&lt;br /&gt;One thing for sure. The past seven months has been a waste. Thanks a lot, VT. Same back atcha.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5665402116255875138?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5665402116255875138/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5665402116255875138' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5665402116255875138'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5665402116255875138'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/12/and-that-job-is-toast.html' title='And that job is toast!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-335413013521475215</id><published>2008-11-13T17:22:00.003-06:00</published><updated>2008-11-13T17:25:45.118-06:00</updated><title type='text'>Hey. What's going on in this blog?</title><content type='html'>Hey. What's going on in this blog?&lt;br /&gt;&lt;br /&gt;Not a damn thing.&lt;br /&gt;&lt;br /&gt;*sigh*&lt;br /&gt;&lt;br /&gt;Not even working on WiX stuff right now. Working on manually deploying with NAnt, which is fine with me as it is proven that it works time after time. I suspect that MercScum is gonna wanna go back to WiX after initial deployment prototyping. I mean, why stick with something that is proven and works, right?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-335413013521475215?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/335413013521475215/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=335413013521475215' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/335413013521475215'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/335413013521475215'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/11/hey-whats-going-on-in-this-blog.html' title='Hey. What&apos;s going on in this blog?'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-2671233106375549436</id><published>2008-10-28T16:52:00.002-05:00</published><updated>2008-10-28T16:55:56.389-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WiX'/><title type='text'>Almost there... Stay on target...</title><content type='html'>Getting closer to getting the first general WiX task done.&lt;br /&gt;&lt;br /&gt;I now have WiX creating my user, adding that user to the Administrators group (No comments from the peanut gallery, please!) and then creating a custom AppPool and creating a Web Application that is bound to that custom AppPool with a created VDir. Pretty nifty.&lt;br /&gt;&lt;br /&gt;Here is what it looks like so far:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Component&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="MySoftwareRequirements"&lt;/span&gt; &lt;span class="attr"&gt;Guid&lt;/span&gt;&lt;span class="kwrd"&gt;="{SOME-VALID-GUID}"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;CreateFolder&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;util:User&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="MySoftwareUser"&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;="MySoftwareUser"&lt;/span&gt; &lt;span class="attr"&gt;Password&lt;/span&gt;&lt;span class="kwrd"&gt;="supersekrit"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;util:GroupRef&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="Administrators"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;util:User&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;iis:WebSite&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;          = &lt;span class="kwrd"&gt;"DefaultWebSite"&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;               &lt;span class="attr"&gt;Description&lt;/span&gt; = &lt;span class="kwrd"&gt;"DefaultWebSite"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;               &lt;span class="attr"&gt;Directory&lt;/span&gt;   = &lt;span class="kwrd"&gt;"MySoftwareSubDir"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;iis:WebAddress&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt; = &lt;span class="kwrd"&gt;"AllUnassigned"&lt;/span&gt; &lt;span class="attr"&gt;Port&lt;/span&gt;&lt;span class="kwrd"&gt;="80"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;iis:WebDirProperties&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;                    = &lt;span class="kwrd"&gt;"WebVirtualDirProperties"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;                            &lt;span class="attr"&gt;Execute&lt;/span&gt;               = &lt;span class="kwrd"&gt;"yes"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;                            &lt;span class="attr"&gt;Script&lt;/span&gt;                = &lt;span class="kwrd"&gt;"yes"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;                            &lt;span class="attr"&gt;Read&lt;/span&gt;                  = &lt;span class="kwrd"&gt;"yes"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;                            &lt;span class="attr"&gt;WindowsAuthentication&lt;/span&gt; = &lt;span class="kwrd"&gt;"no"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;                            &lt;span class="attr"&gt;AnonymousAccess&lt;/span&gt;       = &lt;span class="kwrd"&gt;"yes"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;                            &lt;span class="attr"&gt;AnonymousUser&lt;/span&gt;         = &lt;span class="kwrd"&gt;"MySoftwareUser"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;                            &lt;span class="attr"&gt;IIsControlledPassword&lt;/span&gt; = &lt;span class="kwrd"&gt;"no"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;iis:WebSite&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;iis:WebAppPool&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="MySoftware"&lt;/span&gt; &lt;span class="attr"&gt;Identity&lt;/span&gt;&lt;span class="kwrd"&gt;="other"&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;="MySoftware"&lt;/span&gt; &lt;span class="attr"&gt;User&lt;/span&gt;&lt;span class="kwrd"&gt;="MySoftwareUser"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;iis:WebVirtualDir&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;            = &lt;span class="kwrd"&gt;"MySoftwareVDir"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;                     &lt;span class="attr"&gt;Alias&lt;/span&gt;         = &lt;span class="kwrd"&gt;"MySoftwareVDir"&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;                     &lt;span class="attr"&gt;Directory&lt;/span&gt;     = &lt;span class="kwrd"&gt;"MySoftwareSubDir"&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;                     &lt;span class="attr"&gt;WebSite&lt;/span&gt;       = &lt;span class="kwrd"&gt;"DefaultWebSite"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;                     &lt;span class="attr"&gt;DirProperties&lt;/span&gt; = &lt;span class="kwrd"&gt;"WebVirtualDirProperties"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;iis:WebApplication&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;         = &lt;span class="kwrd"&gt;"WebApplication"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;                        &lt;span class="attr"&gt;Name&lt;/span&gt;       = &lt;span class="kwrd"&gt;"MySoftware"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;                        &lt;span class="attr"&gt;WebAppPool&lt;/span&gt; = &lt;span class="kwrd"&gt;"MySoftware"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;iis:WebVirtualDir&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Of course, you can't see the reference to the Administrators group that is up at the top of the .wxs file under the Package element (see previous post). It is starting to come together.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-2671233106375549436?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/2671233106375549436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=2671233106375549436' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2671233106375549436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2671233106375549436'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/almost-there-stay-on-target.html' title='Almost there... Stay on target...'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-7526045041353392494</id><published>2008-10-28T12:55:00.002-05:00</published><updated>2008-10-28T13:04:42.322-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WiX'/><title type='text'>More WiX stuff: Creating a user and adding them to a group.</title><content type='html'>Now that I am able to successfully create a new user I need to add that user to the Administrators group. Yeah, I hear you already, "That's a major security issue!" and I would tend to agree but this has been "mandated" by MercScum so there isn't much I can do about it.&lt;br /&gt;&lt;br /&gt;Anyhoo. To do it I had do the following.&lt;br /&gt;&lt;br /&gt;After the Product and Package element declarations I had to add a Group element, like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Product&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="{F0C1692F-2D79-4D46-9BDF-DCBAD1DBCDDB}"&lt;/span&gt; &lt;span class="attr"&gt;Language&lt;/span&gt;&lt;span class="kwrd"&gt;="1033"&lt;/span&gt; &lt;span class="attr"&gt;Manufacturer&lt;/span&gt;&lt;span class="kwrd"&gt;="SomeManufacturer"&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;="SomeProduct"&lt;/span&gt; &lt;span class="attr"&gt;UpgradeCode&lt;/span&gt;&lt;span class="kwrd"&gt;="{SOME-VALID-GUID}"&lt;/span&gt; &lt;span class="attr"&gt;Version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0.0.0"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Package&lt;/span&gt; &lt;span class="attr"&gt;Compressed&lt;/span&gt;&lt;span class="kwrd"&gt;="yes"&lt;/span&gt; &lt;span class="attr"&gt;InstallerVersion&lt;/span&gt;&lt;span class="kwrd"&gt;="200"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;util:Group&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="Administrators"&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;="Administrators"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;It took some trial and error to find out where to put the util:Group element. The documentation says that it is a child of the Product element but I couldn't put it directly after. It had to go under the Package element. Good to know!&lt;br /&gt;&lt;br /&gt;In my previously declared util:User element I had to nest a GroupRef element so the entire bit of XML reads like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Component&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="RequiredUser"&lt;/span&gt; &lt;span class="attr"&gt;Guid&lt;/span&gt;&lt;span class="kwrd"&gt;="{SOME-VALID-GUID}"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;util:User&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="NewUser"&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;="NewUserName"&lt;/span&gt; &lt;span class="attr"&gt;Password&lt;/span&gt;&lt;span class="kwrd"&gt;="supersekrit"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;util:GroupRef&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="Administrators"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;util:User&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;CreateFolder&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;I was able to create the .msi and installed it on a VM and it worked like a charm. Luckily I only banged my head against the WiX brick wall for a few hours this time, unlike the create a new user head-banging. I suspect everything else will start to fall into place now that I am getting more comfortable with WiX and XML in general. Yes, I've managed to be in industry for almost 20 years without having to bow down to the great XML monster but now I am paying the price for being behind the curve. Back in my day we had CSVs and we liked 'em!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-7526045041353392494?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/7526045041353392494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=7526045041353392494' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7526045041353392494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7526045041353392494'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/more-wix-stuff-creating-user-and-adding.html' title='More WiX stuff: Creating a user and adding them to a group.'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8048773473971142890</id><published>2008-10-27T17:26:00.003-05:00</published><updated>2008-10-27T17:35:05.892-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WiX'/><title type='text'>Can I has user created by WiX?</title><content type='html'>Ok. I'm starting to get this stuff figured out.&lt;br /&gt;&lt;br /&gt;The User tag is part of WixUtilExtension.dll but it is not enough to simply reference WixUtilExtension on the command line.&lt;br /&gt;&lt;br /&gt;You must also add an XML name space reference in the .wxs file. Also, another gotcha. The namespace spelling is different from the WixUtilExtension spelling. D'oh!&lt;br /&gt;&lt;br /&gt;I had to add the following:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Wix&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://schemas.microsoft.com/wix/2006/wi"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;     &lt;span class="attr"&gt;xmlns:util&lt;/span&gt;&lt;span class="kwrd"&gt;="http://schemas.microsoft.com/wix/UtilExtension"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now, the User tag that I created has to have the "util" prefix to let the linker know that the User tag is being referenced for the WixUtilExtension:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;Component&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="RequiredUser"&lt;/span&gt; &lt;span class="attr"&gt;Guid&lt;/span&gt;&lt;span class="kwrd"&gt;="{SUPER-DUPER-GUID}"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;util:User&lt;/span&gt; &lt;span class="attr"&gt;Id&lt;/span&gt;&lt;span class="kwrd"&gt;="UserStuff"&lt;/span&gt; &lt;span class="attr"&gt;Name&lt;/span&gt;&lt;span class="kwrd"&gt;="UserStuff"&lt;/span&gt; &lt;span class="attr"&gt;Password&lt;/span&gt;&lt;span class="kwrd"&gt;="supersekrit"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;CreateFolder&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;Component&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Notice that I had to add the CreateFolder tag in my User tag to prevent error ICE18. Otherwise, it'll fail and make me cry.&lt;br /&gt;&lt;br /&gt;I was able to link with candle with a "-ext WixUtilExtension.dll" reference and the wixobj was created and with the CreateFolder I avoided the dreaded ICE18 error. I was also able to create my .msi and I will test that tomorrow on my VM.&lt;br /&gt;&lt;br /&gt;Wasn't that all intuitively obvious?&lt;br /&gt;&lt;br /&gt;My thanks go out to the wix-users listserv that pointed out where I was banging my head against the brick wall of wix. Now with this information I should be able to go forward with the other tasks required and be a tad bit more efficient with WiX (Finally!).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8048773473971142890?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8048773473971142890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8048773473971142890' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8048773473971142890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8048773473971142890'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/can-i-has-user-created-by-wix.html' title='Can I has user created by WiX?'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-528403619765263054</id><published>2008-10-27T14:39:00.004-05:00</published><updated>2008-10-27T15:09:48.597-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WiX'/><title type='text'>My head hurts...</title><content type='html'>*bangs head against desk*&lt;br /&gt;&lt;br /&gt;Search for creating users with WiX and you see the handy dandy User tag.&lt;br /&gt;&lt;br /&gt;Go ahead and try that with WiX v3. Doesn't work?&lt;br /&gt;&lt;br /&gt;Google the documentation. Aha! I need the sca.wixlib and associated sca*.dll files?&lt;br /&gt;&lt;br /&gt;Search for files.&lt;br /&gt;&lt;br /&gt;Not shipped with v3?&lt;br /&gt;&lt;br /&gt;Hmmmm...&lt;br /&gt;&lt;br /&gt;Download WiX v2.&lt;br /&gt;&lt;br /&gt;Try using with WiX v3.&lt;br /&gt;&lt;br /&gt;"The extension 'sca.wixlib' could not be loaded."&lt;br /&gt;&lt;br /&gt;*continue to bang head against desk*&lt;br /&gt;&lt;br /&gt;Now off to try this again with WiX v2. Can't use heat to harvest files? Guess I need to re-learn the process with tallow.&lt;br /&gt;&lt;br /&gt;*continue to bang head against desk*&lt;br /&gt;&lt;br /&gt;Do some more Googling. Hey, automated tasks are in the Wix*Extension.dlls. That's what I thought but couldn't get to load with -ext. Hmmmmm?&lt;br /&gt;&lt;br /&gt;Some more Googling and reading of blogs. Oh, you can't just use the Wix*Extension.dll by name. Needs full path. Ok. That's not so bad.&lt;br /&gt;&lt;br /&gt;Which Wix*Extension.dll has the task for using with the User tag? Dunno. I'll just copy all Wix*Extension.dll to my subdir and try 'em all.&lt;br /&gt;&lt;br /&gt;Oops. WinDifxAppExtension.dll can't be loaded? Remove it from command line.&lt;br /&gt;&lt;br /&gt;Oops. WixIsolatedAppExtension.dll can't be loaded. Remove it from command line.&lt;br /&gt;&lt;br /&gt;Will by User tag be recognized now? *crosses fingers* Let's give it a try!&lt;br /&gt;&lt;br /&gt;8&lt;--------------------------------------------------&lt;br /&gt;C:\SomeProduct\SP\Dist&gt;candle -o SP.wixobj SP.wxs  -ext WixDifxAppExtension.dll -ext WixDirectXExtension.dll -ext WixFirewallExtension.dl&lt;br /&gt;l -ext WixGamingExtension.dll -ext WixIIsExtension.dll -ext WixMsmqExtension.dll -ext WixNetFxExtension.dll -ext WixPSExtension.dll&lt;br /&gt; -ext WixSqlExtension.dll -ext WixUIExtension.dll -ext WixUtilExtension.dll -ext WixVSExtension.dll&lt;br /&gt;Microsoft (R) Windows Installer Xml Compiler version 3.0.4318.0&lt;br /&gt;Copyright (C) Microsoft Corporation. All rights reserved.&lt;br /&gt;&lt;br /&gt;SP.wxs&lt;br /&gt;C:\SomeProduct\SP\Dist\SP.wxs(461) : error CNDL0005 : The Component element contains an unexpected child element 'User'.&lt;br /&gt;--------------------------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;Fsck.&lt;br /&gt;&lt;br /&gt;*sigh*&lt;br /&gt;&lt;br /&gt;Hey. Wasn't I hired to be the performance SME? Hmmmm. What happened to that?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-528403619765263054?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/528403619765263054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=528403619765263054' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/528403619765263054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/528403619765263054'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/my-head-hurts.html' title='My head hurts...'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-2046306451792423328</id><published>2008-10-24T15:57:00.005-05:00</published><updated>2008-10-24T21:51:30.468-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WiX'/><title type='text'>The pain of the WiX</title><content type='html'>Much like the use of candles in BDSM play, I am feeling the pain of the WiX wax motif windows installer. But like those candles, I think it'll pay off in the long run. &lt;br /&gt;&lt;br /&gt;Once again, into the depths of XML fun! At least it's not XML scripting languages at this point but I am definitely feeling the pain of heat, candle and not seeing the light at the moment. WiX has a very steep learning curve.&lt;br /&gt;&lt;br /&gt;So far I've learned how to harvest a bunch of files with heat, link with candle and assemble a .msi with light. With a simple and straightforward project it's actually pretty simple but unfortunately I am not on a simple project.&lt;br /&gt;&lt;br /&gt;The first thing that I've found that has burned me is that we have a bunch of duplicated DLLs nested in different subdirs that I want to package as a product and apparently that is a no-no and can't be done. I've had to separate the subdirs generated by MSBuild into two seperate trees with parallel structures and create two .msi packages that when executed create the desired tree.&lt;br /&gt;&lt;br /&gt;I banged my head up against that for long enough.&lt;br /&gt;&lt;br /&gt;Now I'm trying to figure out how to create a user and that is pretty painful. There is a most excellent older tutorial located &lt;a href="http://www.tramontana.co.hu/wix/lesson1.php"&gt;here&lt;/a&gt; but it doesn't document all the gotchas. I'm trying to find the extra DLL I have to reference via -ext on the command line for candle.exe that recognizes the user tag.&lt;br /&gt;&lt;br /&gt;Unlike the nant to MSBuild conversion (which I don't think it was necessary) I figure that learning this WiX stuff will pay off. I mean, if WiX was good enough for MS to deploy SQL Server 2005 and Office 2007 it should do everything I ever want. It's just the learning curve that has to be climbed.&lt;br /&gt;&lt;br /&gt;I can really see why deployment/build engineering can be a full time position on a team. I don't want to be a full time build/deployment engineer but I appreciate the knowledge and suspect that it'll pay off in the long run.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-2046306451792423328?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/2046306451792423328/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=2046306451792423328' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2046306451792423328'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2046306451792423328'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/pain-of-wix.html' title='The pain of the WiX'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-2556066955352471925</id><published>2008-10-21T23:04:00.001-05:00</published><updated>2008-10-21T23:08:22.264-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='WiX'/><category scheme='http://www.blogger.com/atom/ns#' term='Orthogonal'/><category scheme='http://www.blogger.com/atom/ns#' term='MSBuild'/><title type='text'>From MSBuild to WiX</title><content type='html'>Now that I've converted from NAnt to MSBuild scripts I am now learning the wonderful world of WiX to create .msi install packages. What does this have to do with my performance testing duties? Not a damn thing.&lt;br /&gt;&lt;br /&gt;The hired mercenary scum that is running the project now is having do all sorts of things that are not part of our normal activities. I was hired to be a performanc SME and to build up a testing framework which is about 80% complete. My last work on that was over a month ago.&lt;br /&gt;&lt;br /&gt;I suspect I shall not see another performance test on this project.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-2556066955352471925?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/2556066955352471925/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=2556066955352471925' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2556066955352471925'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2556066955352471925'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/from-msbuild-to-wix.html' title='From MSBuild to WiX'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-313796581822051858</id><published>2008-10-11T03:09:00.007-05:00</published><updated>2008-10-16T17:12:37.787-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MSBuild'/><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><category scheme='http://www.blogger.com/atom/ns#' term='perl'/><title type='text'>NAnt versus MSBuild</title><content type='html'>At work we are currently converting from NAnt to MSBuild scripts as we convert from CruiseControl.NET to TeamCity. Why are we changing from NAnt to MSBuild as we change CI servers? Nobody really knows for sure besides the guy that is running the project basically says, "I've mandated it." That's the only reason. Seems like a silly reason to be to go through all the trouble to rewrite all the scripts.&lt;br /&gt;&lt;br /&gt;After working with NAnt and MSBuild I have come to one conclusion: I don't like XML based scripting languages. That's right, you heard it here. They are both a pain. Way too verbose and too much of a PITA. My personal belief is that we should just wrap all the functionality with a perl script and be done with it. :-) Just think about it: Simple if/then/else statements and simple loops without all the XML filler junque and all the dynamic data structures you want without a bunch more XML filler stuff. It's beautiful! Back in my day, you darn whipper snappers, we had .CSV files and liked 'em! And like Ant/NAnt, perl works under both Unix and Win32 (I've been using Win32 for a gazillion years but now that I run a 64-bit OS should I start saying Win32/64? That's a discussion for another time! Now back to my MSBuild and NAnt rant!) so you have multiple platform functionality.&lt;br /&gt;&lt;br /&gt;MSBuild is taking me longer to wrap my head around. I don't like that I can create a property inside of a target, but that property is not available until the target where it was created as finished. Makes it a pain to attempt to modularize common calls for database work. And why doesn't MSBuild have a foreach construct? Sure, you can use the metabase accessing `%` to invoke multiple calls but how non-intuitive is that? Argh! I shouldn't have to write a helper function to make multiple calls with the % operator.&lt;br /&gt;&lt;br /&gt;Yeah. I stand by my convictions on this one. Junque both of 'em and write a perl wrapper and be happy.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-313796581822051858?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/313796581822051858/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=313796581822051858' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/313796581822051858'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/313796581822051858'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/10/nant-versus-msbuild.html' title='NAnt versus MSBuild'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6778543632392745862</id><published>2008-09-30T15:13:00.008-05:00</published><updated>2008-10-06T23:24:08.159-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Automatic Load Test'/><title type='text'>Asynchronous Invocation of  a Load Test from a Continuous Integration System</title><content type='html'>Why the fancy pants title? A Microsoft recruiter asked for my resume for some load testing related stuff and my blog is on my resume so I figured I needed some high falutin' titles. Hopefully they don't read anything but the titles. :-)&lt;br /&gt;&lt;br /&gt;Anyway...&lt;br /&gt;&lt;br /&gt;I'm getting to the point where I need to be able to invoke a load test after the nightly build and automagically run a load test. Now, I know, right now you're saying to yourself, "Hey, a nightly build doesn't constitute a continuous integration system!" And I agree, however, "Continuous Integration System" sounds much more impressive than just "nightly build." ;-)&lt;br /&gt;&lt;br /&gt;Back to the important stuff: How am I going to invoke a Load Test on another machine after the nightly build is done? Right now I am working everything out with that super-duper handy utility, psexec from the UberBrains at SysInternals (now part of Microsoft).&lt;br /&gt;&lt;br /&gt;I have recently build a stand alone server that I had installed SQL Server 2005, VS2008 TSTE and my custom LoadTestResults database where I store summary information from runs that I will be reporting from. On that server which I will refer to as my Load Test Controller, I setup a machine account user account by the name of TestRunner. During my trial and errors I found that I had to add TestRunner to the Administrators group but I need to go back and play around with that some to see if there is a more secure way to invoke the nightly automagic load test.&lt;br /&gt;&lt;br /&gt;On the Load Test Controller I have two batch files: trigger.bat and loadtest.bat.&lt;br /&gt;&lt;br /&gt;trigger.bat:&lt;br /&gt;&lt;br /&gt;8&lt;----------------------------&lt;br /&gt;@echo off&lt;br /&gt;echo Cry havoc and let slip loose the dogs of loadtest!&lt;br /&gt;start C:\LoadTest\Trigger\loadtest.bat&lt;br /&gt;----------------------------&gt;8&lt;br /&gt;&lt;br /&gt;The start command allows the async execution of the second batch file loadtest.bat, where the load test is really started.&lt;br /&gt;&lt;br /&gt;loadtest.bat:&lt;br /&gt;&lt;br /&gt;8&lt;----------------------------&lt;br /&gt;call "C:\Program Files (x86)\Microsoft Visual Studio 9.0\VC\vcvarsall.bat"&lt;br /&gt;cd "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\"&lt;br /&gt;MSTest.exe /testcontainer:c:\LoadTest\LoadTests\MyLoadTest\MyLoadTest\MyReallyCoolLoadTest.loadtest&lt;br /&gt;----------------------------&gt;8&lt;br /&gt;&lt;br /&gt;I can call trigger.bat from psexec with any automated process that can shell out to the command line or execute a batch file. Here is an example of starting a load test on the Load Test Controller using psexec from the command line:&lt;br /&gt;&lt;br /&gt;8&lt;----------------------------&lt;br /&gt;C:\&gt;psexec \\loadtestbox -u testrunner -p testrunner  cmd /c c:\loadtest\trigger\trigger.bat&lt;br /&gt;&lt;br /&gt;PsExec v1.92 - Execute processes remotely&lt;br /&gt;Copyright (C) 2001-2007 Mark Russinovich&lt;br /&gt;Sysinternals - www.sysinternals.com&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Cry havoc and let slip loose the dogs of loadtest!&lt;br /&gt;c:\loadtest\trigger\trigger.bat exited on loadtestbox with error code 0.&lt;br /&gt;----------------------------&gt;8&lt;br /&gt;&lt;br /&gt;Even though psexec exit back to the command line, the load test is actually running on my Load Test Controller:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/SOKNHpJWsmI/AAAAAAAAAKA/HuSPiRYYI3Y/s1600-h/LoadTestIsRunning.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/SOKNHpJWsmI/AAAAAAAAAKA/HuSPiRYYI3Y/s320/LoadTestIsRunning.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5251915277911110242" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I was having problems getting results to be automagically saved to the Load Test Results Repository. I figured the SQL connection string was stored in the VS2008 solution but I had to open up VS2008 on my Load Test Controller machine and manually add the connection string for my TestRunner profile. After that, the LoadTest database is updated with each execution. w00t!&lt;br /&gt;&lt;br /&gt;Now it's off to add the automagic reporting SP calls via osql.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6778543632392745862?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6778543632392745862/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6778543632392745862' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6778543632392745862'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6778543632392745862'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/09/asynchronous-invocation-of-load-test.html' title='Asynchronous Invocation of  a Load Test from a Continuous Integration System'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qsa4nRIPmPo/SOKNHpJWsmI/AAAAAAAAAKA/HuSPiRYYI3Y/s72-c/LoadTestIsRunning.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-2592204399561797641</id><published>2008-09-06T14:46:00.004-05:00</published><updated>2008-09-06T15:10:03.736-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='FreeBSD'/><category scheme='http://www.blogger.com/atom/ns#' term='DHCP'/><category scheme='http://www.blogger.com/atom/ns#' term='Win2K8'/><title type='text'>DHCP problems with Win2k8</title><content type='html'>Last weekend I brought home the Portable Server of Powah! home to do some work over the weekend and when I fired things up I wasn't getting an IP addressed assigned by the DHCP server on my FreeBSD box that is the gateway to the intarweb here at the house.&lt;br /&gt;&lt;br /&gt;I thought that very odd and when I checked /var/log/dhcpd.log it appeared that a request was going out for an IP address and being sent back but was never accepted by Win2k8. How odd...&lt;br /&gt;&lt;br /&gt;Here is what I saw in the logs:&lt;br /&gt;&lt;br /&gt;8&lt;---------------------------------&lt;br /&gt;Sep  6 14:42:09 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:10 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:13 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:13 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:22 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:22 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:39 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:42:39 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;---------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;There should have been an DHCPACK after the first two messages. Now this really confused me as I have had the Portable Server of Powah! home many times and had never had any problems getting an IP address before.&lt;br /&gt;&lt;br /&gt;But, through playing around I found out that the wireless interface could get an IP address lickitysplit and being the slacker that I am, I kept on trucking with the wifi so that I could get my work done over the holiday.&lt;br /&gt;&lt;br /&gt;I once again brought home the Portable Server of Powah! and once again, no IP address. This made me very sad as I wanted to have a wired connection to use the Gigabit goodness, that and it was just odd that I couldn't get an IP address.&lt;br /&gt;&lt;br /&gt;I bowed down before the Great Google and queried it's great knowledge and came across &lt;a href="http://support.microsoft.com/kb/928233"&gt;this KB&lt;/a&gt;. Here is the problem in a nutshell: "Windows Vista cannot obtain an IP address from certain routers or from certain non-Microsoft DHCP servers"&lt;br /&gt;&lt;br /&gt;If you follow the directions of the KB the problem can be fixed but they leave out a very important part of the puzzle. Basically, you drill down into the registry to where the various TCP/IP settings are stored and you have to find the GUID of the interface. Since you don't have "friendly" interface names like xl0, xl1 (FreeBSD interface drivers for 3Com NICs) or even friendlier Linux names like eth0, eth1, etc this becomes quite a chore.&lt;br /&gt;&lt;br /&gt;Here is what I was looking at:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/SMLhuannzZI/AAAAAAAAAJ4/dKuSHqxawXg/s1600-h/guid_interface_hell.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/SMLhuannzZI/AAAAAAAAAJ4/dKuSHqxawXg/s320/guid_interface_hell.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5243001103748418962" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;At this point I'm doing a Kelly-esque "What the hell?" Which one of these GUIDs represents my physical NIC and which one represents my virtual NIC that Hyper-V uses? Do I modify the entry for the physical NIC or the Hyper-V NIC? Confusion abounds!&lt;br /&gt;&lt;br /&gt;I manually assigned an IP address and found the GUID entry that represented the Hyper-V virtual NIC and made the changes in the Registry that the above mentioned KB required.&lt;br /&gt;&lt;br /&gt;I closed regedit, attempted to do a ipconfig /release &amp;&amp; ipconfig /renew and watched the /var/log/dhcpd.log as Win2K8 tried to get an IP address with no love. Then I remembered, this is Windows...&lt;br /&gt;&lt;br /&gt;I reboot the machine and sure enough, as it is firing up I see this entry come across /var/log/dhcpd.log:&lt;br /&gt;&lt;br /&gt;8&lt;-------------------------------&lt;br /&gt;Sep  6 14:44:48 firewall dhcpd: DHCPDISCOVER from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:44:49 firewall dhcpd: DHCPOFFER on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:44:49 firewall dhcpd: DHCPREQUEST for 10.0.0.204 (10.0.0.1) from 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;Sep  6 14:44:49 firewall dhcpd: DHCPACK on 10.0.0.204 to 10:80:f4:6f:0c:23 (auswipelaptop) via xl0&lt;br /&gt;-------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;Hazzah! It works!&lt;br /&gt;&lt;br /&gt;Apparently during an update something got tweaked and I could no longer pull an IP address from the FreeBSD dhcpd and had to apply this KB hack to get things to work. Or it could have been when I found how to get the Client for Microsoft Networks working with Hyper-V again. No matter, something got tweaked and now it is fixed which is good 'nuff for me. I'm easy like that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-2592204399561797641?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/2592204399561797641/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=2592204399561797641' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2592204399561797641'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2592204399561797641'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/09/dhcp-problems-with-win2k8.html' title='DHCP problems with Win2k8'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qsa4nRIPmPo/SMLhuannzZI/AAAAAAAAAJ4/dKuSHqxawXg/s72-c/guid_interface_hell.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6958677879463388126</id><published>2008-09-05T22:31:00.004-05:00</published><updated>2008-09-05T23:17:55.332-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer 2005'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><title type='text'>Extracting Perfmon data from the VS2008 Load Test Repository</title><content type='html'>Ok. You are performing load tests with VS2008 TSTE and you've enabled the logging of perfmon metrics in your test by tweaking the "Timing Details Storage" property of the Run Settings in your load test. Every 5 seconds the perfmon metrics you specified are being stored to the load test repository and after the run you get some metrics. That's great and all but what if you want more detailed statistics such as average AND standard deviation.&lt;br /&gt;&lt;br /&gt;Or better yet! You want at that data to generate graphs of the metrics over the duration of the test. Sure, you can do this by firing up good 'ol Perfmon and logging the values or you can just get them from your load test repository with some SQL. Why have Perfmon logging when VS2008 already does this for you?&lt;br /&gt;&lt;br /&gt;I have a need for just such an action and I've been working on the SQL statements to extract the data I need. It's not complete, but this is a major step forward to allowing me to finish up the SPs I want to collect the data dynamically for consumption by managers and other QA folks.&lt;br /&gt;&lt;br /&gt;Looking at the handy dandy schema of the load test repository (located &lt;a href="http://blogs.msdn.com/billbar/articles/529874.aspx"&gt;here&lt;/a&gt; for all to gander) I have created some SQL statements that take care of the leg work for me.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; LoadTestPerformanceCounterInstance.InstanceId, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;       LoadTestPerformanceCounterCategory.CategoryName, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;       LoadTestPerformanceCounter.CounterName, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;       LoadTestPerformanceCounterInstance.InstanceName, LoadTestPerformanceCounterCategory.MachineName, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;       &lt;span class="kwrd"&gt;AVG&lt;/span&gt;(LoadTestPerformanceCounterSample.ComputedValue) &lt;span class="kwrd"&gt;AS&lt;/span&gt; AverageValue,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;       stdev(LoadTestPerformanceCounterSample.ComputedValue) &lt;span class="kwrd"&gt;as&lt;/span&gt; StdDev,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;       &lt;span class="kwrd"&gt;count&lt;/span&gt;(LoadTestPerformanceCounterSample.ComputedValue) &lt;span class="kwrd"&gt;as&lt;/span&gt; &lt;span class="kwrd"&gt;Count&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&lt;span class="kwrd"&gt;FROM&lt;/span&gt;  LoadTestPerformanceCounterCategory &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;               LoadTestPerformanceCounter &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;               LoadTestPerformanceCounterInstance &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTestPerformanceCounter.LoadTestRunId = LoadTestPerformanceCounterInstance.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;               LoadTestPerformanceCounter.CounterId = LoadTestPerformanceCounterInstance.CounterId &lt;span class="kwrd"&gt;ON&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;               LoadTestPerformanceCounterCategory.CounterCategoryId = LoadTestPerformanceCounter.CounterCategoryId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;               LoadTestPerformanceCounterCategory.LoadTestRunId = LoadTestPerformanceCounter.LoadTestRunId &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;               LoadTestPerformanceCounterSample &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTestPerformanceCounterInstance.LoadTestRunId = LoadTestPerformanceCounterSample.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;               LoadTestPerformanceCounterInstance.InstanceId = LoadTestPerformanceCounterSample.InstanceId&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt; (LoadTestPerformanceCounter.LoadTestRunId = 83)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;&lt;span class="kwrd"&gt;GROUP&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; LoadTestPerformanceCounterCategory.CategoryName, LoadTestPerformanceCounter.CounterName, LoadTestPerformanceCounterInstance.InstanceName, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;               LoadTestPerformanceCounterInstance.InstanceId, LoadTestPerformanceCounterCategory.MachineName&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; LoadTestPerformanceCounterInstance.InstanceId&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;In the above example, the reference to LoadTestRunId is hard coded to a previous run that I have in my results repository. For all my other SPs I use the GUID of the test and will convert the above SQL statement to reference GUID all in good time. The query above returns results like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;0     .NET CLR Memory     Gen 0 heap size                             w3wp     10.0.0.11     944824777.209431     185400315.203616     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;1     .NET CLR Memory     Large Object Heap size                      w3wp     10.0.0.11     86994000.5298197     29625345.9051784     721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;2     .NET CLR Memory     # Gen 0 Collections                         w3wp     10.0.0.11     132.608876560333     69.5129454689118     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;3     .NET CLR Memory     Gen 2 heap size                             w3wp     10.0.0.11     197119053.001387     78824416.0154383     721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;4     .NET CLR Memory     Allocated Bytes/sec                         w3wp     10.0.0.11     63521791.4160888     91335026.9218956     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;5     .NET CLR Memory     # Gen 2 Collections                         w3wp     10.0.0.11     29.5104022191401     12.1351287581502     721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;6     .NET CLR Memory     Promoted Memory from Gen 0                  w3wp     10.0.0.11     42540355.2815534     13356693.0175676     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;7     .NET CLR Memory     # Induced GC                                w3wp     10.0.0.11     0                    0                    721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;8     .NET CLR Memory     Gen 0 Promoted Bytes/Sec                    w3wp     10.0.0.11     418557.084366331     1120132.98732717     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;9     .NET CLR Memory     Promoted Memory from Gen 1                  w3wp     10.0.0.11     21740368.0721221     25126733.8606902     721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;10    .NET CLR Memory     # GC Handles                                w3wp     10.0.0.11     3750.73370319001     216.191892870829     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;11    .NET CLR Memory     # Gen 1 Collections                         w3wp     10.0.0.11     70.0693481276005     35.4698332819129     721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;12    .NET CLR Memory     Gen 1 heap size                             w3wp     10.0.0.11     62394980.9237171     31034211.4233198     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;13    .NET CLR Memory     Finalization Survivors                      w3wp     10.0.0.11     8217.1040221914      3024.02383909951     721&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;14    .NET CLR Memory     Promoted Finalization-Memory from Gen 0     w3wp     10.0.0.11     11415345.6768377     11434973.2174913     721&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;15    .NET CLR Memory     % Time in GC                                w3wp     10.0.0.11     3.48545611573799     6.73310861817717     721&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The first column is the InstanceId of the perfmon counter and the information for the counter is split up into three separate columns (CategoryName, CounterName and InstanceName) but the column that ties them all together is the InstanceId.&lt;br /&gt;&lt;br /&gt;Ok. That's fine and dandy but what about graphing the metrics over the duration of the load test?&lt;br /&gt;&lt;br /&gt;Well, that's another SQL query I wrote that is in the alpha stage of development. In the above example I displayed 15 out of 466 metrics that were logged. Let's choose to graph the .NET CLR Memory Gen 2 heap size for w3wp. This particular metric happens to be InstanceId #3 this time around and we can use it in the SQL statement below to extract the data that can eventually be used to graph the results.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; LoadTestPerformanceCounterInstance.InstanceName,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;       LoadTestPerformanceCounterSample.ComputedValue&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;FROM&lt;/span&gt;  LoadTestPerformanceCounterInstance &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;      LoadTestPerformanceCounterSample &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTestPerformanceCounterInstance.LoadTestRunId = LoadTestPerformanceCounterSample.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      LoadTestPerformanceCounterInstance.InstanceId = LoadTestPerformanceCounterSample.InstanceId&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt; (LoadTestPerformanceCounterInstance.LoadTestRunId = 83) &lt;span class="kwrd"&gt;AND&lt;/span&gt; (LoadTestPerformanceCounterInstance.InstanceId = 3)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; LoadTestPerformanceCounterSample.TestRunIntervalId&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;This query yields the results of:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;w3wp    0&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;w3wp    0&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;w3wp    96&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;w3wp    3231264&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;w3wp    2.170597E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;w3wp    2.170597E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;w3wp    2.170597E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;w3wp    2.976022E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;w3wp    2.976022E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;w3wp    2.976022E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;w3wp    2.976022E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;w3wp    4.127677E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;w3wp    4.127677E+07&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;w3wp    4.127677E+07&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;and so on and so on until all 722 metrics are displayed. We can take those 722 metrics and copypasta into Excel and generate a quick and dirty graph:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/SMIAmi2KYOI/AAAAAAAAAJw/XGNPm0lc-70/s1600-h/gen2_mem_example.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/SMIAmi2KYOI/AAAAAAAAAJw/XGNPm0lc-70/s320/gen2_mem_example.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5242753578401685730" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Viola! Quick and dirty graph from Excel!&lt;br /&gt;&lt;br /&gt;I'll need to change the column output to show more details on the metrics to display all three columns to better identify the perfmon metric and also display the time that the metric was taken.&lt;br /&gt;&lt;br /&gt;After I do all that I'll need to write some code to generate graphs on the fly. I'm thinking that &lt;a href="http://zedgraph.org/wiki/index.php?title=Main_Page"&gt;this project on SourceForge&lt;/a&gt; will fit the bill nicely and there seems to be a bunch of examples out on the 'net how to use ZedGraph to it's fullest.&lt;br /&gt;&lt;br /&gt;Oh yeah! Almost forgot. I need to double check the results between the first query and the individual information for InstanceId 3 metric (the .NET CLR Gen 2 heap size for w3wp). The first query output says the average value is 197119053.001387. Take the average of output that I did the copypasta into Excel gonkulates to an average of 197119053.4. The difference between the two? A measly 0.355. I'm thinking that is good 'nuff for government work.&lt;br /&gt;&lt;br /&gt;Now I just need to finish up the queries and get them into SPs and integrate them with the other code that I've written that does automagic before/after comparison of transaction response times (more on that at a later date).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6958677879463388126?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6958677879463388126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6958677879463388126' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6958677879463388126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6958677879463388126'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/09/extracting-perfmon-data-from-vs2008.html' title='Extracting Perfmon data from the VS2008 Load Test Repository'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_qsa4nRIPmPo/SMIAmi2KYOI/AAAAAAAAAJw/XGNPm0lc-70/s72-c/gen2_mem_example.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5154438294476584799</id><published>2008-09-02T13:00:00.006-05:00</published><updated>2008-09-02T13:29:06.557-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SDC Tasks'/><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><title type='text'>Using NAnt custom task to wrap SDC Task to create VDir</title><content type='html'>I got my VDir code working, but there are some caveats that have to be watched, I've found.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NAnt.Core;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NAnt.Core.Attributes;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Sdc.Tasks;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Build.Utilities;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Build.Framework;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; Auswipe.NAntTasks.VDirTasks {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;  # region Delete VDir&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;  [TaskName(&lt;span class="str"&gt;"deletevdir"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; DeleteVDir : NAnt.Core.Task {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; vDirToDelete;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; webSiteName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"vdirtodelete"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; VDirToDelete {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; vDirToDelete; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;      set { vDirToDelete = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"machinename"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; MachineName {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; machineName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;      set { machineName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"websitename"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; WebSiteName {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; webSiteName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;      set { webSiteName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ExecuteTask() {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;      Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : The following AppPool will be deleted: '"&lt;/span&gt; + vDirToDelete + &lt;span class="str"&gt;"'"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;      &lt;span class="kwrd"&gt;try&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;        Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory delVDirObject = &lt;span class="kwrd"&gt;new&lt;/span&gt; Microsoft.Sdc.Tasks.Web.WebSite.DeleteVirtualDirectory();&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;        delVDirObject.MachineName          = machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;        delVDirObject.VirtualDirectoryName = vDirToDelete;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;        delVDirObject.WebSiteName          = webSiteName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (delVDirObject.Execute()) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;          Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : The VDir '"&lt;/span&gt; + vDirToDelete + &lt;span class="str"&gt;"' was successful deleted."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;          Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : ERROR: The VDir '"&lt;/span&gt; + vDirToDelete + &lt;span class="str"&gt;"' was NOT deleted but an exception was not thrown."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;      } &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;        Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : VDir deletion failed with the resulting exception:"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;        Project.Log(Level.Error, e.ToString());&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;  &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;  # region Create VDir&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;  [TaskName(&lt;span class="str"&gt;"createvdir"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CreateVDir : NAnt.Core.Task {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; vDirName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; webSiteName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; appPoolName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; fullPath;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"apppoolname"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; AppPoolName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; appPoolName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;      set { appPoolName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"vdirname"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; VDirName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; vDirName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;      set { vDirName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"website"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; WebSite {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; webSiteName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;      set { webSiteName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"machinename"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; MachineName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; machineName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt;      set { machineName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"path"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; FullPath {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  87:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; fullPath; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  88:  &lt;/span&gt;      set { fullPath = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ExecuteTask() {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;      Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : The following VDir will be created: '"&lt;/span&gt; + vDirName + &lt;span class="str"&gt;"'"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt;      &lt;span class="kwrd"&gt;try&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;        Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory createVDirObject = &lt;span class="kwrd"&gt;new&lt;/span&gt; Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory();&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  94:  &lt;/span&gt;        createVDirObject.AppCreate            = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  95:  &lt;/span&gt;        createVDirObject.AppPoolId            = appPoolName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  96:  &lt;/span&gt;        createVDirObject.MachineName          = machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  97:  &lt;/span&gt;        createVDirObject.VirtualDirectoryName = vDirName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  98:  &lt;/span&gt;        createVDirObject.WebSiteName          = webSiteName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  99:  &lt;/span&gt;        createVDirObject.Path                 = fullPath;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 100:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (createVDirObject.Execute()) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 101:  &lt;/span&gt;          Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : The VDir '"&lt;/span&gt; + vDirName + &lt;span class="str"&gt;"' was successfuly created."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 102:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 103:  &lt;/span&gt;          Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : ERROR: The VDir '"&lt;/span&gt; + vDirName + &lt;span class="str"&gt;"' was NOT created but an exception was not thrown."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 104:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 105:  &lt;/span&gt;      } &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 106:  &lt;/span&gt;        Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 107:  &lt;/span&gt;        Project.Log(Level.Error, e.ToString());&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 108:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 109:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 110:  &lt;/span&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt; 111:  &lt;/span&gt;  &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt; 112:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;A caveat that I've found though is that even though the invoked SDC Task might run into a problem during the .Execute call, .Execute still returns a true so my NAnt task doesn't know if the process has ran into a problem.&lt;br /&gt;&lt;br /&gt;In the example below, I'm trying to create a VDir with an AppPool that doesn't exist:&lt;br /&gt;&lt;br /&gt;8&lt;-------------------------------------------&lt;br /&gt;NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)&lt;br /&gt;Copyright (C) 2001-2006 Gerry Shaw&lt;br /&gt;http://nant.sourceforge.net&lt;br /&gt;&lt;br /&gt;Buildfile: file:///C:/customtask/default.build&lt;br /&gt;Target framework: Microsoft .NET Framework 2.0&lt;br /&gt;Target(s) specified: test-task &lt;br /&gt;&lt;br /&gt;[loadtasks] Scanning assembly "Auswipe.NAntTasks.VDirTasks" for extensions.&lt;br /&gt;[loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions.&lt;br /&gt;&lt;br /&gt;test-task:&lt;br /&gt;&lt;br /&gt;     [echo] Creating VDir&lt;br /&gt;Auswipe.NAntTasks.VDirTasks : The following VDir will be created: 'someVDir'&lt;br /&gt;Creating virtual directory "someVDir".&lt;br /&gt;A task error has occured.&lt;br /&gt;Message               = Exception has been thrown by the target of an invocation.&lt;br /&gt;VirtualDirectoryName  = someVDir&lt;br /&gt;Path                  = c:\somewebsite&lt;br /&gt;MachineName           = Auswipel-rfs2we&lt;br /&gt;WebSiteName           = Default Web Site&lt;br /&gt;WebAppName            = &lt;String.Empty&gt;&lt;br /&gt;AppPoolId             = AppPoolDoesNotExist&lt;br /&gt;AppCreate             = True&lt;br /&gt;AnonymousUserName     = &lt;String.Empty&gt;&lt;br /&gt;AnonymousUserPassword = &lt;String.Empty&gt;&lt;br /&gt;UncUserName           = &lt;String.Empty&gt;&lt;br /&gt;UncPassword           = &lt;String.Empty&gt;&lt;br /&gt;AuthFlags             = &lt;String.Empty&gt;&lt;br /&gt;AccessFlags           = &lt;String.Empty&gt;&lt;br /&gt;&lt;br /&gt;   at System.DirectoryServices.DirectoryEntry.Invoke(String methodName, Object[] args)&lt;br /&gt;   at Microsoft.Sdc.Tasks.Configuration.Web.VirtualDirectory.AppCreate3() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Configuration\Web\VirtualDirectory.cs:line 324&lt;br /&gt;   at Microsoft.Sdc.Tasks.Web.WebSite.CreateVirtualDirectory.InternalExecute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\Web\WebSite\CreateVirtualDirectory.cs:line 315&lt;br /&gt;   at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 66&lt;br /&gt;Element not found. (Exception from HRESULT: 0x80070490)&lt;br /&gt;&lt;br /&gt;PseudoEngineException&lt;br /&gt;   at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77&lt;br /&gt;   at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)&lt;br /&gt;   at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String message, Object[] messageArgs)&lt;br /&gt;   at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 95&lt;br /&gt;Auswipe.NAntTasks.VDirTasks : VDir creation failed with the resulting exception:&lt;br /&gt;System.ApplicationException: PseudoEngineException&lt;br /&gt;   at Microsoft.Sdc.Tasks.PseudoBuildEngine.LogErrorEvent(BuildErrorEventArgs eventArgs) in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\PseudoBuildEngine.cs:line 77&lt;br /&gt;   at Microsoft.Build.Utilities.TaskLoggingHelper.LogError(String subcategory, String errorCode, String helpKeyword, String file, Int32 lineNumber, Int32 columnNumber, Int32 endLineNumber, Int32 endColumnNumber, String message, Object[] messageArgs)&lt;br /&gt;   at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace, Boolean showDetail, String file)&lt;br /&gt;   at Microsoft.Build.Utilities.TaskLoggingHelper.LogErrorFromException(Exception exception, Boolean showStackTrace)&lt;br /&gt;   at Microsoft.Sdc.Tasks.TaskBase.Execute() in c:\projects\codeplex\sdctasks\Solutions\Main\Tasks\TaskBase.cs:line 99&lt;br /&gt;   at Auswipe.NAntTasks.VDirTasks.CreateVDir.ExecuteTask()&lt;br /&gt;&lt;br /&gt;BUILD SUCCEEDED - 2 non-fatal error(s), 0 warning(s)&lt;br /&gt;&lt;br /&gt;Total time: 0.2 seconds.&lt;br /&gt;-------------------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;You would think that .Execute should return a False for the call but it doesn't. Notice the verbage of "2 non-fatal error(s)" so it appears that the SDC Task does not treat the non-existant Applicaiton Pool as a fatal error. Good to know! &lt;br /&gt;&lt;br /&gt;Going back and looking at the IIS Manager we see this for the create VDir:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_qsa4nRIPmPo/SL2DUD6HVqI/AAAAAAAAAJo/NYBThbnHrhw/s1600-h/VDir_sans_Valid_AppPool.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://3.bp.blogspot.com/_qsa4nRIPmPo/SL2DUD6HVqI/AAAAAAAAAJo/NYBThbnHrhw/s320/VDir_sans_Valid_AppPool.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5241489921998804642" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Notice how the Application Pool is set to "&amp;lt;Invalid Application Pool&amp;gt;."&lt;br /&gt;&lt;br /&gt;So, the VDir was created, despite the lack of a valid Application Pool. That's something to be aware of when testing out NAnt tasks that wrap SDC Task functionality.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5154438294476584799?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5154438294476584799/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5154438294476584799' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5154438294476584799'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5154438294476584799'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/09/using-nant-custom-task-to-wrap-sdc-task.html' title='Using NAnt custom task to wrap SDC Task to create VDir'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_qsa4nRIPmPo/SL2DUD6HVqI/AAAAAAAAAJo/NYBThbnHrhw/s72-c/VDir_sans_Valid_AppPool.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-7044279172139125744</id><published>2008-08-31T03:18:00.006-05:00</published><updated>2008-09-05T23:17:29.425-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SDC Tasks'/><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><title type='text'>Hey, whaddya know? It works!</title><content type='html'>I finished the first part of my task to create a custom NAnt task that utilizes the SDC Tasks that were originally built for MSBuild. What I am interested in is deleting and creating custom AppPools for IIS as well as deleting and creating vdirs bound to custom AppPools and not DefaultAppPool.&lt;br /&gt;&lt;br /&gt;I did some quick and dirty coding today and came up with this first pass:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Text;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NAnt.Core;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NAnt.Core.Attributes;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Sdc.Tasks;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Build.Utilities;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; Microsoft.Build.Framework;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; Auswipe.NAntTasks.AppPoolTasks {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;  # region DeleteAppPool&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;  [TaskName(&lt;span class="str"&gt;"deleteapppool"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; DeleteAppPool : NAnt.Core.Task {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; appPoolName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"apppoolname"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; AppPoolName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; appPoolName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;      set { appPoolName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"machinename"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; MachineName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; machineName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;      set { machineName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ExecuteTask() {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;      Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be deleted: '"&lt;/span&gt; + appPoolName + &lt;span class="str"&gt;"'"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;      &lt;span class="kwrd"&gt;try&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;        Microsoft.Sdc.Tasks.Web.DeleteAppPool.DeleteAppPool delAppPoolObject = &lt;span class="kwrd"&gt;new&lt;/span&gt; Microsoft.Sdc.Tasks.Web.DeleteAppPool.DeleteAppPool();&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;        delAppPoolObject.AppPoolName = appPoolName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;        delAppPoolObject.MachineName = machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (delAppPoolObject.Execute()) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;          Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : The AppPool '"&lt;/span&gt; + appPoolName + &lt;span class="str"&gt;"' was successful deleted."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;          Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : ERROR: The AppPool '"&lt;/span&gt; + appPoolName + &lt;span class="str"&gt;"' was NOT deleted but an exception was not thrown."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;      } &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;        Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : AppPool deletion failed with the resulting exception:"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;        Project.Log(Level.Error, e.ToString());&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;  &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;  # region Create AppPool&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;  [TaskName(&lt;span class="str"&gt;"createapppool"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; CreateAppPool : NAnt.Core.Task {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; appPoolName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; userName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; passWord;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; userDomain;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"apppoolname"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; AppPoolName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; appPoolName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;      set { appPoolName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"username"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; UserName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; userName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;      set { userName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"password"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; PassWord {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; passWord; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;      set { passWord = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"machinename"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; MachineName {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;      get { &lt;span class="kwrd"&gt;return&lt;/span&gt; machineName; }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt;      set { machineName = &lt;span class="kwrd"&gt;value&lt;/span&gt;; }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ExecuteTask() {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;      Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be created: '"&lt;/span&gt; + appPoolName + &lt;span class="str"&gt;"'"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;      &lt;span class="kwrd"&gt;try&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;        Microsoft.Sdc.Tasks.Web.AppPool.Create createAppPoolObject = &lt;span class="kwrd"&gt;new&lt;/span&gt; Microsoft.Sdc.Tasks.Web.AppPool.Create();&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;        createAppPoolObject.AppPoolName  = appPoolName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  81:  &lt;/span&gt;        createAppPoolObject.IdentityType = &lt;span class="str"&gt;"SpecifiedUserAccount"&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  82:  &lt;/span&gt;        createAppPoolObject.Identity     = userName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  83:  &lt;/span&gt;        createAppPoolObject.Password     = passWord;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  84:  &lt;/span&gt;        createAppPoolObject.MachineName  = machineName;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  85:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (createAppPoolObject.Execute()) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  86:  &lt;/span&gt;          Project.Log(Level.Info, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : The AppPool '"&lt;/span&gt; + appPoolName + &lt;span class="str"&gt;"' was successfuly created."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  87:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  88:  &lt;/span&gt;          Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : ERROR: The AppPool '"&lt;/span&gt; + appPoolName + &lt;span class="str"&gt;"' was NOT created but an exception was not thrown."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  89:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  90:  &lt;/span&gt;      } &lt;span class="kwrd"&gt;catch&lt;/span&gt; (Exception e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  91:  &lt;/span&gt;        Project.Log(Level.Error, &lt;span class="str"&gt;"Auswipe.NAntTasks.AppPoolTasks : AppPool creation failed with the resulting exception:"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  92:  &lt;/span&gt;        Project.Log(Level.Error, e.ToString());&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  93:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  94:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  95:  &lt;/span&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  96:  &lt;/span&gt;  &lt;span class="preproc"&gt;#endregion&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  97:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;After I created my Auswipe.NAntTasks.AppPoolTasks.dll I had to copy both Auswipe.NAntTasks.AppPoolTasks.dll and Microsoft.Sdc.Tasks.dll to the subdir where I put my default.build that I've been playing with so that the code could properly execute and create my AppPool.&lt;br /&gt;&lt;br /&gt;Here is my default.build script for NAnt:&lt;br /&gt;&lt;br /&gt;8&lt;-----------------------------------------&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt;?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;project&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Task Test"&lt;/span&gt; &lt;span class="attr"&gt;default&lt;/span&gt;&lt;span class="kwrd"&gt;="test-task"&lt;/span&gt; &lt;span class="attr"&gt;basedir&lt;/span&gt;&lt;span class="kwrd"&gt;="."&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://nant.sf.net/release/0.86-beta1/nant.xsd"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;loadtasks&lt;/span&gt; &lt;span class="attr"&gt;assembly&lt;/span&gt;&lt;span class="kwrd"&gt;="Auswipe.NAntTasks.AppPoolTasks.dll"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="test-task"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;    &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;echo&lt;/span&gt; &lt;span class="attr"&gt;message&lt;/span&gt;&lt;span class="kwrd"&gt;="Deleting AppPool."&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;deleteapppool&lt;/span&gt; &lt;span class="attr"&gt;apppoolname&lt;/span&gt; = &lt;span class="kwrd"&gt;"AuswipeAppPool"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;                   &lt;span class="attr"&gt;machinename&lt;/span&gt; = &lt;span class="kwrd"&gt;"Auswipel-rfs2we"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;     &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;echo&lt;/span&gt; &lt;span class="attr"&gt;message&lt;/span&gt;&lt;span class="kwrd"&gt;="Create AppPool."&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;createapppool&lt;/span&gt; &lt;span class="attr"&gt;apppoolname&lt;/span&gt; = &lt;span class="kwrd"&gt;"AuswipeAppPool"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;                   &lt;span class="attr"&gt;username&lt;/span&gt;    = &lt;span class="kwrd"&gt;"AuswipeAppPoolUser"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;                   &lt;span class="attr"&gt;password&lt;/span&gt;    = &lt;span class="kwrd"&gt;"supersekrit"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;                   &lt;span class="attr"&gt;machinename&lt;/span&gt; = &lt;span class="kwrd"&gt;"Auswipel-rfs2we"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;    &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;                                   &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;    &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;project&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;-----------------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;And here is the output from the execution:&lt;br /&gt;&lt;br /&gt;8&lt;-----------------------------------------&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;C:\customtask&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;nant /f:default.build test-task&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;Copyright (C) 2001-2006 Gerry Shaw&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;http://nant.sourceforge.net&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;Buildfile: file:///C:/customtask/default.build&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;Target framework: Microsoft .NET Framework 2.0&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;Target(s) specified: test-task&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;[loadtasks] Scanning assembly "Auswipe.NAntTasks.AppPoolTasks" for extensions.&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;test-task:&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;     [echo] Deleting AppPool.&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be deleted: 'AuswipeAppPool'&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;Auswipe.NAntTasks.AppPoolTasks : The AppPool 'AuswipeAppPool' was successful deleted.&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;     [echo] Create AppPool.&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;Auswipe.NAntTasks.AppPoolTasks : The following AppPool will be created: 'AuswipeAppPool'&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;Creating app pool "AuswipeAppPool".&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;Auswipe.NAntTasks.AppPoolTasks : The AppPool 'AuswipeAppPool' was successfuly created.&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;BUILD SUCCEEDED&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;Total time: 0.1 seconds.&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;-----------------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;Here we see a screen shot of the system before NAnt execution:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_qsa4nRIPmPo/SLpWBH81QxI/AAAAAAAAAJQ/6II5RqghyUg/s1600-h/CustomAppPool_Before.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://2.bp.blogspot.com/_qsa4nRIPmPo/SLpWBH81QxI/AAAAAAAAAJQ/6II5RqghyUg/s320/CustomAppPool_Before.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5240595693713048338" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now I execute the NAnt script and re-fresh the IIS manager view of AppPools and lo-and-behold! It worked!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_qsa4nRIPmPo/SLpWPpdts3I/AAAAAAAAAJY/XztvjGt5wLw/s1600-h/CustomAppPool_After_1.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://4.bp.blogspot.com/_qsa4nRIPmPo/SLpWPpdts3I/AAAAAAAAAJY/XztvjGt5wLw/s320/CustomAppPool_After_1.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5240595943227503474" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I checked the properties of the newly created AppPool and we see that it has the credentials I specified in the default.build. Hazzah!&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_qsa4nRIPmPo/SLpWc8MND3I/AAAAAAAAAJg/gIhxWObIpmE/s1600-h/CustomAppPool_After_2.JPG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_qsa4nRIPmPo/SLpWc8MND3I/AAAAAAAAAJg/gIhxWObIpmE/s320/CustomAppPool_After_2.JPG" border="0" alt=""id="BLOGGER_PHOTO_ID_5240596171592634226" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Now the next step is to go forth and code the deletion and creation of vdirs with the SDC Tasks.&lt;br /&gt;&lt;br /&gt;Nifty!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-7044279172139125744?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/7044279172139125744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=7044279172139125744' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7044279172139125744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7044279172139125744'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/hey-whaddya-know-it-works.html' title='Hey, whaddya know? It works!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_qsa4nRIPmPo/SLpWBH81QxI/AAAAAAAAAJQ/6II5RqghyUg/s72-c/CustomAppPool_Before.JPG' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-7624417317537663099</id><published>2008-08-29T23:48:00.001-05:00</published><updated>2008-09-05T01:57:50.272-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='NAnt'/><title type='text'>Creating my own custom NAnt tasks in C#</title><content type='html'>One of the things that I find myself doing when not crunching numbers as a performance enginerd is learning the ways of the Buildmiester. We use NAnt for deploying our code from the build server to a target test machine and UAT test environment.&lt;br /&gt;&lt;br /&gt;During the process we use NAnt to delete and create vdirs under IIS with mkiisdir which is handy dandy and all but it doesn't fully get the job done as some of the vdirs we need should not belong to DefaultAppPool. And also unfortunately it appears that NAntContrib doesn't have all the tools that I need to delete and create a custom AppPool that I need to create with a special local username/password.&lt;br /&gt;&lt;br /&gt;I found the command line code to create the AppPool that I want and I was just going to wrap the execution of the command line stuff with an &amp;lt;exec&amp;gt; task but I still couldn't find how to create a vdir on the command line and have it binding to my custom AppPool that I wanted.&lt;br /&gt;&lt;br /&gt;This morning after Scrum another coder working on a sister project that uses MSBuild instead of NAnt suggested that I take a gander at the &lt;a href="http://www.codeplex.com/sdctasks"&gt;SDC Tasks for MSBuild up on CodePlex&lt;/a&gt;. After pulling down the latest and greatest SDC Tasks it appears that I can delete, then create my custom AppPool and then delete/create a customer vdir bound to my custom AppPool. How great is that?&lt;br /&gt;&lt;br /&gt;Except that SDC Tasks was designed for MSBuild and we are already heavily into NAnt. Bummer, right? Well. I know that NAnt has a &amp;lt;msbuild&amp;gt; task but going from NAnt into MSBuild that would reference a custom MSBuild script and pass the params around seemed like a real PITA, not to mention a documentation nightmare. But then again, I really want this functionality of the SDC Tasks.&lt;br /&gt;&lt;br /&gt;What do I do? What do I do?&lt;br /&gt;&lt;br /&gt;After consulting the Great Google, I decided that it was time to "Man up, Nancy Boy!" as one of the female trainers at my gym would say (actually shout during a middle of a grueling spin class with the grunting and sweating and the pain!) and learn how to code my own custom NAnt task and build a custom wrapper for the SDC stuff so that I could invoke the functionality from within my NAnt script.&lt;br /&gt;&lt;br /&gt;After consulting some blogs and some pain and suffering I've been able to create a basic useless NAnt task. I haven't written the wrapper code yet, but I think that'll be simple compared to actually getting a custom NAnt task up and running.&lt;br /&gt;&lt;br /&gt;This is what I did to create my very own useless NAnt task.&lt;br /&gt;&lt;br /&gt;First, I created a .NET Class project by the name of "NAntCustomAuswipeTasks." It seems to be very important that the assembly name postfix be "Tasks.dll." The second thing is that it is important to use class decorators for specifying various things that NAnt needs to get the job done. Two of those being "TaskName" and "TaskAttribute."&lt;br /&gt;&lt;br /&gt;Another important thing that I learned is that when calling the task from inside the NAnt script that the task name is case sensitive to the decorator string.&lt;br /&gt;&lt;br /&gt;Below is my code for my useless NAnt task:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NAnt.Core;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; NAnt.Core.Attributes;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; NAntCustomAuswipeTasks {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;  [TaskName(&lt;span class="str"&gt;"auswipetask"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; AuswipeTask : Task {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; customData;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;    [TaskAttribute(&lt;span class="str"&gt;"customdata"&lt;/span&gt;, Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    [StringValidator(AllowEmpty = &lt;span class="kwrd"&gt;false&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;    &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;string&lt;/span&gt; CustomData {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;      get { &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;        &lt;span class="kwrd"&gt;return&lt;/span&gt; customData; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;      }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;      set { &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;        customData = &lt;span class="kwrd"&gt;value&lt;/span&gt;; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;      }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;    &lt;span class="kwrd"&gt;protected&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ExecuteTask() {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;      Project.Log(Level.Info, &lt;span class="str"&gt;"[AuwipeTask] The parameter passed is '"&lt;/span&gt; + customData + &lt;span class="str"&gt;"'."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Take notice of the TaskName, TaskAttribute and StringValidator decorators. Very important or otherwise your attempts to call your custom NAnt task will fail. The blog entry that I was learning on didn't have the TaskName decorator and so my call to my task was failing. Very frustrating.&lt;br /&gt;&lt;br /&gt;After compiling into my assembly of "NAntCustomAuswipeTasks.dll" I copied the DLL into the same subdir where my NAnt build file resides. In my case, this is a folder off of my Desktop subdir where I am playing around checking to see if it really works. I'm using a default.build file right now just to make things simple:&lt;br /&gt;&lt;br /&gt;8&lt;--------------------------------&lt;br /&gt;C:\Users\Auswipe\Desktop\NAntTask&gt;dir&lt;br /&gt; Volume in drive C has no label.&lt;br /&gt; Volume Serial Number is A463-3DC5&lt;br /&gt;&lt;br /&gt; Directory of C:\Users\Auswipe\Desktop\NAntTask&lt;br /&gt;&lt;br /&gt;08/29/2008  11:44 PM    &amp;lt;DIR&amp;gt;            .&lt;br /&gt;08/29/2008  11:44 PM    &amp;lt;DIR&amp;gt;            ..&lt;br /&gt;08/29/2008  11:44 PM               404 default.build&lt;br /&gt;08/29/2008  11:45 PM             4,608 NAntCustomAuswipeTasks.dll&lt;br /&gt;               2 File(s)          5,012 bytes&lt;br /&gt;               2 Dir(s)  332,392,656,896 bytes free&lt;br /&gt;--------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;8&lt;--------------------------------&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;?&lt;/span&gt;&lt;span class="html"&gt;xml&lt;/span&gt; &lt;span class="attr"&gt;version&lt;/span&gt;&lt;span class="kwrd"&gt;="1.0"&lt;/span&gt;?&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;project&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Custom NAnt Task Test"&lt;/span&gt; &lt;span class="attr"&gt;default&lt;/span&gt;&lt;span class="kwrd"&gt;="test-task"&lt;/span&gt; &lt;span class="attr"&gt;basedir&lt;/span&gt;&lt;span class="kwrd"&gt;="."&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="http://nant.sf.net/release/0.86-beta1/nant.xsd"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;loadtasks&lt;/span&gt; &lt;span class="attr"&gt;assembly&lt;/span&gt;&lt;span class="kwrd"&gt;="NAntCustomAuswipeTasks.dll"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="test-task"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;echo&lt;/span&gt; &lt;span class="attr"&gt;message&lt;/span&gt;&lt;span class="kwrd"&gt;="Here goes nothing...."&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;auswipetask&lt;/span&gt; &lt;span class="attr"&gt;customdata&lt;/span&gt;&lt;span class="kwrd"&gt;="Killroy was here."&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;target&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;project&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;--------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;And here I am testing out my useless NAnt task:&lt;br /&gt;&lt;br /&gt;8&lt;--------------------------------&lt;br /&gt;C:\Users\auswipe\Desktop\NAntTask&gt;nant /f:default.build test-task&lt;br /&gt;NAnt 0.85 (Build 0.85.2478.0; release; 10/14/2006)&lt;br /&gt;Copyright (C) 2001-2006 Gerry Shaw&lt;br /&gt;http://nant.sourceforge.net&lt;br /&gt;&lt;br /&gt;Buildfile: file:///C:/Users/auswipe/Desktop/NAntTask/default.build&lt;br /&gt;Target framework: Microsoft .NET Framework 2.0&lt;br /&gt;Target(s) specified: test-task&lt;br /&gt;&lt;br /&gt;[loadtasks] Scanning assembly "NAntCustomAuswipeTasks" for extensions.&lt;br /&gt;&lt;br /&gt;test-task:&lt;br /&gt;&lt;br /&gt;     [echo] Here goes nothing....&lt;br /&gt; [AuwipeTask] The parameter passed is 'Killroy was here.'.&lt;br /&gt;&lt;br /&gt;BUILD SUCCEEDED&lt;br /&gt;&lt;br /&gt;Total time: 0 seconds.&lt;br /&gt;&lt;br /&gt;--------------------------------&gt;8&lt;br /&gt;&lt;br /&gt;Notice that I go ahead and specify the default.build file on the command line along with the target despite having the test-task as the defacto target of default.build and knowing full well that NAnt looks for default.build. Why'd I do that? Just because I hate seeing demonstrations of some technology where the author assumes that the readers know all the ins and out of the subject. Sure, I can get the same results with `nant` as I did with `nant /f:default.build test-task` but having been in the RTFM mode before, I can gleam more information from `nant /f:default.build test-task` and learn the rest later. But that is just my own opinion. YMMV, IANAL, ROFLWTFBBQ.&lt;br /&gt;&lt;br /&gt;So, now that I've created my useless NAnt task successfully I can work on wrapping the SDC Tasks that I need to make my world (or at least my deploy) a better place. And the less time I'm spending cleaning up after a deploy changing settings is more time I can be sitting on my butt surfing the web, or something like that.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-7624417317537663099?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/7624417317537663099/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=7624417317537663099' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7624417317537663099'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7624417317537663099'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/creating-my-own-custom-nant-tasks-in-c.html' title='Creating my own custom NAnt tasks in C#'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8893720439525106454</id><published>2008-08-28T16:22:00.001-05:00</published><updated>2008-09-05T01:56:23.378-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Metrics'/><title type='text'>Interesting blog post about Apdex</title><content type='html'>Here is an interesting post from all around smart dude, Tim Koopmans at 90kts.com concerning reporting response time to managers and other non-techy folks and it is interesting.&lt;br /&gt;&lt;br /&gt;Check it out &lt;a href="http://90kts.com/blog/2008/performance-testing-with-apdex/"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Basically, it is a way to gonkulate a value and color code scheme to show if the response time for a transaction falls within acceptable limits for the type of transaction.&lt;br /&gt;&lt;br /&gt;Checking out http://www.apdex.org/using.html you can see some of the target response times for the Apdex coefficient gonkulations.&lt;br /&gt;&lt;br /&gt;This might be a better a cool way to supplement the way that I am planning on comparing before/after tests with Z-score values and a lot easier for management folk.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8893720439525106454?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8893720439525106454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8893720439525106454' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8893720439525106454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8893720439525106454'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/interesting-bog-post-about-apdex.html' title='Interesting blog post about Apdex'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-3539215865572692683</id><published>2008-08-14T13:17:00.000-05:00</published><updated>2008-08-30T00:34:09.695-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hyper-V'/><title type='text'>w00t! I can keep Hyper-V after all!</title><content type='html'>I can keep Hyper-V after all! How did I get back Client for Microsoft Networking do you ask?&lt;br /&gt;&lt;br /&gt;In my intarweb searchings I came across &lt;a href="http://www.expta.com/2008/03/getting-networking-to-work-in-hyper-v.html"&gt;this&lt;/a&gt; blog entry.&lt;br /&gt;&lt;br /&gt;Here is the solution in a nutshell.&lt;br /&gt;&lt;br /&gt;Blow away any pre-existing virtual NICs with the Hyper-V manager. With your physical NIC go ahead and un-bind all protocols. Once that is done, go back into Hyper-V manager and re-create a virtual NIC and viola, you'll have a virtual NIC with Client for Microsoft Networks bound. Just reboot your machine and it should be good to go.&lt;br /&gt;&lt;br /&gt;Back to using Hyper-V for prototyping! Yay!&lt;br /&gt;&lt;br /&gt;Thanks, Jeff! You rock!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-3539215865572692683?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/3539215865572692683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=3539215865572692683' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3539215865572692683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3539215865572692683'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/w00t-i-can-keep-hyper-v-after-all.html' title='w00t! I can keep Hyper-V after all!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6978288808182062145</id><published>2008-08-07T20:23:00.002-05:00</published><updated>2008-09-04T22:33:47.724-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Correlation'/><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Extraction'/><title type='text'>Coding like Ike and Tina...</title><content type='html'>I'm working on a WebTestRequest that changes an address and in the post back the POST data contains a bunch of information that is displayed in the HTML and I need to extract data from text boxes, check boxes and drop down lists. Extracting the textboxes and checkboxes are pretty simple, it is the drop down boxes that are a pain.&lt;br /&gt;&lt;br /&gt;The .NET HTML parser treats the HtmlTags that reside inside the SELECT tag separately so I cannot just get the attributes for the select and then slice and dice and get the default selected value for the drop in.&lt;br /&gt;&lt;br /&gt;I tried to be "elegant" in my solution and use the HTML Agility Pack and a XPath pulled from FireBug but alas, there was no love to be found. I'm not sure what the exact problem is other than me being a total XPath n00b (I normally use an open source Java project to extract XPaths from XML docs) but something was amiss. After wasting several hours trying to get the elegant solution I decided to be like Ike and Tina and just brute force the solution of identifying the default selected value for the drop down lists. I populate the Context of the WebTestRequest with all the values that are extracted and just reference them by element name in the address change POST ala the HIDDEN1$&lt;blahblahblah&gt; that VS2008 does for hidden field extraction and it seems to work pretty good.&lt;br /&gt;&lt;br /&gt;Here is the extraction code that I wrote to pull the fields that I needed to reference in the Context:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; ExtractCustomerDataToContext(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, ExtractionEventArgs e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; customerData = &lt;span class="kwrd"&gt;new&lt;/span&gt; Dictionary&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt;();&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;string&lt;/span&gt; lastDropDown = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (HtmlTag tag &lt;span class="kwrd"&gt;in&lt;/span&gt; e.Response.HtmlDocument.HtmlTags) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    &lt;span class="kwrd"&gt;string&lt;/span&gt; type = tag.GetAttributeValueAsString(&lt;span class="str"&gt;"type"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (type != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;      lastDropDown = &lt;span class="kwrd"&gt;null&lt;/span&gt; ;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;      &lt;span class="kwrd"&gt;if&lt;/span&gt; (type == &lt;span class="str"&gt;"text"&lt;/span&gt; || type == &lt;span class="str"&gt;"checkbox"&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (tag.GetAttributeValueAsString(&lt;span class="str"&gt;"checked"&lt;/span&gt;) != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (tag.GetAttributeValueAsString(&lt;span class="str"&gt;"checked"&lt;/span&gt;) == &lt;span class="str"&gt;"checked"&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;            customerData.Add(tag.GetAttributeValueAsString(&lt;span class="str"&gt;"name"&lt;/span&gt;), &lt;span class="str"&gt;"on"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;          } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;            customerData.Add(tag.GetAttributeValueAsString(&lt;span class="str"&gt;"name"&lt;/span&gt;), &lt;span class="str"&gt;"off"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;          };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;          customerData.Add(tag.GetAttributeValueAsString(&lt;span class="str"&gt;"name"&lt;/span&gt;), tag.GetAttributeValueAsString(&lt;span class="str"&gt;"value"&lt;/span&gt;));&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;    } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;      &lt;span class="kwrd"&gt;if&lt;/span&gt; (tag.GetAttributeValueAsString(&lt;span class="str"&gt;"class"&lt;/span&gt;) != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (tag.GetAttributeValueAsString(&lt;span class="str"&gt;"class"&lt;/span&gt;) == &lt;span class="str"&gt;"dropDownListDefault"&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;          lastDropDown = tag.GetAttributeValueAsString(&lt;span class="str"&gt;"name"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;      } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (tag.GetAttributeValueAsString(&lt;span class="str"&gt;"selected"&lt;/span&gt;) != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (lastDropDown != &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;            customerData.Add(lastDropDown, tag.GetAttributeValueAsString(&lt;span class="str"&gt;"value"&lt;/span&gt;));&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;          };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;    };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;  };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;  &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (KeyValuePair&amp;lt;&lt;span class="kwrd"&gt;string&lt;/span&gt;, &lt;span class="kwrd"&gt;string&lt;/span&gt;&amp;gt; kvp &lt;span class="kwrd"&gt;in&lt;/span&gt; customerData) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (&lt;span class="kwrd"&gt;this&lt;/span&gt;.Context.ContainsKey(&lt;span class="str"&gt;"CUSTOMER_UPDATE_DATA."&lt;/span&gt; + kvp.Key)) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;      &lt;span class="kwrd"&gt;this&lt;/span&gt;.Context.Remove(&lt;span class="str"&gt;"CUSTOMER_UPDATE_DATA."&lt;/span&gt; + kvp.Key);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;    };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (kvp.Value == &lt;span class="kwrd"&gt;null&lt;/span&gt;) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;      &lt;span class="kwrd"&gt;this&lt;/span&gt;.Context.Add(&lt;span class="str"&gt;"CUSTOMER_UPDATE_DATA."&lt;/span&gt; + kvp.Key, &lt;span class="str"&gt;""&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;    } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;      &lt;span class="kwrd"&gt;this&lt;/span&gt;.Context.Add(&lt;span class="str"&gt;"CUSTOMER_UPDATE_DATA."&lt;/span&gt; + kvp.Key, kvp.Value);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;    };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;  };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;So, if the previous page HTML has a text field by the name of "txtCustomerAccountNumber" I can simply add that information to the post with:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;changeAddressRequestBody.FormPostParameters.Add(&lt;span class="str"&gt;"txtCustomerAccountNumber"&lt;/span&gt;, &lt;span class="kwrd"&gt;this&lt;/span&gt;.Context[&lt;span class="str"&gt;"CUSTOMER_UPDATE_DATA.txtCustomerAccountNumber"&lt;/span&gt;].ToString());&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;As long as the information was displayed via HTML in the prior hit, I should be good to go.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6978288808182062145?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6978288808182062145/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6978288808182062145' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6978288808182062145'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6978288808182062145'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/coding-like-ike-and-tina.html' title='Coding like Ike and Tina...'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8942080699559986258</id><published>2008-08-06T11:10:00.001-05:00</published><updated>2008-09-02T20:47:17.435-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Correlation'/><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><title type='text'>Revisiting CSV Data Binding in VS2008 Coded Web Tests</title><content type='html'>I've been going through the WinDBG labs located here. Tess has done a great job with the labs and done all the hard work, so w00t for her!&lt;br /&gt;&lt;br /&gt;I needed to add another .CSV data file for my main VUser that I am currently working on so I did a copypasta of a pre-existing DataBinding and DataSource. When I first created the coded web test I had added so much custom code that I deleted the web test and left only the coded web test. The fear was that I might accidently re-gen the code from the web test and over write my work. That in and of itself shouldn't be too bad since we all know to use a code repository, right? Of course we do.&lt;br /&gt;&lt;br /&gt;Anyhoo, I made the code changes and was greeted by an error when my file couldn't be loaded. Huh? How rude!&lt;br /&gt;&lt;br /&gt;I finally got it to work, but I wanted to note the way .CSV data binding works in a coded web test in greater detail just to remind myself next time this happens. There are plenty of examples of binding a .CSV file to a web test, but few on a pre-existing coded web test. So, here goes!&lt;br /&gt;&lt;br /&gt;First we have to have a DataSource declaration that takes five parameters for a .CSV file:&lt;br /&gt;&lt;br /&gt;1: dataSourceName&lt;br /&gt;This is the name we are giving the DSN for our .CSV file.&lt;br /&gt;2: providerName&lt;br /&gt;In my case for a .CSV file I am using "Microsoft.VisualStudio.TestTools.DataSource.CSV"&lt;br /&gt;3: connectionString.&lt;br /&gt;For my use, this is the path with escaped back-whacks to the .CSV file. In my case, it is "c:\\data\\streetNames.csv"&lt;br /&gt;4: DataBindingAccessMethod&lt;br /&gt;For my use, I want a random selection of data from the .CSV file so I am using "Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Random"&lt;br /&gt;5: tableName&lt;br /&gt;I thought that I could provide my own table name to reference the .CSV file but that didn't work and was forced to use the form of &lt;csvFileNameWithoutExtension&gt;#csv, like this: "streetNames#csv". When I tried to use the name "streetNameTable" I would get back an error of "cannot find streetTableName.txt" from VS2008. But, the &lt;csvFileNameWithoutExtension&gt;#csv form works, so no biggie.&lt;br /&gt;&lt;br /&gt;Here is what I am using for my entire Data Source:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;  [DataSource(&lt;span class="str"&gt;"streetNameDataSource"&lt;/span&gt;,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;            &lt;span class="str"&gt;"Microsoft.VisualStudio.TestTools.DataSource.CSV"&lt;/span&gt;,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;            &lt;span class="str"&gt;"C:\\Data\\streetNames.csv"&lt;/span&gt;,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;            Microsoft.VisualStudio.TestTools.WebTesting.DataBindingAccessMethod.Random,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;            &lt;span class="str"&gt;"streetNames#csv"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Next, we have to define what column we are pulling from the .CSV file. In my case, it is a single column CSV file but we still have to have a column name defined. For my CSV file I am using the form of:&lt;br /&gt;&lt;br /&gt;8&lt;---------------------------&lt;br /&gt;streetNames&lt;br /&gt;"ABBINGTON"&lt;br /&gt;"ALDRICH"&lt;br /&gt;"ALEXANDER"&lt;br /&gt;"ALICE"&lt;br /&gt;"ALLISON"&lt;br /&gt;"ALMOND"&lt;br /&gt;"ALTA"&lt;br /&gt;"AMHERST"&lt;br /&gt;---------------------------&gt;8&lt;br /&gt;&lt;br /&gt;These are all the street names in Grover's Mill, NJ as reported by &lt;a href="http://www.melissadata.com/lookups/zipstreet.asp"&gt;Melissa Data&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The DataBinding declaration that I am using takes four parameters.&lt;br /&gt;&lt;br /&gt;1: dataSourceName&lt;br /&gt;This is the DSN that we previously defined.&lt;br /&gt;2: tableName&lt;br /&gt;This is the tableName that we defined in the form of &lt;csvFileNameWithoutExtension&gt;#csv.&lt;br /&gt;3: columnName&lt;br /&gt;This is the column name of the CSV that we are extracting. I've found this to be case insensitive.&lt;br /&gt;4: contextVariableName&lt;br /&gt;This is the key name of the entry that will be created in the Context for the iteration.&lt;br /&gt;&lt;br /&gt;Here is what I am using for my DataBinding:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;  [DataBinding(&lt;span class="str"&gt;"streetNameDataSource"&lt;/span&gt;, &lt;span class="str"&gt;"streetNames#csv"&lt;/span&gt;, &lt;span class="str"&gt;"streetNames"&lt;/span&gt;, &lt;span class="str"&gt;"streetNames"&lt;/span&gt;)]&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Now with each iteration of the vuser I can reference the Context for the randomized street name, ala:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;Debug.WriteLine(&lt;span class="str"&gt;"Street name is "&lt;/span&gt; + &lt;span class="kwrd"&gt;this&lt;/span&gt;.Context[&lt;span class="str"&gt;"streetNames"&lt;/span&gt;].ToString());&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Works like a champ!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8942080699559986258?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8942080699559986258/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8942080699559986258' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8942080699559986258'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8942080699559986258'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/revisiting-csv-data-binding-in-vs2008.html' title='Revisiting CSV Data Binding in VS2008 Coded Web Tests'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-3514082444044481798</id><published>2008-08-02T02:06:00.001-05:00</published><updated>2008-09-02T20:48:59.559-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GC'/><category scheme='http://www.blogger.com/atom/ns#' term='WinDBG'/><category scheme='http://www.blogger.com/atom/ns#' term='Memory'/><title type='text'>Tasty memory! Om Nom Nom!</title><content type='html'>So, I created a scenario and had 45 virtual users doing nothing but browsing the application that I am testing. After one hour I noticed that the w3wp private bytes are running around 1.5 gigs of memory. That's right, just browsing. I was hitting it at a rate about 26 hits per second and running about 90% CPU utilization and 1.5 gigs of private bytes being consumed with almost 700 megs of hit in Gen 0.&lt;br /&gt;&lt;br /&gt;Yeah, something ain't right.&lt;br /&gt;&lt;br /&gt;But, that gives me an excuse to brush up on my WinDBG skills. I fired up adplus.vbs and took a -hang dump that resulted in a 1.5 gig dump. Woo! So tonight I've been re-learning the art of poking around in a dump file with a debugger.&lt;br /&gt;&lt;br /&gt;Along the way I found a most excellent blog for a MS dudette in Sweden that really knows her stuff and has some great tutorials for debuggery: &lt;br /&gt;&lt;br /&gt;http://blogs.msdn.com/tess/default.aspx&lt;br /&gt;&lt;br /&gt;Check out the most excellent labs located &lt;a href="http://blogs.msdn.com/tess/pages/net-debugging-demos-information-and-setup-instructions.aspx"&gt;here&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-3514082444044481798?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/3514082444044481798/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=3514082444044481798' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3514082444044481798'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3514082444044481798'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/tasty-memory-om-nom-nom.html' title='Tasty memory! Om Nom Nom!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-3000182724549036016</id><published>2008-08-01T11:45:00.001-05:00</published><updated>2008-09-02T20:49:20.992-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Thinktime'/><title type='text'>Let's be deviant with our think times!</title><content type='html'>I mentioned in another post that I stopped using the WebTestRequest.ThinkTime as the think time is reported in the Load Test Repository and that annoys me, especially because the default option for think time is for the MS tools to use a normally distributed variable think time on the time requested.&lt;br /&gt;&lt;br /&gt;Now, I think the normally distributed think time is awesome to help prevent VUsers from getting into a lock step situation and most of the time we want the VUsers to be in route step.&lt;br /&gt;&lt;br /&gt;I created a ThinkTime routine the other day that just used a slightly randomized think time but of course, it was not normally distributed and I really like the idea of a normally distributed think time. So, off to the Internets and I found a simple to implement algorithm by the name of Box-Muller that I was able to use for my VUsers think times.&lt;br /&gt;&lt;br /&gt;Below is a graph with an example of a 5 second think time with multiple values of deviation, s.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp1.blogger.com/_qsa4nRIPmPo/SJM_ucZz3CI/AAAAAAAAAJE/fPyimqTFeao/s1600-h/Box_Muller_ThinkTime.jpg"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;" src="http://bp1.blogger.com/_qsa4nRIPmPo/SJM_ucZz3CI/AAAAAAAAAJE/fPyimqTFeao/s320/Box_Muller_ThinkTime.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5229593659438259234" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Below is the main code that I used to write the class to be invoked from inside the coded web test and of course, think time must be invoked outside of any named transactions otherwise you end up in the same situation before with variable think time being reported in the transaction time.&lt;br /&gt;&lt;br /&gt;Because of my slackard like nature, I went ahead and made all the methods static so that I don't have in instantiate any objects for later referencing. &lt;br /&gt;&lt;br /&gt;I simply call the code like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;      &lt;span class="kwrd"&gt;string&lt;/span&gt; transactionName = &lt;span class="str"&gt;"Login.PageHit"&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;      &lt;span class="kwrd"&gt;this&lt;/span&gt;.BeginTransaction(transactionName);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;      WebTestRequest loginPageHit = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebTestRequest(&lt;span class="str"&gt;"http://"&lt;/span&gt; + targetWebServer.ToString() + &lt;span class="str"&gt;"/SomeWebApp/login.aspx"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;      ExtractHiddenFields loginExtractionRule = &lt;span class="kwrd"&gt;new&lt;/span&gt; ExtractHiddenFields();&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      loginExtractionRule.Required = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;      loginExtractionRule.HtmlDecode = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;      loginExtractionRule.ContextParameterName = &lt;span class="str"&gt;"1"&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;      loginPageHit.ExtractValues += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;ExtractionEventArgs&amp;gt;(loginExtractionRule.Extract);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;      &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; loginPageHit;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;      loginPageHit = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;      &lt;span class="kwrd"&gt;this&lt;/span&gt;.EndTransaction(transactionName);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;      &lt;span class="preproc"&gt;#if&lt;/span&gt; THINKTIME&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;        LoadTestThinkTime.ThinkTime(5, 0.1);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;      &lt;span class="preproc"&gt;#endif&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Below is the actual code that I wrote:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;using&lt;/span&gt; System.Threading;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;namespace&lt;/span&gt; LoadTestThinkTime {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;  &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; LoadTestThinkTime {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    &lt;span class="rem"&gt;/// This routine will generate a randomized variable that is in a standard distribution with a deviation of 0&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;    &lt;span class="rem"&gt;/// ± s/2 for given s and then invoke Thread.Sleep for the given amount of seconds converted to milliSeconds.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;param name="secondsToSleep"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;param name="s"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; RandomizedSleep(&lt;span class="kwrd"&gt;int&lt;/span&gt; secondsToSleep, &lt;span class="kwrd"&gt;double&lt;/span&gt; s) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;      &lt;span class="kwrd"&gt;if&lt;/span&gt; (s &amp;gt; 0) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;        &lt;span class="kwrd"&gt;if&lt;/span&gt; (s &amp;lt;= 1.5) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;          &lt;span class="kwrd"&gt;if&lt;/span&gt; (secondsToSleep &amp;gt; 0) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;            &lt;span class="kwrd"&gt;if&lt;/span&gt; (secondsToSleep &amp;lt; (&lt;span class="kwrd"&gt;int&lt;/span&gt;.MaxValue / 1000)) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;              &lt;span class="kwrd"&gt;double&lt;/span&gt; u  = 0;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;              &lt;span class="kwrd"&gt;double&lt;/span&gt; v  = 0;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;              &lt;span class="kwrd"&gt;double&lt;/span&gt; w  = 0;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;              &lt;span class="kwrd"&gt;double&lt;/span&gt; z0 = 0;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;              &lt;span class="kwrd"&gt;double&lt;/span&gt; z1 = 0;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;              Random randomizer = &lt;span class="kwrd"&gt;new&lt;/span&gt; Random();&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;              &lt;span class="kwrd"&gt;do&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;                u = 2.0 * randomizer.NextDouble() - 1.0;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;                v = 2.0 * randomizer.NextDouble() - 1.0;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;                w = u * u + v * v;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;              } &lt;span class="kwrd"&gt;while&lt;/span&gt; (w &amp;gt;= 1.0);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;              w = Math.Sqrt(((-2.0 * Math.Log(w)) / w));&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;              z0 = u * w;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;              z1 = v * w; &lt;span class="rem"&gt;/* I don't use z1, but it is part of the original algorithm so here it is. */&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;              &lt;span class="rem"&gt;/* When using small time values, i.e, 1 second, large values of s can generate negative values. In that&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;&lt;span class="rem"&gt;               * situation go ahead and take the absolute value and invoke the call with that to prevent errors during&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;&lt;span class="rem"&gt;               * loadtests. */&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;              &lt;span class="kwrd"&gt;int&lt;/span&gt; milliSecondSleepTime = (&lt;span class="kwrd"&gt;int&lt;/span&gt;)((secondsToSleep + z0 * s) * 1000.0);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;              &lt;span class="kwrd"&gt;if&lt;/span&gt; (milliSecondSleepTime &amp;lt; 0) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;                milliSecondSleepTime = Math.Abs(milliSecondSleepTime);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;              };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;              Thread.Sleep(milliSecondSleepTime);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;            } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;              &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Exception(&lt;span class="str"&gt;"LoadTestThinkTime: Maximum sleep time is "&lt;/span&gt; + ((&lt;span class="kwrd"&gt;int&lt;/span&gt;)&lt;span class="kwrd"&gt;int&lt;/span&gt;.MaxValue / 1000).ToString() + &lt;span class="str"&gt;" seconds."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;            };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;          } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;            &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Exception(&lt;span class="str"&gt;"LoadTestThinkTime: Sleep time cannot be negative."&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;          };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;        } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;          &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Exception(&lt;span class="str"&gt;"LoadTestThinkTime: Deviation cannot be larger than 1.5"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;        };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;      } &lt;span class="kwrd"&gt;else&lt;/span&gt; {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;        &lt;span class="kwrd"&gt;throw&lt;/span&gt; &lt;span class="kwrd"&gt;new&lt;/span&gt; Exception(&lt;span class="str"&gt;"LoadTestThinkTime: Deviation cannot be negative"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;      }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;    } &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;    &lt;span class="rem"&gt;/// Public method to invoke variable sleep time with a given deviation of s where s is in the domain of [0, 1.5].&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;param name="secondsToSleep"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;param name="s"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ThinkTime(&lt;span class="kwrd"&gt;int&lt;/span&gt; secondsToSleep, &lt;span class="kwrd"&gt;double&lt;/span&gt; s) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;      RandomizedSleep(secondsToSleep, s);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;    &lt;span class="rem"&gt;/// Public method to invoke variable sleep time with a default deviation of 0.25.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;/summary&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;    &lt;span class="rem"&gt;/// &amp;lt;param name="secondsToSleep"&amp;gt;&amp;lt;/param&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;    &lt;span class="kwrd"&gt;static&lt;/span&gt; &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; ThinkTime(&lt;span class="kwrd"&gt;int&lt;/span&gt; secondsToSleep) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;      RandomizedSleep(secondsToSleep, 0.25);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;  }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-3000182724549036016?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/3000182724549036016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=3000182724549036016' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3000182724549036016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3000182724549036016'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/08/lets-be-deviant-with-our-think-times.html' title='Let&apos;s be deviant with our think times!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp1.blogger.com/_qsa4nRIPmPo/SJM_ucZz3CI/AAAAAAAAAJE/fPyimqTFeao/s72-c/Box_Muller_ThinkTime.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-7175211656336020032</id><published>2008-07-29T22:33:00.002-05:00</published><updated>2008-09-02T20:49:54.421-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><title type='text'>Extracting Transaction Response Time by Transaction Name per Load Test Run</title><content type='html'>Despite being annoyed that think time is included in the VS2008 web test response time I would like to be able to graph the response times for a given transaction name. I load my Load Test History database by the unique GUID and forgo the integer test ID to prevent possible duplicates in the future.&lt;br /&gt;&lt;br /&gt;I created a query that takes transaction name and test GUID to pull all the transaction times.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; LoadTestTransactionDetail.&lt;span class="kwrd"&gt;TimeStamp&lt;/span&gt;, LoadTestTransactionDetail.ElapsedTime&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;FROM&lt;/span&gt;  LoadTest.dbo.LoadTestRun &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;               LoadTest.dbo.WebLoadTestTransaction &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTestRun.LoadTestRunId = WebLoadTestTransaction.LoadTestRunId &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;               LoadTest.dbo.LoadTestTransactionDetail &lt;span class="kwrd"&gt;ON&lt;/span&gt; WebLoadTestTransaction.TransactionId = LoadTestTransactionDetail.TransactionId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;               LoadTest.dbo.LoadTestRun.LoadTestRunId = LoadTestTransactionDetail.LoadTestRunId&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt; (WebLoadTestTransaction.TransactionName = &lt;span class="str"&gt;'Some_Transaction_Name'&lt;/span&gt;) &lt;span class="kwrd"&gt;AND&lt;/span&gt; (LoadTestRun.RunId = &lt;span class="str"&gt;'987cce52-7608-40a7-ac85-96714af1ac6d'&lt;/span&gt;)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; LoadTestTransactionDetail.&lt;span class="kwrd"&gt;TimeStamp&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There we go! All the transaction times for "Some_Transaction_Name:"&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;2008-07-29 15:58:04.467    7.944&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;2008-07-29 15:58:37.633    6.138&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;2008-07-29 15:58:55.930    5.243&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;2008-07-29 15:59:04.977    4.615&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;2008-07-29 15:59:25.607    6.461&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;2008-07-29 15:59:36.493    5.026&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;2008-07-29 15:59:40.820    5.256&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;2008-07-29 16:00:00.507    5.573&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;2008-07-29 16:00:04.940    5.457&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;2008-07-29 16:00:08.977    5.577&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;2008-07-29 16:00:16.547    5.506&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;2008-07-29 16:00:34.303    5.758&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-7175211656336020032?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/7175211656336020032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=7175211656336020032' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7175211656336020032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/7175211656336020032'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/extracting-transaction-response-time-by.html' title='Extracting Transaction Response Time by Transaction Name per Load Test Run'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-600391832891810582</id><published>2008-07-29T20:51:00.001-05:00</published><updated>2008-09-02T20:50:13.180-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><category scheme='http://www.blogger.com/atom/ns#' term='Thinktime'/><title type='text'>Uh-Oh. This isn't good.</title><content type='html'>I found out something today that I really don't like with VS2008 for load testing. I created my virtual user and setup think time ala WebTestRequest.ThinkTime with reasonable think times. After the testing was done I moved the results over to my LoadTestHistory database with a SP that I wrote about earlier in my blog. When I examined the results it turned out that the think time was included in the transaction response time. Nope, I don't like that. I dislike that about as much as I don't like having a failed counter for transactions.&lt;br /&gt;&lt;br /&gt;I guess I can't be really surprised as the Begin/End transaction is wrapped around the WebTestRequest object and the think time is handled by the WebTestRequest object and there is no communication between the transaction begin/end and the WTR object (AFAIK).&lt;br /&gt;&lt;br /&gt;I looked in the schema for the Load Test Repository and didn't see any fields where response time alone (without think time) would be recorded but there is definitely response time without sleep time recorded during the execution of the VS2008 loadtest. I figured there would be a field in the database with this value but I didn't see it.&lt;br /&gt;&lt;br /&gt;Luckily, though, this is pretty simple to get around. If you aren't going to be using variable think times you can simple perform a Thread.Sleep(new TimeSpan(0, 0, 0, 5, 0)) which is similar to the LoadRunner idea behind lr_think_time(). Don't forget to add the "using System.Threading;" to your code to reference the Thread object since it is not referenced by default.&lt;br /&gt;&lt;br /&gt;If you want to use variable think time then you'll have to come up with your own mechanism for varying the randomness of the think time. It's not that difficult, it is just a pain.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-600391832891810582?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/600391832891810582/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=600391832891810582' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/600391832891810582'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/600391832891810582'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/uh-oh-this-isnt-good.html' title='Uh-Oh. This isn&apos;t good.'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6926510641222513109</id><published>2008-07-28T12:19:00.000-05:00</published><updated>2008-08-07T23:00:19.019-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><title type='text'>Clearing cookies in VS2008</title><content type='html'>In a coded web test that I am working on I have a need to clear all pre-existing cookies for a login page. I don't want the page identifying my vuser with a cookie that is assigned later on in the page hits.&lt;br /&gt;&lt;br /&gt;While VS2008 has a CookieContainer that is a container for cookies, there is not a .Clear() method to clear out all the cookies like can be done with LoadRunner with the web_cleanup_cookies() API call.&lt;br /&gt;&lt;br /&gt;I have found the way to do this is to use assign a new CookieContainer during the PreWebTest event to clear out all pre-existing cookies like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;public&lt;/span&gt; Some_Coded_Web_Test() {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  &lt;span class="kwrd"&gt;this&lt;/span&gt;.PreAuthenticate = &lt;span class="kwrd"&gt;true&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;this&lt;/span&gt;.PreWebTest += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;PreWebTestEventArgs&amp;gt;(ClearCookies);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; ClearCookies(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, PreWebTestEventArgs e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;  Debug.WriteLine(&lt;span class="str"&gt;"Ha!Ha! I am clearing cookies!"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;  &lt;span class="kwrd"&gt;this&lt;/span&gt;.Context.CookieContainer = &lt;span class="kwrd"&gt;new&lt;/span&gt; System.Net.CookieContainer();&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;No more tasty, tasty cookies on script iterations.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6926510641222513109?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6926510641222513109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6926510641222513109' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6926510641222513109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6926510641222513109'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/clearing-cookies-in-vs2008.html' title='Clearing cookies in VS2008'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-1249286471425478254</id><published>2008-07-27T23:36:00.001-05:00</published><updated>2008-08-07T23:05:54.306-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hyper-V'/><title type='text'>Thinkin' about ditching Hyper-V</title><content type='html'>Seriously thinking about ditching Hyper-V for it's smaller brother, Virtual PC 2007. Sure, Virtual PC doesn't give the VM as much horsepower or as much contact with the metal of the host machine but by golly gosh the networking doesn't try to kill off Client for Microsoft Network.&lt;br /&gt;&lt;br /&gt;I'm sure Hyper-V works great in the intended role of hosting guest VMs while racked in a server room someplace but the network issue that I am having is really irking me. Perhaps Hyper-V isn't ready for the type of prototyping work I want to do and Virtual PC 2007 will gimp along for my prototyping work.&lt;br /&gt;&lt;br /&gt;Of course, the only issue that I have so far is that I have four VMs created and I'd have to go through all that trouble of reloading the OS. Well, I'm just about done with my load balancing prototyping so blowing away the VMs and removing the Hyper-V role wouldn't be that painful...&lt;br /&gt;&lt;br /&gt;Of course, the biggest thing that I'd miss is the snapshot feature of Hyper-V. For prototyping and messin' around with a VM that is the greatest thing since sliced bread. I don't suspect we'll see that in a free Virtual PC anytime soon...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-1249286471425478254?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/1249286471425478254/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=1249286471425478254' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1249286471425478254'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1249286471425478254'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/thinkin-about-ditching-hyper-v.html' title='Thinkin&apos; about ditching Hyper-V'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-1875562185625865046</id><published>2008-07-26T00:04:00.000-05:00</published><updated>2008-08-07T23:06:15.054-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hyper-V'/><title type='text'>Hyper-V Networking Problems</title><content type='html'>I encountered some "interesting" situations with Hyper-V networking last night. For some reason I've lost the ability to connect to network shares on my machine, not good. I poke around and find for some reason that "Client for Microsoft Networks" is disabled on the Hyper-V virtual NIC. WTF?&lt;br /&gt;&lt;br /&gt;I try to re-enable it and get the strange error message of "Your current selection will also disable the following features: Client for Microsoft Networks"&lt;br /&gt;&lt;br /&gt;Uh, no? I'm trying to re-enable.&lt;br /&gt;&lt;br /&gt;It appears that I am not the only person that has had this issue:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://sqlsolace.blogspot.com/2008_04_01_archive.html"&gt;http://sqlsolace.blogspot.com/2008_04_01_archive.html&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I disabled the Hyper-V virtual NIC and re-established my physical NIC and am able to truck on down the road. I find that if I delete the Hyper-V virtual NIC (external network connection) and create an internal virtual network (no physical connection) that the internal network has Client for Microsoft Network without issue. It's only the external virtual connections that are bjorked.&lt;br /&gt;&lt;br /&gt;In the blog post above they said that if they un-installed all protocols and clients, reboot, and then re-installed that it was a work around. What a PITA. I haven't tried that yet as I got my Ubuntu 8.04 TLS installation up and running with HAProxy load balancing to two Win2k3 VMs which for prototyping works just fine on an internal virtual network just fine.&lt;br /&gt;&lt;br /&gt;I like HAProxy. It is pretty full featured for a free, open source L7 load balance tool. It has a lot of features that remind me of NetScaler boxes (HAProxy even has a FreeBSD port so you too can run your balancer under FreeBSD just like NetScaler, 'cept of course it isn't NetSclaer).&lt;br /&gt;&lt;br /&gt;I fired up my Win2k3 running NLB and the NLB manager shows both client machines to be up and running but it will only direct traffic to one of the Win2k3 VMs. Nice. And even niftier, if I ping the NLB cluster (in my case, it's 192.168.0.100) I will get back dupe pings as NLB creates the 192.168.0.100 VIP on both the NLB machine and all the client machines. Yeah, NLB is over kill for the stuff I want to do and HAProxy has nifty persistence support via cookies since it is a L7 balancer. &lt;br /&gt;&lt;br /&gt;The only issue is that IIS logs the IP address of the HAProxy machine and not the actual client IP address. The HAProxy, much like the NetScaler product, sends the true IP in an X HTTP request header but you must use an ISAPI filter with IIS to log the true IP address to the IIS log. Apache has built in support for logging X-Header entries to the apache log. That is kinda handy but we aren't using apache. I've found some sample Visual C++ 6.0 ISAPI code for handling this task. Heck, I don't even have Visual Studio 6.0 installed or even Visual C++ 2008 installed on my machine.&lt;br /&gt;&lt;br /&gt;Meh, not going to worry about it right now. Just suffice it to say that Hyper-V networking is irking me and NLB is ooky. HAProxy is pretty nifty and didn't take long to get a sample balancer up and running.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-1875562185625865046?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/1875562185625865046/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=1875562185625865046' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1875562185625865046'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/1875562185625865046'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/hyper-v-networking-problems.html' title='Hyper-V Networking Problems'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-2377560449486038978</id><published>2008-07-23T21:34:00.000-05:00</published><updated>2008-07-23T21:47:05.096-05:00</updated><title type='text'>Getting back to load testing</title><content type='html'>I actually got to do some load testing today! Heck, I needed to figure out how many combinations for a test and even got to whip out some discrete math! Woooooo! Look at me go!&lt;br /&gt;&lt;br /&gt;One of the things that I like about load testing is the "WTF?" factor. Finding those things that could never be found in "onesie-twosie" testing. In my current testing I am using JMeter to chunk 1500 e-mails and FTPs through a file routing system and I found that after the xfer is done that the w3wp.exe process would go high, spike a processor and the large object heap of the w3wp would go high. On top of that, during the same period where the w3wp goes whizzing off into la-la land the private byte usage of SQL Server climbs into the exosphere. We don't know what the problem is just yet but I like weeding stuff like that out with the initial raise of the eyebrow and a muffled "WTF?" Something tells me that I'll be brushing off my memory dump skills and using Son of Strike to figure out what is being dumped into the LOH. &lt;br /&gt;&lt;br /&gt;I've been looking at load balancers and in my Google journeys I came across HAProxy and it seems to be quite a piece of art and an industrial load balancing solution. It runs natively under Linux but there is a FreeBSD version out in the ports tree. I haven't done anything with it yet as I've been busy with other tasks but I want to give it a test drive. It should be well more than what we need for the task at hand. 9 GB/s throughput? Yeah. We're never gonna see that. :-)&lt;br /&gt;&lt;br /&gt;http://haproxy.1wt.eu/&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-2377560449486038978?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/2377560449486038978/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=2377560449486038978' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2377560449486038978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/2377560449486038978'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/getting-back-to-load-testing.html' title='Getting back to load testing'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6744560368232419978</id><published>2008-07-21T21:58:00.001-05:00</published><updated>2008-08-07T23:06:45.608-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Load Balancing'/><title type='text'>NLB is ooky</title><content type='html'>Am I the only one that gets an oooky feeling from Microsoft's Network Load Balancer? Seems to be a little bit of over kill for just port 80 stuff. I built a virtual network in Hyper-V with three Win2k3 installations and played around with getting NLB up and running.&lt;br /&gt;&lt;br /&gt;Sure, it gets up and running pretty easily but boy howdy if you ever want to make any changes like changing the NICs from a 10.0.0.0/8 space to 192.168.0.0/16 space. Yikes! Errors a plenty and that is after breaking down the cluster and deleting the cluster and then un-installing NLB from the target NICs and the balancer installation only to rebuild. Helllooo "unable to bind" errors!&lt;br /&gt;&lt;br /&gt;A bit overkill for what I'm doing. I'm thinking maybe a nice balancer like Virtual Linux Server project or something similar would get the job done. Or perhaps Pure Load Balance (runs under FreeBSD, w00t!).&lt;br /&gt;&lt;br /&gt;Maybe I just mucked up something in the process and need to play around more (this is the most likely scenario). Thank goodness for Hyper-V snapshots. They really save the day when prototyping like I have been doing.&lt;br /&gt;&lt;br /&gt;I did run into an interesting situation. I created the three above mentioned VMs and Win2k3 didn't have a driver for my notebooks gigabit NIC. For some reason it took me a while to realize I could add a legacy NIC that Win2k3 could see.&lt;br /&gt;&lt;br /&gt;I've enjoyed showing off the snapshot functionality by writing one line command lines that delete every file off the file system and then restoring with the snapshot and seeing the machine up and running 10 seconds later. Greatness for prototyping and fooling around!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6744560368232419978?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6744560368232419978/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6744560368232419978' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6744560368232419978'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6744560368232419978'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/nlb-is-ooky.html' title='NLB is ooky'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6638806740449221356</id><published>2008-07-19T03:56:00.000-05:00</published><updated>2008-08-07T23:07:28.493-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Portable Server'/><title type='text'>Portable Server Drive Performance</title><content type='html'>I was wondering what the performance of the RAID5 setup in my new portable server and I found this nifty little HD benchmark tool at http://www.datamarck.com/.&lt;br /&gt;&lt;br /&gt;I ran it and it showed that the average throughput of my RAID5 array was about 77 MB/s. In contrast, my older desktop machine ran about 46 MB/s and my older laptop was a measly 36 MB/s. Not too shabby.&lt;br /&gt;&lt;br /&gt;I haven't done any video or CPU benchmarking yet but I wonder how the video performs with Win2k8 server and the nVidia 8800M single card.&lt;br /&gt;&lt;br /&gt;I'm currently installing two instances of Win2k3 standard under Hyper-V to play around with xfering IIS metabase and some other automated tasks and the beauty of this is that I can take snapshots and restore those snapshots while playing around.&lt;br /&gt;&lt;br /&gt;But I did find a bummer about Hyper-V. You can bind it to a NIC as long as it is not a wireless NIC. I found that rather strange that there would be a differentiation of interfaces as both are ethernet devices. Go figure.&lt;br /&gt;&lt;br /&gt;Another thing I noticed is that the network card is reported by Win2k8 as a 10 GB/s card which I find hard to believe. It's prolly just a reporting err. No biggie as I don't have 10 GB infrastructure in the house.&lt;br /&gt;&lt;br /&gt;All in all, pretty nifty for a portable device.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6638806740449221356?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6638806740449221356/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6638806740449221356' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6638806740449221356'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6638806740449221356'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/portable-server-drive-performance.html' title='Portable Server Drive Performance'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5239986454350364240</id><published>2008-07-17T22:15:00.001-05:00</published><updated>2008-08-07T23:07:41.712-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Hyper-V'/><title type='text'>Hyper-V is nifty!</title><content type='html'>I haven't had much of a chance to play with Hyper-V but I did a little bit of fiddling tonight. I loaded up a VM with the latest Ubuntu and got it up and running and all is well with the world.&lt;br /&gt;&lt;br /&gt;I made a snapshot and then did the classic n00b mistake of:&lt;br /&gt;&lt;br /&gt;`cd / &amp;&amp; rm -rf *`&lt;br /&gt;&lt;br /&gt;Yeah. Not a good thing to do and yes, it destroyed the installation as expected.&lt;br /&gt;&lt;br /&gt;I applied the snapshot that I had taken a few minutes before and within a few seconds my Ubuntu was up and running exactly the way it was when I took the snapshot. How stinkin' cool is that?&lt;br /&gt;&lt;br /&gt;I have a need to build out an installation process that builds installs and configures a Win2k3 machine with users, applications, IIS, vdirs, etc and this will be the perfect environment to test. Build up a base machine, get it configured and snap shot.&lt;br /&gt;&lt;br /&gt;Then I can try the process and tweak as much as a I need by restoring the snapshot. Too stinkin' cool!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5239986454350364240?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5239986454350364240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5239986454350364240' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5239986454350364240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5239986454350364240'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/hyper-v-is-nifty.html' title='Hyper-V is nifty!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5698745559892975818</id><published>2008-07-16T00:10:00.000-05:00</published><updated>2008-08-07T23:08:19.243-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Portable Server'/><title type='text'>The Beast!</title><content type='html'>I got my new laptop in today. It is a rebadged Clevo 901 sporting a Quad Xeon 2.83 GHz proc, RAID5 with 3 200 gig spindles at 7200 RPM and 4 gig of RAM. I can upgrade to 8 gig of RAM when the memory becomes cheap enough. It also has a nVidia 8800M video card.&lt;br /&gt;&lt;br /&gt;The sucker is huge with a 17 inch wide screen display (1680x1050) and is almost three inches thick. It's definately not a laptop but a desktop replacement. In my case, it is a mobile server.&lt;br /&gt;&lt;br /&gt;I was able to order it without an OS as I can get a license through my employer. Today I loaded up Win2k8 64-bit Standard (no need for Enterprise as I am not clustering my "laptop." I also loaded SQL Server 2005 64-bit and then VS2008 Team Suite Testing Edition. On top of that I threw in Office 2007 and spent a lot of the day twiddling with installations and settings.&lt;br /&gt;&lt;br /&gt;After using the Win2k8 interface for a while it seems that I am going to just have to bite the bullet and get used to the Vista UI as Win2k8 uses it as well. *sigh* It was good knowing ya, XP Pro.&lt;br /&gt;&lt;br /&gt;But, for what it's worth, Win2k8 did seem pretty damn snappy. Of course, it might have something to do with the quad core Xeon and 4 gig of RAM and a 512 meg video card. :)&lt;br /&gt;&lt;br /&gt;I also enabled IIS and Hyper-V after I enabled VMM and NX in the machine's BIOS configuration. I'm stoked about Hyper-V but I've already found out one bummer with Hyper-V and that is enabling the Hyper-V role disables the ability to hibernate the machine. I guess I can understand why. If you are hosting a bunch of VMs with Hyper-V you really don't need the ability to hibernate the server. So, it's a minor irritation that but is all.&lt;br /&gt;&lt;br /&gt;I did enable some eye-candy such as smoothing fonts and thumbnails instead of icons. That is one Vista thing that I really liked. That and the use of c:\users instead of the iritating "documents and settings." If you are a keyboard commando like me, those spaces get to be damned annoying. Of course, Microsoft still uses spaces in a lot of their subdirs so the issue isn't totally settled.&lt;br /&gt;&lt;br /&gt;And I learned something from my previous Vista Ultimate experience on my older laptop (15.4 inch more conventional laptop, not a UberPowah machine like my current big rig) and that is to avoid the Microsoft Unix tools and just run with the GNU Unix Tools for Win32. I found a lot of unix commands lacking with the MS implementation and it is easier to unzip the GNU tools and add the subdir to the path. Of course, I can't compile any X code like I supposedly could with the MS unix services but I didn't need to compile any anyway so it is no great loss.&lt;br /&gt;&lt;br /&gt;Another side effect is that I had to buy a new laptop bag and this time instead of a backpack I went with a Swiss Army gear rolling laptop bag that let's me put both my new MobileServer and Acer Ferrari in the bag at the same time and roll it along. With both machines and accessories (power bricks, extra battery, typical junque) that bag feels like it weighs in at 45 - 50 pounds. That would have been a bit tough to carry around an airport. I spent more than I wanted to but I think it'll be worth it.&lt;br /&gt;&lt;br /&gt;On other good news fronts last week work gave me two physical boxen to actually build up my LT environment and I think that I'll actually be doing some dry runs tomorrow measuring up initial testing capacity with JMeter testing against FTP chunking my GPG encrypted files for the service to munch on. Finally!&lt;br /&gt;&lt;br /&gt;I've been doing a lot of hopping around at work but my goal is still the same, to get daily automagic load tests running and in place with our continuous integration work flow.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5698745559892975818?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5698745559892975818/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5698745559892975818' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5698745559892975818'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5698745559892975818'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/beast.html' title='The Beast!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-377528533458788338</id><published>2008-07-07T09:55:00.001-05:00</published><updated>2008-09-02T20:50:38.430-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Statistics'/><title type='text'>Transaction Transaction, what's your action?</title><content type='html'>I noticed something this morning as I was going over my SP that I wrote on Thursday night. I realized that I needed to also gonkulate how many transactions had failed. Seems simple enough, right? If a page (or dependent request) fails inside a transaction the whole transaction gets marked as a failure, right? That's the way that LoadRunner handles the transaction issue.&lt;br /&gt;&lt;br /&gt;In LoadRunner I would normally leave the determination of the status of the transaction by using LR_AUTO. There were a few times that I would explicitly call EndTransaction with a LR_FAIL.&lt;br /&gt;&lt;br /&gt;I realized looking over the schema of the Load Test Repository Store that there does not appear to be any logging of failures on the transaction level. The page level? Sure. Not a problem. LoadTestMessage table contains those goodies for us but I cannot find a way to link LoadTestMessage with WebLoadTestRequestMap. Going back over some of the online documentation for running VS2008 load tests it appears that the tool itself does not count transaction failures and only counts failed pages.&lt;br /&gt;&lt;br /&gt;Yeah... I'm not too happy about that. With my analysis I want to see what transactions failed as I might have multiple page hits to a single transaction. Hopefully this gets fixed in a future version of the MS LT tool.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-377528533458788338?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/377528533458788338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=377528533458788338' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/377528533458788338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/377528533458788338'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/transaction-transaction-whats-your.html' title='Transaction Transaction, what&apos;s your action?'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6443999260650650731</id><published>2008-07-04T11:21:00.001-05:00</published><updated>2008-09-02T20:51:05.042-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='SQLServer 2005'/><title type='text'>Collecting daily LT Metrics</title><content type='html'>Last night I wrote a stored procedure to automagically collect the latest LT results and insert the values into a database that I created on my local SQL Server 2005 Express.&lt;br /&gt;&lt;br /&gt;This SP finds the latest LT that has been run, crunches the numbers and inserts them into two tables, LoadTestHistory and LoadTestHistoryDetails. I used the GUID generated by VS2008 in the Load Test Repository Store as the primary/foreign key between the two tables.&lt;br /&gt;&lt;br /&gt;It's a bit rough and could probably use some more polish:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;set&lt;/span&gt; ANSI_NULLS &lt;span class="kwrd"&gt;ON&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;set&lt;/span&gt; QUOTED_IDENTIFIER &lt;span class="kwrd"&gt;ON&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&lt;span class="kwrd"&gt;GO&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;ALTER&lt;/span&gt; &lt;span class="kwrd"&gt;PROCEDURE&lt;/span&gt; [dbo].[UpdateLoadTestHistory]&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="kwrd"&gt;AS&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;BEGIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;  &lt;span class="kwrd"&gt;SET&lt;/span&gt; NOCOUNT &lt;span class="kwrd"&gt;ON&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;  &lt;span class="kwrd"&gt;declare&lt;/span&gt; @AlreadyExists    &lt;span class="kwrd"&gt;as&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;  &lt;span class="kwrd"&gt;declare&lt;/span&gt; @latestLoadTest   &lt;span class="kwrd"&gt;as&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;  &lt;span class="kwrd"&gt;declare&lt;/span&gt; @loadTestGUID     &lt;span class="kwrd"&gt;as&lt;/span&gt; nvarchar(36)&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;  &lt;span class="kwrd"&gt;declare&lt;/span&gt; @StartTime        &lt;span class="kwrd"&gt;as&lt;/span&gt; datetime&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;  &lt;span class="kwrd"&gt;declare&lt;/span&gt; @EndTime          &lt;span class="kwrd"&gt;as&lt;/span&gt; datetime&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;  &lt;span class="kwrd"&gt;declare&lt;/span&gt; @Duration         &lt;span class="kwrd"&gt;as&lt;/span&gt; &lt;span class="kwrd"&gt;int&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;  &lt;span class="kwrd"&gt;select&lt;/span&gt; @latestLoadTest = &lt;span class="kwrd"&gt;max&lt;/span&gt;(LoadTestRunId)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;  &lt;span class="kwrd"&gt;from&lt;/span&gt; LoadTest.dbo.LoadTestRun&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;  &lt;span class="kwrd"&gt;where&lt;/span&gt; EndTime &lt;span class="kwrd"&gt;is&lt;/span&gt; &lt;span class="kwrd"&gt;not&lt;/span&gt; &lt;span class="kwrd"&gt;null&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;  &lt;span class="kwrd"&gt;select&lt;/span&gt; @loadTestGUID   = RunId,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;         @StartTime      = StartTime,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;         @EndTime        = EndTime,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  25:  &lt;/span&gt;         @Duration       = RunDuration&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  26:  &lt;/span&gt;  &lt;span class="kwrd"&gt;from&lt;/span&gt; LoadTest.dbo.LoadTestRun&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  27:  &lt;/span&gt;  &lt;span class="kwrd"&gt;where&lt;/span&gt; LoadTestRunId = @latestLoadTest&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  28:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  29:  &lt;/span&gt;  &lt;span class="kwrd"&gt;select&lt;/span&gt; @AlreadyExists = &lt;span class="kwrd"&gt;count&lt;/span&gt;(*)&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  30:  &lt;/span&gt;  &lt;span class="kwrd"&gt;from&lt;/span&gt; dbo.LoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  31:  &lt;/span&gt;  &lt;span class="kwrd"&gt;where&lt;/span&gt; LoadTestGUID = @loadTestGUID&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  32:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  33:  &lt;/span&gt;  &lt;span class="kwrd"&gt;if&lt;/span&gt; (@AlreadyExists = 0) &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  34:  &lt;/span&gt;    &lt;span class="kwrd"&gt;begin&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  35:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  36:  &lt;/span&gt;      &lt;span class="kwrd"&gt;begin&lt;/span&gt; &lt;span class="kwrd"&gt;transaction&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  37:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  38:  &lt;/span&gt;      &lt;span class="kwrd"&gt;begin&lt;/span&gt; try&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  39:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  40:  &lt;/span&gt;        &lt;span class="kwrd"&gt;insert&lt;/span&gt; &lt;span class="kwrd"&gt;into&lt;/span&gt; dbo.LoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  41:  &lt;/span&gt;        &lt;span class="kwrd"&gt;values&lt;/span&gt; (@LoadTestGUID,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  42:  &lt;/span&gt;                @StartTime,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  43:  &lt;/span&gt;                @EndTime,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  44:  &lt;/span&gt;                @Duration)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  45:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  46:  &lt;/span&gt;        &lt;span class="kwrd"&gt;SELECT&lt;/span&gt; @loadTestGUID                                &lt;span class="kwrd"&gt;as&lt;/span&gt; LoadTestGUID,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  47:  &lt;/span&gt;               WebLoadTestTransaction.TransactionName, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  48:  &lt;/span&gt;               LoadTestTransactionSummaryData.Average, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  49:  &lt;/span&gt;               STDEV(LoadTestTransactionDetail.ElapsedTime) &lt;span class="kwrd"&gt;as&lt;/span&gt; StdDev, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  50:  &lt;/span&gt;               LoadTestTransactionSummaryData.Minimum, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  51:  &lt;/span&gt;               LoadTestTransactionSummaryData.Maximum, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  52:  &lt;/span&gt;               LoadTestTransactionSummaryData.Percentile90, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  53:  &lt;/span&gt;               LoadTestTransactionSummaryData.Percentile95, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  54:  &lt;/span&gt;               LoadTestTransactionSummaryData.TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  55:  &lt;/span&gt;        &lt;span class="kwrd"&gt;INTO&lt;/span&gt;   #tmpLoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  56:  &lt;/span&gt;        &lt;span class="kwrd"&gt;FROM&lt;/span&gt;   LoadTest.dbo.LoadTestTransactionDetail &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  57:  &lt;/span&gt;               LoadTest.dbo.WebLoadTestTransaction &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTest.dbo.LoadTestTransactionDetail.LoadTestRunId = LoadTest.dbo.WebLoadTestTransaction.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  58:  &lt;/span&gt;               LoadTest.dbo.LoadTestTransactionDetail.TransactionId = LoadTest.dbo.WebLoadTestTransaction.TransactionId &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  59:  &lt;/span&gt;               LoadTest.dbo.LoadTestTransactionSummaryData &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTest.dbo.WebLoadTestTransaction.LoadTestRunId = LoadTest.dbo.LoadTestTransactionSummaryData.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  60:  &lt;/span&gt;               LoadTest.dbo.WebLoadTestTransaction.TransactionId = LoadTest.dbo.LoadTestTransactionSummaryData.TransactionId&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  61:  &lt;/span&gt;        &lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  (LoadTestTransactionDetail.LoadTestRunId = @latestLoadTest)&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  62:  &lt;/span&gt;        &lt;span class="kwrd"&gt;GROUP&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; WebLoadTestTransaction.TransactionName, LoadTestTransactionSummaryData.Average, LoadTestTransactionSummaryData.Minimum, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  63:  &lt;/span&gt;                 LoadTestTransactionSummaryData.Maximum, LoadTestTransactionSummaryData.Percentile90, LoadTestTransactionSummaryData.Percentile95, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  64:  &lt;/span&gt;                 LoadTestTransactionSummaryData.TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  65:  &lt;/span&gt;        &lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; WebLoadTestTransaction.TransactionName&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  66:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  67:  &lt;/span&gt;        &lt;span class="kwrd"&gt;insert&lt;/span&gt; dbo.LoadTestHistoryDetails&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  68:  &lt;/span&gt;        &lt;span class="kwrd"&gt;select&lt;/span&gt; LoadTestGUID, TransactionName, Average, StdDev, Minimum, Maximum, Percentile90, Percentile95, TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  69:  &lt;/span&gt;        &lt;span class="kwrd"&gt;from&lt;/span&gt; #tmpLoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  70:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  71:  &lt;/span&gt;        &lt;span class="kwrd"&gt;drop&lt;/span&gt; &lt;span class="kwrd"&gt;table&lt;/span&gt; #tmpLoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  72:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  73:  &lt;/span&gt;        &lt;span class="kwrd"&gt;commit&lt;/span&gt; &lt;span class="kwrd"&gt;transaction&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  74:  &lt;/span&gt;      &lt;span class="kwrd"&gt;end&lt;/span&gt; try&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  75:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  76:  &lt;/span&gt;      &lt;span class="kwrd"&gt;begin&lt;/span&gt; catch&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  77:  &lt;/span&gt;        &lt;span class="kwrd"&gt;rollback&lt;/span&gt; &lt;span class="kwrd"&gt;transaction&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  78:  &lt;/span&gt;      &lt;span class="kwrd"&gt;end&lt;/span&gt; catch&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  79:  &lt;/span&gt;    &lt;span class="kwrd"&gt;end&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  80:  &lt;/span&gt;&lt;span class="kwrd"&gt;END&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And it works like a champ!&lt;br /&gt;&lt;br /&gt;I tested it with this bit of SQL:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;use&lt;/span&gt; LoadTestResults&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;&lt;span class="kwrd"&gt;go&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;&lt;span class="kwrd"&gt;truncate&lt;/span&gt; &lt;span class="kwrd"&gt;table&lt;/span&gt; dbo.LoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;&lt;span class="kwrd"&gt;truncate&lt;/span&gt; &lt;span class="kwrd"&gt;table&lt;/span&gt; dbo.LoadTestHistoryDetails&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;&lt;span class="kwrd"&gt;go&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;dbo.UpdateLoadTestHistory&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&lt;span class="kwrd"&gt;go&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;&amp;nbsp;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;&lt;span class="kwrd"&gt;select&lt;/span&gt; dbo.LoadTestHistory.LoadTestGUID, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;       StartTime, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;       EndTime, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;       Duration, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;       TransactionName, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;       Average, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;       StdDev,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;       Minimum,&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  19:  &lt;/span&gt;       Maximum,&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  20:  &lt;/span&gt;       [90th],&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  21:  &lt;/span&gt;       [95th],&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  22:  &lt;/span&gt;       TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  23:  &lt;/span&gt;&lt;span class="kwrd"&gt;from&lt;/span&gt; dbo.LoadTestHistory, dbo.LoadTestHistoryDetails&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  24:  &lt;/span&gt;&lt;span class="kwrd"&gt;where&lt;/span&gt; dbo.LoadTestHistory.LoadTestGUID = dbo.LoadTestHistoryDetails.LoadTestGUID&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;And the results come out for all to enjoy:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;LoadTestGUID                         StartTime               EndTime                 Duration TransactionName            Average          StdDev            Minimum Maximum 90th  95th  TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;0ebdf821-454f-4c50-8e3a-a82a291adb97 2008-07-03 16:21:33.280 2008-07-03 16:31:33.280 600      someTransaction.Details    3.4155221238938  0.583565372475793 1.526   4.952   4.161 4.35  226&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;0ebdf821-454f-4c50-8e3a-a82a291adb97 2008-07-03 16:21:33.280 2008-07-03 16:31:33.280 600      someTransaction.FirstHit   3.42468584070796 0.79565610855916  1.636   11.888  4.053 4.285 226&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;0ebdf821-454f-4c50-8e3a-a82a291adb97 2008-07-03 16:21:33.280 2008-07-03 16:31:33.280 600      someTransaction.LookupName 3.46306194690266 0.606631255863035 2.087   5.15    4.285 4.411 226&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6443999260650650731?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6443999260650650731/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6443999260650650731' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6443999260650650731'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6443999260650650731'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/collecting-daily-lt-metrics.html' title='Collecting daily LT Metrics'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-6600554376619554890</id><published>2008-07-03T13:28:00.001-05:00</published><updated>2008-09-02T20:51:39.934-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='PITA'/><category scheme='http://www.blogger.com/atom/ns#' term='LoadRunner'/><title type='text'>Retrieving Standard Deviation for Transaction Response times with VS2008</title><content type='html'>One of my plans for the firm for which I work is to integrate a nightly automagic LT with the Continuous Integration effort. I want to be able to automagically compare results of the previous days results and see if anything is amiss. One of the tests I would like to apply is the two population mean test which requires having the average response time, standard deviation and the number of transactions. &lt;br /&gt;&lt;br /&gt;VS2008 doesn't provide all of these statistics by default in their LT summary. LoadRunner includes this information by default in their summary results and extracting the values are quite simple if you publish the results to an Excel Spreadsheet.&lt;br /&gt;&lt;br /&gt;It's not so quite as simple as that with VS2008 but it can be done with a little effort.&lt;br /&gt;&lt;br /&gt;I have configured my LT to record individual metrics to the LoadTest Data Store so that I can query the tables and extract the information that I want. In the examples below I have identified the LoadTestRunId for a given run where I have plenty of metrics to number crunch. In my nightly build and test scenario I envision a simple query to pull the max(LoadTestRunId) to get the latest run to run queries to extract the required information.&lt;br /&gt;&lt;br /&gt;I wrote this query today to get the information that I wanted:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;SELECT&lt;/span&gt; WebLoadTestTransaction.TransactionName, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;       LoadTestTransactionSummaryData.Average, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;       STDEV(LoadTestTransactionDetail.ElapsedTime) &lt;span class="kwrd"&gt;AS&lt;/span&gt; StdDev, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;       LoadTestTransactionSummaryData.Minimum, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;       LoadTestTransactionSummaryData.Maximum, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;       LoadTestTransactionSummaryData.Percentile90, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;       LoadTestTransactionSummaryData.Percentile95, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;       LoadTestTransactionSummaryData.TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;&lt;span class="kwrd"&gt;FROM&lt;/span&gt;   LoadTestTransactionDetail &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;                  WebLoadTestTransaction &lt;span class="kwrd"&gt;ON&lt;/span&gt; LoadTestTransactionDetail.LoadTestRunId = WebLoadTestTransaction.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;                  LoadTestTransactionDetail.TransactionId = WebLoadTestTransaction.TransactionId &lt;span class="kwrd"&gt;INNER&lt;/span&gt; &lt;span class="kwrd"&gt;JOIN&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;                  LoadTestTransactionSummaryData &lt;span class="kwrd"&gt;ON&lt;/span&gt; WebLoadTestTransaction.LoadTestRunId = LoadTestTransactionSummaryData.LoadTestRunId &lt;span class="kwrd"&gt;AND&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;                  WebLoadTestTransaction.TransactionId = LoadTestTransactionSummaryData.TransactionId&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;&lt;span class="kwrd"&gt;WHERE&lt;/span&gt;  (LoadTestTransactionDetail.LoadTestRunId = 33)&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;&lt;span class="kwrd"&gt;GROUP&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; WebLoadTestTransaction.TransactionName, LoadTestTransactionSummaryData.Average, LoadTestTransactionSummaryData.Minimum, &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  16:  &lt;/span&gt;                  LoadTestTransactionSummaryData.Maximum, LoadTestTransactionSummaryData.Percentile90, LoadTestTransactionSummaryData.Percentile95, &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  17:  &lt;/span&gt;                  LoadTestTransactionSummaryData.TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  18:  &lt;/span&gt;&lt;span class="kwrd"&gt;ORDER&lt;/span&gt; &lt;span class="kwrd"&gt;BY&lt;/span&gt; WebLoadTestTransaction.TransactionName&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The QBE table entries look this this:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://bp3.blogger.com/_qsa4nRIPmPo/SG0dskhiHVI/AAAAAAAAAI8/q-CgFYQjGWE/s1600-h/StdDev_QBE_Tables.jpg"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://bp3.blogger.com/_qsa4nRIPmPo/SG0dskhiHVI/AAAAAAAAAI8/q-CgFYQjGWE/s320/StdDev_QBE_Tables.jpg" alt="" id="BLOGGER_PHOTO_ID_5218860194748046674" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;And I get the results I want:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;TransactionName            Average             StdDev               Minimum Maximum 90th  95th  TransactionCount&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;someTransaction.Details    0.47510353043101411 0.090671477518885088 0.416   3.695   0.522 0.552 4617&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;someTransaction.FirstHit   0.44759085986571417 0.077896506427873255 0.384   3.956   0.502 0.526 4617&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;someTransaction.LookupName 0.51917002382499466 0.082360597958219192 0.45    2.844   0.577 0.605 4617&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Huzzah!&lt;br /&gt;&lt;br /&gt;I'll end up setting up an automagic comparison routine. A number of years ago I wrote a C# class for doing statistical tests so I'll probably end up using that for my left/right comparisons and number crunching.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-6600554376619554890?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/6600554376619554890/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=6600554376619554890' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6600554376619554890'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/6600554376619554890'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/retrieving-standard-deviation-for.html' title='Retrieving Standard Deviation for Transaction Response times with VS2008'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://bp3.blogger.com/_qsa4nRIPmPo/SG0dskhiHVI/AAAAAAAAAI8/q-CgFYQjGWE/s72-c/StdDev_QBE_Tables.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-8404390462233797197</id><published>2008-07-01T21:19:00.001-05:00</published><updated>2008-08-07T23:09:19.568-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Portable Server'/><title type='text'>It's finally been ordered!</title><content type='html'>I got news today that my new laptop has finally been official ordered. Here is the basic specs as it stands right now:&lt;br /&gt;&lt;br /&gt;Quad Core Xeon 2.83 GHz proc&lt;br /&gt;4 gig RAM&lt;br /&gt;17 inch display at 1680x1080&lt;br /&gt;RAID5 with 3 drives spinning 7200 RPM with 200 gig each for 400 gig of filespace&lt;br /&gt;nVidia 8800M 512 meg video card (only 1 but it can support 2)&lt;br /&gt;&lt;br /&gt;I was able to order it without an operating system so I will be installing either Win2k3 or Win2k8 for my load testing needs.&lt;br /&gt;&lt;br /&gt;Hopefully tomorrow I'll find out when it is arriving. It's like Christmas in July!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-8404390462233797197?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/8404390462233797197/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=8404390462233797197' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8404390462233797197'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/8404390462233797197'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/07/its-finally-been-ordered.html' title='It&apos;s finally been ordered!'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-3844352961671933338</id><published>2008-06-30T14:50:00.000-05:00</published><updated>2008-08-07T23:09:08.647-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><title type='text'>Another neat use for PostRequest</title><content type='html'>I have a page that I am testing against that comes back as a failure because a .js file hasn't been migrated yet so I don't want my coded web test to hit that page and fail. I took care of the problem with PostRequest for the WebTestRequest call like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;void&lt;/span&gt; preventCallsToJS(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, PostRequestEventArgs e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  List&amp;lt;WebTestRequest&amp;gt; requestsToRemove = &lt;span class="kwrd"&gt;new&lt;/span&gt; List&amp;lt;WebTestRequest&amp;gt;()&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;  &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (WebTestRequest linkToRemove &lt;span class="kwrd"&gt;in&lt;/span&gt; e.Request.DependentRequest&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    &lt;span class="kwrd"&gt;if&lt;/span&gt; (linkToRemove.Url.EndsWith(&lt;span class="str"&gt;"someJavaScriptFile.js"&lt;/span&gt;)) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      requestsToRemove.Add(linkToRemove);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;  };&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;  &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (WebTestRequest linkToRemove &lt;span class="kwrd"&gt;in&lt;/span&gt; requestsToRemove) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    e.Request.DependentRequests.Remove(linkToRemove);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;  };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;For each request I have added the call to the above PostRequest like this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;request4.PostRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;PostRequestEventArgs&amp;gt;(preventCallsToJS);&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;No more problems hitting those pesky not yet deployed .js files. w00t!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-3844352961671933338?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/3844352961671933338/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=3844352961671933338' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3844352961671933338'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3844352961671933338'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/06/another-neat-use-for-postrequest.html' title='Another neat use for PostRequest'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-3487161045781765195</id><published>2008-06-30T10:49:00.001-05:00</published><updated>2008-09-02T20:52:14.149-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='Fiddler2'/><title type='text'>Saving sessions as webtests with Fiddler2</title><content type='html'>Today I was trying to save some saved sessions with Fiddler2 to a webtest since I cannot record webtests with VS2008 with my common user profile that I use on my laptop.&lt;br /&gt;&lt;br /&gt;When I tried to save the file I got a very annoying error that a specified assembly could not be found. Fiddler2 is trying to load version 8.0.0.0 of Microsoft.VisualStudio.QualityTools.WebTestFramework.dll but alas, I do not have VS2005 install, I have VS2008. What to do? I'm already bummed that I cannot record webtests as I should be able to. I find this to be really annoying as recording webtests shouldn't be this annoying.&lt;br /&gt;&lt;br /&gt;At this point you're probably asking, "So, Mr. Auswipe-Load-Tester-Dude, does this shake your faith in the VS2008 framework for load testing?" Not yet is my reply. Why you ask when these two issues are clearly impediments? Well, with Load Runner I was running into problems recording virtual users and had the problem for over three weeks where I could not record vusers on my work machine. When I tried to record vusers both FireFox and IE would detonate and crash. I worked with Level I and Level II techs for over three weeks trying to correct the problem and the issues was never solved. It turns out I got my current job offer and accepted it. I told my former coworkers that I figured the solution was to take the fdisk quiz and reload the OS and start again. I certainly hope the same solution is not required for the MS solution.&lt;br /&gt;&lt;br /&gt;Anyway, back to the issue at hand...&lt;br /&gt;&lt;br /&gt;I did some Googling and I found this entry on &lt;a href="http://www.codeprof.com/dev-archive/218/5-1408-2182081.shtm"&gt;CodeProf.com&lt;/a&gt; with a solution by Ed Glas. Ed basically says to modify the fiddler.exe.config and use the &lt;bindingredirect&gt; tag but as documented &lt;a href="http://msdn.microsoft.com/en-us/library/twy1dw1e%28VS.80%29.aspx"&gt;here&lt;/a&gt; but did not include a full fledged example, just the link to the .NET config file documentation. So, I'm not a .NET expert by any stretch of the imagination (but am working on it slowly but surely) so I wasn't quite sure what all config tags were required for the redirect. Here is what I used and it appears to be working *cross fingers*.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;configuration&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;runtime&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;legacyUnhandledExceptionPolicy&lt;/span&gt; &lt;span class="attr"&gt;enabled&lt;/span&gt;&lt;span class="kwrd"&gt;="1"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;    &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;assemblyBinding&lt;/span&gt; &lt;span class="attr"&gt;xmlns&lt;/span&gt;&lt;span class="kwrd"&gt;="urn:schemas-microsoft-com:asm.v1"&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;dependentAssembly&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;        &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;assemblyIdentity&lt;/span&gt; &lt;span class="attr"&gt;name&lt;/span&gt;&lt;span class="kwrd"&gt;="Microsoft.VisualStudio.QualityTools.WebTestFramework"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;                                &lt;span class="attr"&gt;publicKeyToken&lt;/span&gt;&lt;span class="kwrd"&gt;="b03f5f7f11d50a3a"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;                                &lt;span class="attr"&gt;culture&lt;/span&gt;&lt;span class="kwrd"&gt;="neutral"&lt;/span&gt; &lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;            &lt;span class="kwrd"&gt;&amp;lt;&lt;/span&gt;&lt;span class="html"&gt;bindingRedirect&lt;/span&gt; &lt;span class="attr"&gt;oldVersion&lt;/span&gt;&lt;span class="kwrd"&gt;="8.0.0.0"&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;                             &lt;span class="attr"&gt;newVersion&lt;/span&gt;&lt;span class="kwrd"&gt;="9.0.0.0"&lt;/span&gt;&lt;span class="kwrd"&gt;/&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;         &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;dependentAssembly&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;      &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;assemblyBinding&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;  &lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;runtime&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;&lt;span class="kwrd"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="html"&gt;configuration&lt;/span&gt;&lt;span class="kwrd"&gt;&amp;gt;&lt;/span&gt; &lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;/bindingredirect&gt;&lt;br /&gt;&lt;bindingredirect&gt;After doing some recording it appears that the code that is generated from the converted Fiddler2 to .webtest to coded web test looks good and I see the automagic correlation of data extracted from form fields and the ever dreaded VIEWSTATE.&lt;br /&gt;&lt;br /&gt;This oughta be good enough for me right now until I resolve the situation with VS2008. I have no personal problems with recording a session, saving as a .webtest and then added the .webtest to a project and converting to a coded web test so it's all good with me.&lt;br /&gt;&lt;/bindingredirect&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-3487161045781765195?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/3487161045781765195/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=3487161045781765195' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3487161045781765195'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/3487161045781765195'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/06/saving-sessions-as-webtests-with.html' title='Saving sessions as webtests with Fiddler2'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-5657168852880349561</id><published>2008-06-28T22:58:00.001-05:00</published><updated>2008-09-02T20:53:43.879-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='VS2008'/><category scheme='http://www.blogger.com/atom/ns#' term='PITA'/><category scheme='http://www.blogger.com/atom/ns#' term='Fiddler2'/><category scheme='http://www.blogger.com/atom/ns#' term='LoadRunner'/><title type='text'>The Good, The Bad and The Ugly.</title><content type='html'>I've been real busy with non load testing stuff at work and haven't had much of a chance to continue playing around with VS2008 for LT purposes but I have found out some interesting stuff.&lt;br /&gt;&lt;br /&gt;The Good:&lt;br /&gt;&lt;br /&gt;You can specify the data store (either SQL Server or SQL Server Express) for storing all the individual hits from web sites and then query that information which renders that I was doing in the earlier blog post as moot. Yay! By default, VS2008 TSTE keeps these results in SQL Server Express that is installed when Visual Studio is installed. It's pretty cool that you can choose to store results in another location for historical reasons.&lt;br /&gt;&lt;br /&gt;Looking around the data in the tables I'm not sure if you can group page hits together by transaction ID. But, I haven't had much of a chance to play around with querying data and am not for sure if this is the case. If that is the case, I can still use a method similar to a previous blog entry for collecting the metrics myself for catching page response time as it relates to individual transactions. A bit of a PITA but fantastic that I can do it if I want. Yay for a flexible framework that allows me as a test with development background to run around and do this kind of stuff.&lt;br /&gt;&lt;br /&gt;One thing I know for sure though is with the ability to query the databases I should now have the ability to crunch the numbers for transactions and now get average, standard deviation, hit counts, etc. The idea is that in a nicely controlled environment I should be able to generate an automagic report for day to day automated load test execution and be able to do something like a mean population comparison of transactions times from one test to the next and pick out statistically significant deviations for alpha = 0.05. That'll be kinda neat if it works out.&lt;br /&gt;&lt;br /&gt;Now that I think about it, the VS2008 tool should have been reporting these metrics all along. LoadRunner defaults with basic statistical information and VS2008 should report the same as well. It is kind of annoying, but I like the fact that I have a published schema that I can use to extract the information that I want (see link to schema information for VS2008 further down below). So, another plus in the LoadRunner column. [I might really like the VS2008 product, but I do have to be fair in my comparisons of Pro/Con of VS2008 versus LoadRunner.]&lt;br /&gt;&lt;br /&gt;I wasn't able to use the statistical modeling at my previous employer because our systems were just too large with too many variables involved. We had communication from the web server to the database server (which was on the same switch so not much of a problem) but we also had a bunch of communications to the Host. The Host was required for testing the web sites and was actually hitting production systems that merely disregarded the requests and sent back bogus results so that we wouldn't kill the mainframe.&lt;br /&gt;&lt;br /&gt;Many years ago I tried to get rid of this variability by writing what I called a "parroting proxy server." It was a bit of C# code that was essentially a protocol agnostic proxy server that would pass messages from point A to point C where the intermediate Point B was the proxy. It would store responses with a in memory hash and if the same request was caught being requested it would go ahead and send back the hashed response to eliminate the variance that we would see. It worked out pretty good but we never got around to implementing it. Unfortunately in a Fortune 50 company the bureaucracy can be too great and with Empire Building as it is the other managers didn't see what was in this for them and I suppose I didn't do a very good sales job. So be it.&lt;br /&gt;&lt;br /&gt;To flip the switch to log all data so that you can query the data for fun and profit is to modify the "Timing Details Storage Property" in the Run Setting node of the load test editor to "All individual details."&lt;br /&gt;&lt;br /&gt;And unlike the LoadRunner database (AFAIK, I could be wrong!), this schema is published with all sorts of handy information located at &lt;a href="http://blogs.msdn.com/billbar/articles/529874.aspx"&gt;http://blogs.msdn.com/billbar/articles/529874.aspx&lt;/a&gt;. How great is that?&lt;br /&gt;&lt;br /&gt;The Bad:&lt;br /&gt;&lt;br /&gt;I haven't had enough time to continue my little load testing adventure. I'm finding that my new employer needs to update their processes as it relates to building production final machines. The current steps are extremely painful. But I suspect it'll be just fine in the long run as I've had some experience in this in the past and now that this process can be fully automated and integrated into our build process to be painless.&lt;br /&gt;&lt;br /&gt;The Ugly:&lt;br /&gt;&lt;br /&gt;Ok, this is ugly. I tried to write a simple script to augment some stuff that I did last week only to find that I couldn't bring up an instance of IE with the Web Recorder active and it is frustrating as hell. I found several posts that essentially say that the "solution" is to delete your login profile and start again. Yeah, that really sucks, Microsoft. I'd like to have a solution that doesn't require me to blow away my profile and have me spend hours getting my desktop back into the shape that I like it. That is a real PITA for sure.&lt;br /&gt;&lt;br /&gt;I have three profiles on my machine and I can only generate recorded web tests with one of these profiles. I blew away one of the smaller profiles (not my main profile where I have 2.5+ gig of files) and gave it a try and it was no-go. I still could not record web tests. Yeah, that is bad. Very bad.&lt;br /&gt;&lt;br /&gt;I'm not sure what the solution on this is going to be. In the short term I'm going to have to use my Administrator login to create scripts and then xfer that over to my main login script. How craptacular is that? Once I get some free time I'll have to look into this further but I am dreading it. No fun!&lt;br /&gt;&lt;br /&gt;Here are some MS blog entries related to the problem. Perhaps they could help somebody else but I haven't seen any love for me, yet.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blogs.msdn.com/mtaute/archive/2007/11/09/diagnosing-and-fixing-web-test-recorder-bar-issues.aspx"&gt;http://blogs.msdn.com/mtaute/archive/2007/11/09/diagnosing-and-fixing-web-test-recorder-bar-issues.aspx&lt;/a&gt;&lt;br /&gt;&lt;a href="http://blogs.msdn.com/edglas/archive/2007/11/01/web-test-recorder-bar-not-showing-up.aspx"&gt;http://blogs.msdn.com/edglas/archive/2007/11/01/web-test-recorder-bar-not-showing-up.aspx&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;It appears that Fiddler2 might be an interim solution from what I read here in this MS blog post located at &lt;a href="http://blogs.msdn.com/slumley/pages/enhanced-web-test-support-in-fiddler.aspx"&gt;http://blogs.msdn.com/slumley/pages/enhanced-web-test-support-in-fiddler.aspx&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;A PITA? Sure, but it beats not having any solution at all. Fiddler is purt near cool and I've "fiddled" with it a little bit and it is nifty.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9120072357542955286-5657168852880349561?l=adventuresinloadtesting.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://adventuresinloadtesting.blogspot.com/feeds/5657168852880349561/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=9120072357542955286&amp;postID=5657168852880349561' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5657168852880349561'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/9120072357542955286/posts/default/5657168852880349561'/><link rel='alternate' type='text/html' href='http://adventuresinloadtesting.blogspot.com/2008/06/good-bad-and-ugly.html' title='The Good, The Bad and The Ugly.'/><author><name>Auswipe</name><uri>http://www.blogger.com/profile/04301689793936189392</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-9120072357542955286.post-3636157445255737794</id><published>2008-06-20T20:14:00.000-05:00</published><updated>2008-06-21T03:20:46.232-05:00</updated><title type='text'>Getting all the metrics using WebTestRequestPlugin</title><content type='html'>If there is one shortcoming that I've noticed with VS2008 for load testing it is the default report exporting. With LR (since at least version 6.5 that I can remember) we've always had the ability to export the final output of page response times to an Excel spreadsheet.&lt;br /&gt;&lt;br /&gt;At my previous employer that was part of the routine: Analyze results, export to Excel spreadsheet and then use some custom software to crunch the before and after results into a snazzy easy to read format (props to Brian of www.alphasixty.com!).&lt;br /&gt;&lt;br /&gt;From what I've seen with VS2008 so far I see that the exported XML format (with an extension of TRX) contains a lot of good data and it is in an XML format but it isn't as simple as opening up an Excel spreadsheet with all the page response times and slicing and dicing beyond that. So, I still have to give props to LR for that.&lt;br /&gt;&lt;br /&gt;Something that I've always wanted from LR was to get even more information. More page response times and the response times of the objects that were pulled by the pages that we were testing. Transaction times is all we ever got.&lt;br /&gt;&lt;br /&gt;Well, while playing with VS2008 today I figured out some nifty stuff. Microsoft has done a pretty good job of allowing the tester who happens to be a coder to develop code to do just this time of thing that I want and I am doing it with the WebTestRequestPlugin.&lt;br /&gt;&lt;br /&gt;Here is the basic class that I wrote to get page response times of the primary call. It's nothing fancy and for right now it just dumps to STDOUT but it easily can (and will be) adapted to insert into a database for futher analysis. I've just dumped the page and the time in milliseconds just for sake of ease to demonstrate.&lt;br /&gt;&lt;br /&gt;Here is my plugin code:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;class&lt;/span&gt; GetAllMetrics : WebTestRequestPlugin {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;      &lt;span class="kwrd"&gt;public&lt;/span&gt; &lt;span class="kwrd"&gt;override&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; PostRequest(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, PostRequestEventArgs e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; requestedURL = e.Request.UrlWithQueryString.ToString();&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;        &lt;span class="kwrd"&gt;double&lt;/span&gt; requestTime  = e.Response.Statistics.MillisecondsToLastByte;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;        &lt;span class="kwrd"&gt;string&lt;/span&gt; outputString = &lt;span class="str"&gt;"The resource "&lt;/span&gt; + requestedURL + &lt;span class="str"&gt;" took "&lt;/span&gt; + requestTime.ToString() +&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;                              &lt;span class="str"&gt;" milliseconds to load."&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;        Debug.WriteLine(outputString);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;      }&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;Pretty simple, eh? Sure it is. Short and sweet and to the point.&lt;br /&gt;&lt;br /&gt;For this example I am hitting AOL.com. The reasons will be clear in a bit.&lt;br /&gt;&lt;br /&gt;I made a private instance of this class in my coded web test:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; GetAllMetrics getMetrics = &lt;span class="kwrd"&gt;new&lt;/span&gt; GetAllMetrics();&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;The reason for this will be clear in a moment. Now, wire up the PostRequest to the instance declared above for my request to AOL.com:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;      WebTestRequest request1 = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebTestRequest(&lt;span class="str"&gt;"http://www.aol.com/"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;      request1.ThinkTime = 2;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;      request1.PostRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;PostRequestEventArgs&amp;gt;(getMetrics.PostRequest);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;      &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; request1;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      request1 = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;When I hit AOL.com I get something like this in my output window:&lt;br /&gt;&lt;br /&gt;"The resource http://www.aol.com/ took 272 milliseconds to load."&lt;br /&gt;&lt;br /&gt;That is nifty.&lt;br /&gt;&lt;br /&gt;After that, I got to thinking. Why can't I wire up all the dependent requests with the same plumbing and get all the metrics for dependent requests?&lt;br /&gt;&lt;br /&gt;I couldn't think of any reason why not and coded this method to do so:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;    &lt;span class="kwrd"&gt;private&lt;/span&gt; &lt;span class="kwrd"&gt;void&lt;/span&gt; setupDependentMetrics(&lt;span class="kwrd"&gt;object&lt;/span&gt; sender, PostRequestEventArgs e) {&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;      &lt;span class="kwrd"&gt;foreach&lt;/span&gt; (WebTestRequest request &lt;span class="kwrd"&gt;in&lt;/span&gt; e.Request.DependentRequests) {&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;        request.PostRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;PostRequestEventArgs&amp;gt;(getMetrics.PostRequest);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;      };&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;    }&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There on line #3 you see where I reference the private instance declared in my coded web test class. Makes sense now, eh?&lt;br /&gt;&lt;br /&gt;I wired up a PostRequest with the method that I coded and got this:&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;      WebTestRequest request1 = &lt;span class="kwrd"&gt;new&lt;/span&gt; WebTestRequest(&lt;span class="str"&gt;"http://www.aol.com/"&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;      request1.ThinkTime = 2;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;      request1.PostRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;PostRequestEventArgs&amp;gt;(setupDependentMetrics);&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;      request1.PostRequest += &lt;span class="kwrd"&gt;new&lt;/span&gt; EventHandler&amp;lt;PostRequestEventArgs&amp;gt;(getMetrics.PostRequest);&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;      &lt;span class="kwrd"&gt;yield&lt;/span&gt; &lt;span class="kwrd"&gt;return&lt;/span&gt; request1;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;      request1 = &lt;span class="kwrd"&gt;null&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;&lt;br /&gt;There we go! Let's fire off this bad boy and see what kind of output we get now.&lt;br /&gt;&lt;br /&gt;&lt;!-- code formatted by http://manoli.net/csharpformat/ --&gt;&lt;br /&gt;&lt;div class="csharpcode"&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   1:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aol.com/ took 272 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   2:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/_media/aolp_v31/main.js took 44 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   3:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/_media/aolp_v31/main.css took 86 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   4:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//o.aolcdn.com/ads/adsWrapperAT.js took 41 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   5:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/aolp_mkhome/474f6b9b-00394-029c3-400cb8e1 took 22 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   6:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//o.aolcdn.com/omniunih.js took 51 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   7:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/aolp_mkhome/474f6b9a-00272-029c3-400cb8e1 took 13 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;   8:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/aolp/oo_engine_main.js took 14 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;   9:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/_media/aolp_v31/updated.gif took 17 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  10:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/_media/aolp_v31/new.gif took 20 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  11:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/aolportal/mars-200-061708.jpg took 36 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  12:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/aolp_dlsnag/snagbutton_default.gif took 12 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  13:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/aolportal/derek-jeter-yankees-60mh0620.jpg took 20 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="lnum"&gt;  14:  &lt;/span&gt;The resource http:&lt;span class="rem"&gt;//www.aolcdn.com/_media/aolp_v31/bctrl.gif took 10 milliseconds to load.&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="alt"&gt;&lt;span class="lnum"&gt;  15:  &lt;/span&gt;The res
