Canonical Voiceshttp://voices.canonical.com2010-12-10T23:21:26ZCanonical ISD: Tsung Quick Start Part 22010-12-10T23:21:26ZDanny Tameznospam@nospam.comhttp://voices.canonical.com/isd/2010/12/10/tsung-quick-start-part-2/<p>In part 1 of this article (http://voices.canonical.com/isd/?p=118) we did sort of a hello world for a Tsung load test. This time we&#8217;re going to continue by getting into more of the details of how Tsung works and how to use it for real world tests.</p>
<h3>Using the Recorder</h3>
<p>More likely than not, the application you want to test consists of lots of urls with lots of query string values or form post values. Typing this in by hand is not practical and would take way too long. What Tsung provides is a a proxy recorder that you can set up to record all of the http calls you are making to and receiving from you application. The end result is a Tsung &#8220;session&#8221; in an xml file. You can then tweak the file however you want to better suit your tests.</p>
<p>Here&#8217;s how to set it up.<br />
1) Configure your browser to use a proxy server at http://localhost:8090. For firefox you can follow the instructions at http://support.mozilla.com/en-US/kb/Options window &#8211; Advanced panel?as=u#advanced_network .<br />
2) Start the proxy recorder</p>
<pre>$ <span style="color: #3366ff;">tsung-recorder start</span></pre>
<p>3) Point your browser to your application and go through one or more scenarios that you would like to group together as a teset scenario.<br />
4) When you&#8217;re ready to stop recording simply stop the recorder.</p>
<pre>$<span style="color: #3366ff;">t</span><span style="color: #3366ff;"><span style="color: #3366ff;">s</span>ung-recorder stop</span></pre>
<p>You now have a Tsung session xml file. You can now change things like thinktimes or change values you typed in to be dynamically generated. In order to use the file in your main tsung.xml file all you need to is include it as an xml entity and then reference it inside the sessions tag.</p>
<pre>&lt;?xml version="1.0"?&gt;</pre>
<pre>&lt;!DOCTYPE tsung SYSTEM "/usr/share/tsung/tsung-1.0.dtd" [</pre>
<pre>&lt;!ENTITY new_accounts SYSTEM "./new_accounts.xml"&gt;</pre>
<pre>&lt;!ENTITY log_out SYSTEM "./log_out.xml"&gt;</pre>
<pre>&lt;!ENTITY log_in SYSTEM "./log_in.xml"&gt;</pre>
<pre>&lt;!ENTITY auth_logged_in SYSTEM "./auth_already_logged_in.xml"&gt;</pre>
<pre>&lt;!ENTITY auth_logged_out SYSTEM "./auth_not_logged_in.xml"&gt;</pre>
<pre>&lt;!ENTITY api_register SYSTEM "./api_register.xml"&gt;</pre>
<pre>&lt;!ENTITY api_server SYSTEM "./api_server.xml"&gt;</pre>
<pre>]&gt;</pre>
<p>&lt;tsung loglevel=&#8221;debug&#8221; dumptraffic=&#8221;true&#8221; version=&#8221;1.0&#8243;&gt;</p>
<pre>.</pre>
<pre>.</pre>
<pre>.</pre>
<pre>&lt;sessions&gt;</pre>
<pre>&lt;session name="web" probability="70" type='ts_http'&gt;</pre>
<pre>&amp;new_accounts;</pre>
<pre>&amp;log_in;</pre>
<pre>&lt;/session&gt;</pre>
<pre>.
.
.
&lt;sessions&gt;</pre>
<h3>Creating Load</h3>
<p>Load is configured in Tsung inside the &#8216;load&#8217; tag. This tag has an optional &#8216;duration&#8217; attribute that can specify a maximum time for the test to run regardless of whether or not all requests have been processed. Inside of the load tag you specify one or more &#8216;arrivalphase&#8217; tags each with a specified duration. Finally, within each arrival phase you specify via the &#8216;users&#8217; tag the rate at which users are generated for that phase.</p>
<pre>&lt;load&gt;</pre>
<pre>&lt;arrivalphase phase="1" duration="6" unit="minute"&gt;</pre>
<pre>&lt;users interarrival="1.0" unit="second"/&gt;</pre>
<pre>&lt;/arrivalphase&gt;</pre>
<pre>&lt;arrivalphase phase="2" duration="5" unit="minute"&gt;</pre>
<pre>&lt;users interarrival="0.8" unit="second"/&gt;</pre>
<pre>&lt;/arrivalphase&gt;</pre>
<pre>.</pre>
<pre>.</pre>
<pre>.</pre>
<pre>&lt;/load&gt;</pre>
<p>Some caveats:<br />
Keep in mind that the number of concurrent users will depend on how long it takes for each user&#8217;s session to be processed. If sessions are long then there will be overlap between arrival phases. Likewise, request made per second is not a direct correlation of the user arrival rate as each sessioin may have thinktimes between each request that it makes. Additionally, as sessions usually have more than one request, each user created will overlap subsequent requests with new users&#8217; requests.</p>
<p>One of the reasons that you would choose Tsung over other load testing tools is that it will generate much higher load than other tools. Being built on Erlang, one server can typically generate tens of thousands of users. However, you can also generate even more load if necessary by running a cluster of servers.</p>
<h3>Using Clusters</h3>
<p>It is easy to use Tsung with one client or several clients and just as easy to use it with one or more servers.</p>
<pre>&lt;clients&gt;</pre>
<pre>&lt;client host="&amp;client_host_1;" use_controller_vm="true" maxusers="2000" cpu="2" weight="2"/&gt;</pre>
<pre>&lt;client host="&amp;client_host_2;" use_controller_vm="true" maxusers="2000" weight="1"/&gt;</pre>
<pre>&lt;client host="&amp;client_host_3;" use_controller_vm="true" maxusers="2000" weight="1"/&gt;</pre>
<pre>&lt;/clients&gt;</pre>
<pre>&lt;servers&gt;
&lt;server host="&amp;provider_host_1;" port="80" type="tcp"&gt;&lt;/server&gt;
&lt;server host="&amp;provider_host_2;" port="80" type="tcp"&gt;&lt;/server&gt;
&lt;/servers&gt;</pre>
<p>In the above example Tsung will send twice as many requests to client 1 than it sends to client 2 or 3. Another way of looking at is that 1/2 of the requests will come from client 1, 1/4 from client 2 and 1/4 from client 3. The weight will be evenly distributed between the two servers via simple round robin.</p>
<h3>Using Dynamic Variables</h3>
<p>Often you will have situations where you can&#8217;t hardocode parameters into your session files. For instance, sometimes you need to use a value in one request that was generated by the previous request. An example of this is if your application uses Cross Site Request Forgery tokens.</p>
<pre>&lt;thinktime min="2" max="10" random="true"/&gt;</pre>
<pre>&lt;transaction name="display_log_in"&gt;
&lt;request&gt;
&lt;dyn_variable name="csrfmiddlewaretoken"/&gt;
&lt;http url='&amp;provider_url;' version='1.1' method='GET'/&gt;
&lt;/request&gt;
&lt;/transaction&gt;</pre>
<pre>&lt;thinktime min="7" max="30" random="true"/&gt;</pre>
<pre>&lt;transaction name="log_in"&gt;
&lt;request subst="true"&gt;
&lt;http url='&amp;provider_url;/+login' version='1.1'
contents='csrfmiddlewaretoken=%%_csrfmiddlewaretoken%%&amp;amp;email=%%_email%%@%%_domain%%.com&amp;amp;password=%%_password%%&amp;amp;continue='
content_type='application/x-www-form-urlencoded' method='POST'/&gt;
&lt;/request&gt;
&lt;request&gt;
&lt;http url='&amp;provider_url;' version='1.1' method='GET'/&gt;
&lt;/request&gt;
&lt;/transaction&gt;</pre>
<p>In the example above a form is displayed in the &#8220;display_log_in&#8221; transaction. A hidden csrf token is in that pages login form. We are capturing that value via the &#8216;dyn_variable&#8217; tag in the first requeset. We need that value in the second requeset and use it in the &#8216;content&#8217; attribute as &#8216;%%_csrfmiddlewaretoken&#8217;. When using dynamic variables, be sure to use &#8216;subst=&#8221;true&#8221;&#8216; in the request tag or the variables will be interepreted as literals.</p>
<p>Sometimes the value you need is on the page but is not a form field. You can use a regex to match it and store it in a variable.</p>
<pre>&lt;dyn_variable name="janrain_nonce" regexp='janrain_nonce=\([^\"]*\)'/&gt;</pre>
<pre>&lt;http url='&amp;consumer_url;/verify?openid_identifier=&amp;provider_url;/'</pre>
<pre>version='1.1' method='GET'/&gt;</pre>
<p>It&#8217;s also easy to share variables across requests as they can be defined for a session.</p>
<pre>&lt;session name="web" probability="70" type='ts_http'&gt;</pre>
<pre>&lt;setdynvars sourcetype="random_string" length="8"&gt;</pre>
<pre>&lt;var name="password"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<pre>&lt;setdynvars sourcetype="random_string" length="5"&gt;</pre>
<pre>&lt;var name="first"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<pre>&lt;setdynvars sourcetype="random_string" length="5"&gt;</pre>
<pre>&lt;var name="last"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<pre>&lt;setdynvars sourcetype="random_string" length="5"&gt;</pre>
<pre>&lt;var name="domain"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<pre>&lt;setdynvars sourcetype="random_string" length="5"&gt;</pre>
<pre>&lt;var name="email"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<pre>&lt;setdynvars sourcetype="random_string" length="16"&gt;</pre>
<pre>&lt;var name="secret"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<pre>&amp;new_accounts;</pre>
<pre>&amp;log_in;</pre>
<pre>&amp;auth_logged_in;</pre>
<pre>&amp;log_out;</pre>
<pre>&amp;auth_logged_out;</pre>
<pre>&lt;/session&gt;</pre>
<p>If you need something more powerful you can call custom Erlang code defined in an external module,</p>
<pre>&lt;setdynvars sourcetype="eval"</pre>
<pre>code="fun({Pid, DynVars}) -&gt; sso:auth_header(DynVars) end."&gt;</pre>
<pre>&lt;var name="authorization_header"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<p>or evaluate Erlang code inline,</p>
<pre>&lt;setdynvars</pre>
<pre>sourcetype="eval"</pre>
<pre>code="fun({Pid,DynVars})-&gt;</pre>
<pre>{ok,Val}=ts_dynvars:lookup('openid.assoc_handle',DynVars),</pre>
<pre>edoc_lib:escape_uri(Val)</pre>
<pre>end."&gt;</pre>
<pre>&lt;var name="assoc_handle"/&gt;</pre>
<pre>&lt;/setdynvars&gt;</pre>
<p>or by looking up a value from an external file.</p>
<pre>&lt;options&gt;</pre>
<pre>&lt;option name="file_server" id="accounts" value="../accounts.csv"/&gt;</pre>
<pre>&lt;/options&gt;</pre>
<pre>&lt;sessions&gt;
&lt;session name="all" probability="100" type='ts_http'&gt;
&lt;setdynvars sourcetype="file" fileid="accounts" delimiter="," order="iter"&gt;
&lt;var name="email" /&gt;
&lt;var name="domain" /&gt;
&lt;var name="password" /&gt;
&lt;/setdynvars&gt;</pre>
<h3>Looping and Branching</h3>
<p>Tsung allows you to use simple branching and looping in your Tsung files. For example you can bracket a request inside an if tag so that it only is called under certain conditions,</p>
<pre>&lt;if var="is_super_user" eq="1"&gt;</pre>
<pre>&lt;request&gt; &lt;http url="/delete_account"&gt;&lt;/http&gt; &lt;/request&gt;</pre>
<pre>&lt;/if&gt;</pre>
<p>or loop a certain request several times for the same user.</p>
<pre>&lt;for from="1" to="5" incr="1" var="product_id"&gt;
&lt;request&gt; &lt;http url="/product?id=%%_product_id%%"&gt;&lt;/http&gt; &lt;/request&gt;
&lt;/for&gt;</pre>
<p>There is still much more that Tsung can do and if you&#8217;re interested in using it there is a great community of people around it that can be of assistatnce. I hope these two articles will be of some help to those of you starting with Tsung and that it becomes a great tool in your toolbox. Good luck!</p>Canonical ISD: Load Testing with Tsung Quick Start2010-11-14T01:29:31ZDanny Tameznospam@nospam.comhttp://voices.canonical.com/isd/2010/11/14/load-testing-with-tsung-quick-start/<p><strong>What is Tsung and what is load testing?</strong></p>
<p>One of the tools we use on the ISD team is Tsung. We use it to load test our web based applications in our staging environment. In this article we&#8217;re going to go through all the steps necessary to setup and run a simple as possible load test. Later we&#8217;ll look at how to configure Tsung and your environment for more complex scenarios.</p>
<p>Load testing is the process of examining a system (such as an application or a device) under higher than normal load in order to see how it performs under those conditions. It is a very important part of testing any application where load amounts are variable and unpredictable. Load testing is a huge topic by itself but in brief here a few things that load testing can do for you:</p>
<ul>
<li> Benchmark the performance of the application under load so that it can be compared to previous or future releases.</li>
<li> Identify trouble spots in the application that break only when under high load.</li>
<li> Help managers more accurately anticipate and plan for resources such as hardware, employees, bandwidth etc.</li>
<li> Test whether the application is going to perform within its specifications under projected usage.</li>
</ul>
<p>Tsung is a distributed, multi protocol load testing framework that can be used to stress web and database servers. Tsung has many things in common with other load testing suites but what distinguishes it from the others is its ability to produce very high load with limited resources. Tsung is written in Erlang, (a language built for high concurrency and fault tolerance) but uses simple XML files for its configuration of load testing scenarios.</p>
<p><strong>Let&#8217;s Try it Out</strong></p>
<p>The best way to get familiar with Tsung is just to try it out. It doesn&#8217;t take long at all to get a working Tsung load test up and running.</p>
<p><strong>Installation</strong></p>
<p>My examples were all run on Kubuntu 10.10 so you may have to adapt tweak these steps a little for other distributions.</p>
<p>The first thing you&#8217;ll need to do is install erlang-nox on all of the servers that will be tested or used as clients.</p>
<pre> <span style="color: #0000ff;"><span style="color: #333333;">$</span>sudo apt-get install erlang-nox</span>
</pre>
<p>You should now be able to run</p>
<pre> $<span style="color: #0000ff;">erl</span></pre>
<p>from your terminal and be in an Erlang shell. Type &#8216;Control-C&#8217; and then &#8216;a&#8217; to get out of the Erlang shell.</p>
<p>Next you&#8217;ll need need to install Tsung on your client machine. Tsung has not yet been packaged for Ubuntu but a Debian package is available.</p>
<pre> $<span style="color: #0000ff;">wget http://tsung.erlang-projects.org/dist/debian/tsung_1.3.3-1_all.deb</span></pre>
<pre> $<span style="color: #0000ff;">sudo dpkg -i tsung_1.3.3-1_all.deb</span></pre>
<p>The following are needed for generating reports from the collected data:</p>
<pre> $<span style="color: #0000ff;">sudo apt-get install gnuplot perl libtemplate-perl python-matplotlib</span></pre>
<p>You should now be able to run</p>
<p>$<span style="color: #0000ff;">tsung -v</span></p>
<p>You should then see &#8216;Tsung version 1.3.3&#8217; displayed.</p>
<p>In order for Tsung to communicate with the other servers in the cluster and obtain statistics from them, password-less ssh must be enabled between the client machine(s) and the server machine(s).</p>
<pre> $<span style="color: #0000ff;">ssh-keygen</span></pre>
<pre> $<span style="color: #0000ff;">ssh-copy-id -i ~/.ssh/id_rsa.pub username@host</span></pre>
<p>If for the purposes of experimenting you&#8217;re just running both client and server on one machine the above is not necessary.</p>
<p><strong>Running:</strong></p>
<p>You will recall that Tsung uses XML files for its configuration. Here is an example of pretty much the smallest Tsung configuration file:</p>
<p><span style="color: #333333;">&lt;?xml version=&#8221;1.0&#8243;?&gt;<br />
&lt;!DOCTYPE tsung SYSTEM &#8220;/usr/share/tsung/tsung-1.0.dtd&#8221;[]&gt;<br />
&lt;tsung loglevel=&#8221;info&#8221;&gt;<br />
&lt;!&#8211; Minimal tsung configuration file &#8211;&gt;<br />
&lt;clients&gt;<br />
&lt;client host=&#8221;localhost&#8221;/&gt;<br />
&lt;/clients&gt;<br />
&lt;servers&gt;<br />
&lt;server host=&#8221;localhost&#8221; port=&#8221;8000&#8243; type=&#8221;tcp&#8221;/&gt;<br />
&lt;/servers&gt;<br />
&lt;load&gt;<br />
&lt;arrivalphase phase=&#8221;1&#8243; duration=&#8221;1&#8243; unit=&#8221;minute&#8221;&gt;<br />
&lt;users interarrival=&#8221;1&#8243; unit=&#8221;second&#8221;/&gt;<br />
&lt;/arrivalphase&gt;<br />
&lt;/load&gt;<br />
&lt;sessions&gt;<br />
&lt;session name=&#8221;foo&#8221; probability=&#8221;100&#8243; type=&#8221;ts_http&#8221;&gt;<br />
&lt;request&gt;<br />
&lt;http url=&#8217;/&#8217; version=&#8217;1.1&#8242; method=&#8217;GET&#8217;/&gt;<br />
&lt;/request&gt;<br />
&lt;/session&gt;<br />
&lt;/sessions&gt;<br />
&lt;/tsung&gt;</span></p>
<p>Save the above file and give it a name, for example minimal.XML.</p>
<p>Basically before it can run Tsung has to know 4 things: the IP address of the machine on which the client is running, the IP address of the machine on which the server is running, how many users to create, and what each user is to do. In the above example both the client and the server are running on localhost, a new user will created each second for one minute, and each user will run one get request of the home page of the tested site.</p>
<p>Before we can run our simple load test we&#8217;ll need to run our server. I am running a Django application that&#8217;s listening on port 8000 so you&#8217;ll need to change your server line to match your environment. Once your server is running you&#8217;re ready to start.</p>
<pre> $<span style="color: #0000ff;">sudo tsung -r erl -f minimal.xml start</span></pre>
<p>Note: Normally you do not need to run Tsung via sudo, but the very first time that it is run it needs to create files that require root access.</p>
<p>If Tsung ran without any problems you should have seen displayed the path to the directory where the log files for this load test can be found. CD to that directory and generate your report like this:</p>
<pre> $<span style="color: #0000ff;">/usr/lib/tsung/bin/tsung_stats.pl</span></pre>
<p>You can now view your report from a browser:</p>
<pre> $<span style="color: #0000ff;">firefox report.html &amp;</span></pre>
<p>Congratulations! You&#8217;ve just setup and run your first Tsung load test. Next time we&#8217;ll look at more complex scenarios and delve deeper into Tsung&#8217;s capabilities.</p>