Monday, November 2, 2009

Tsung - Load Testing Tool in Erlang

You've must have heard of Tsung - a load testing tool. Well i've got the opportunity to learn and use it in my current employment and its really versatile in conducting load tests for web-based applications. It has many features that i've come to appreciate after spending a couple of years with Mercury Interactive (its defunct now after being bought over by HP in 2006); admittedly Mercury's solutions were more versatile than what Tsung can offer but considering that web is the common platform where many applications are being hosted on; i think its a safe bet :)

So, IMO i think
  1. Tsung is good for conducting load testing scenarios and executing them in a local / distributed manner
  2. Load Testing can be relatively light weight (Erlang processes are hitting the target app) and hence the cost of using relatively heavy weight machines is likely to reduce since there is lesser need to use those machines since more concurrent users can be simulated on 1 machine in Tsung. (I'll see whether this assumption is correct)
Good starting points (URLs of interest):
  1. http://tsung.erlang-projects.org/
  2. http://www.erlang.org
A load testing tool would be useless if it didn't know how to capture server response data (e.g. checking whether an expected string is returned) and reuse it (e.g. session ids returned from servers which you can use subsequently in a HTTP POST/GET in an URL-rewrite type of string) subsequently, generate dynamic data, read data from an external file (commonly used in storing <username, password> pairs)
So my example would illustrate the logging in to a website (e.g. http://projecteuler.net) and logging out from it - simple enough to illustrate my point.
Next what i did was to record the series of events that mimick my user logging and logging out of the website and this is captured in the tsung_recorder_timestamp.xml
and i used that XML file and included some other stuff so that it looks like what i have for you below (this is a basic load test scenario)
<?xml version="1.0"?>
<!DOCTYPE tsung SYSTEM "/usr/local/share/tsung/tsung-1.0.dtd">

<tsung loglevel="info" dumptraffic="false" version="1.0">

<clients>
<client host="localhost" use_controller_vm="true"/>
</clients>
<servers>
<server host="78.110.165.8" port="80" type="tcp"></server>
</servers>

<load>
<arrivalphase phase="1" duration="2" unit="minute">
<users interarrival="1" unit="minute"></users>
</arrivalphase>
<user session="rec20091102-01:30" start_time="0" unit="second"></user>
</load>

<options>
<option name="file_server" value="/tmp/userlist.csv"></option>
</options>

<sessions>
<session name='rec20091102-01:30' probability='100'  type='ts_http'>
<request><http url='http://projecteuler.net/' version='1.1' method='GET'></http></request>
<request><http url='/style_main.css' version='1.1' if_modified_since='Sat, 29 Nov 2008 20:04:00 GMT' method='GET'></http></request>
<request><http url='/images/logo.jpg' version='1.1' if_modified_since='Thu, 28 Dec 2006 14:12:43 GMT' method='GET'></http></request>
<request><http url='/images/icon_register.png' version='1.1' if_modified_since='Fri, 31 Dec 2004 12:48:16 GMT' method='GET'></http></request>
<request><http url='/images/icon_about.png' version='1.1' if_modified_since='Fri, 31 Dec 2004 12:48:28 GMT' method='GET'></http></request>
<request><http url='/images/icon_problems.png' version='1.1' if_modified_since='Fri, 31 Dec 2004 12:48:26 GMT' method='GET'></http></request>
<request><http url='/images/icon_login.png' version='1.1' if_modified_since='Fri, 31 Dec 2004 12:48:32 GMT' method='GET'></http></request>
<request><http url='http://projecteuler.net/images/corner_tr.gif' version='1.1' if_modified_since='Thu, 10 Apr 2008 19:35:02 GMT' method='GET'></http></request>
<request><http url='/images/corner_tl.gif' version='1.1' if_modified_since='Thu, 10 Apr 2008 19:34:41 GMT' method='GET'></http></request>
<request><http url='/images/corner_br.gif' version='1.1' if_modified_since='Thu, 10 Apr 2008 19:35:10 GMT' method='GET'></http></request>
<request><http url='/images/corner_bl.gif' version='1.1' if_modified_since='Thu, 10 Apr 2008 19:34:55 GMT' method='GET'></http></request>
<request><http url='/images/euler_main.jpg' version='1.1' if_modified_since='Mon, 21 Jan 2002 19:18:20 GMT' method='GET'></http></request>

<thinktime random='true' value='2'/>

<request><http url='http://projecteuler.net/index.php?section=login' version='1.1' method='GET'></http></request>

<thinktime random='true' value='6'/>

<request subst="true">
<match do="continue" when="match">Logged in as %%readcsv:getUsername%%</match>
<http url='/index.php' version='1.1'  contents='%%readcsv:getUserString%%' content_type='application/x-www-form-urlencoded' method='POST'></http>
</request>

<thinktime random='true' value='4'/>

<request><http url='/images/icon_tick.png' version='1.1' method='GET'></http></request>

<thinktime random='true' value='4'/>

<thinktime random='true' value='3'/>

<request><http url='http://projecteuler.net/index.php?section=logout' version='1.1' method='GET'></http></request>
</session>
</sessions>

</tsung>


Hence, the main thing you should note is the use of dynamic substitution (e.g. %%readcsv:getUsername%%) where i wrote a simple erlang program to read my username and password from a file (see the XML tag option above) and replacing each simulated user with a valid user id and password.
Next, i checked that the server response contains a string Logged in as XXX where XXX would be dynamically generated by the function (Check out the erlang code for the function, simple stuff).
The erlang program is shown below.
1 -module(readcsv).
2 -export([getUserString/1, getUsername/1]).
3
4 getUserString({Pid, DynVar}) ->
5  {ok, Line} = ts_file_server:get_next_line(),
6  [Uid,Pwd] = string:tokens(Line, ","),
7  "username=" ++ Uid ++ "&password=" ++ Pwd ++ "&login=Login".
8
9 getUsername({Pid, DynVar}) ->
10  {ok, Line} = ts_file_server:get_next_line(),
11  [Uid,_] = string:tokens(Line, ","),
12  Uid.
The above erlang code must be compiled via erlc and you place the .beam file into the directory via a command like this
sudo mv readcsv.beam /usr/local/lib/erlang/lib/tsung-1.3.1/ebin/

Now, running the load test should be alright.
Note: In this load test, i defined a duration of 2 minutes with 2 users since load testing using 800 gazillion users is considered a chargeable offense so DON'T DO IT.


Have fun!

Tuesday, August 11, 2009

wxErlang

There's another graphics library other than "gs" aka Graphical System which is an adaptation of wxWidgets otherwise known as wxErlang. If you have never tried using wxErlang to run your stuff then you might run into some problems.

Sadly, the documentation on this is pitifully little

For me, i got this error when i attempt to run the default wx libraries shipped with Erlang.

=ERROR REPORT==== 7-Jun-2009::12:17:09 ===
WX Failed loading "wxe_driver"@"/usr/local/lib/erlang/lib/wx-0.98.1/
priv/i386-apple-darwin9.6.0"
** exception error: {load_driver,"dlopen(/usr/local/lib/erlang/lib/
wx-0.98.1/priv/i386-apple-darwin9.6.0/wxe_driver.so, 2): Symbol not
found: __ZN10wxGLCanvas20MacVisibilityChangedEv\n Referenced from: /
usr/local/lib/erlang/lib/wx-0.98.1/priv/i386-apple-darwin9.6.0/
wxe_driver.so\n Expected in: flat namespace\n"}
in function wxe_server:start/0
in call from wx:new/1

If this is familiar to you, then i suggest you do the following
1. Read all of this
or if you are like me building wxErlang & BEAM emulator 5.7 from source
1. Download the wxErlang libraries from wxErlang website and unzip to dir wxMac-2.8.10
2. Change to that directory
3. Execute the following command:
./configure --with-opengl --enable-unicode --disable-shared --enable-graphics_ctx

4. Do the following in sequence:
make && make install
cd contrib/src/stc/
make && make install
Note that you might need to do a "sudo make install" if you use the default installation prefix which is /usr/local/{bin,lib, etc}
5. Assumed that you've download Erlang-OTP 13B source code and unzipped to a directory
6. Export the PATH variable such that the build directory to your wxErlang is right at the front of the PATH. e.g. export PATH=<wxErlang dir>:<rest of your path>
7. Build your erlang source code as per your preferences/needs as defined in the README

After all that is over, you should be able to launch your wxErlang programs.


Thursday, August 6, 2009

An example of lazy evaluation for generating lists

One thing i've learnt from Erlang is only that its very expressive despite its annoyances but a good outcome of this expressiveness is possibly the availability of lazy(delayed) evaluation. In the example of passing lists to functions, the lists are evaluated fully before being passed to functions.

The big deal here is that you could potentially craft a function that's computationally expensive but the user of this function might not need all the results all at once. Example would be using these sorts of functions to obtain database records and to display them on a webpage but every good UI designer knows that its ineffective to display all 15,000 records at 1 time and you could design a web page that displays 10 - 20 records at a time and have "next" buttons/links to display subsequent pages.

So, naturally we can craft functions such that we can have lazy evaluation. A simple example is as follows where the function next/1 behaves like a generator function but only delayed; another way to think of this concept is possibly python generators. In my example below, its not hard to imagine that next/1 could be your computationally expensive operation.

1 -module(lazy_eval).
2 -export([next/1, test/2]).
3
4 next(Num) ->
5 fun() -> [Num|next(Num+1)] end.
6
7 test(Seq, Fun) when Seq =:= 100 -> [Seq];
8 test(Seq, Fun) when Seq < 100 ->
9 [Val|Func] = Fun(),
10 % io:format("~p ~p~n", [Val, Func()]),
11 [Val | test(Seq + 1, Func) ].

A sample output (with debugging statements):

6> lazy_eval:test(1, lazy_eval:next(1)).
1 [2|#Fun<lazy_eval.0.5801025>]
2 [3|#Fun<lazy_eval.0.5801025>]
3 [4|#Fun<lazy_eval.0.5801025>]
4 [5|#Fun<lazy_eval.0.5801025>]
...
99 [100|#Fun<lazy_eval.0.5801025>]
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,
23,24,25,26,27,28,29|...]
Have fun!

Monday, August 3, 2009

Why Erlang Sucks - by Damien Katz

Found another interesting read why Erlang sucks. This post is not to create some sort of flame war and the end result is crucifying Damien but rather his post is a rather insightful look into the annoying part of Erlang that most Erlang developers must have seen (me including) and felt.

Examples of what i thought was good was his
* comparison of "if" and "case" in Erlang
* Erlang's concept of strings (Which is a real irritance personally to me)
* Erlang's concept of records which is super similar to C's struct (Another irritance but you can get the hang of it...after a while)
* Absolutely love Damien's take of exit(...) and his comments on hara-kiri (Really hilarious)
* I share Damien's criticisms on Erlang's documentation of the libraries

Overall, its a very honest look into the weaknesses of Erlang as a whole and there's more though...you can form your own opinions about the language though.


Job openings at Linden Lab

Linden Lab is hiring lots of talented people out there for software development and QA (that's where i'm in). Visit http://lindenlab.com/employment and if you do find something you like, feel free to drop me a email to tay_boon_leong@yahoo.com.sg or ray@lindenlab.com and yes we do stuff in Erlang, Python, Perl, Django blah blah blah


Sunday, August 2, 2009

Parallel QuickSort

Found an interesting blog by a Erlang enthusiast where he implemented a parallel version of the popular QuickSort algorithm. Read about his entry at http://bicosyes.com/paralelizando-quicksort-en-erlang/ where he used the list-comprehension version of QuickSort to implement parallel computation. Its very helpful to see how he made use of erlang:make_ref() to help in the algorithm. Good read.

Tuesday, April 21, 2009

Notice

Hi all, its been a while since i last blogged on Erlang ... infact its almost a year. Its not that i have given up on erlang but rather things have been moving in my career and life in general which left me little time to blog about this but i'll be picking this up again and hopefully u'll like it